summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/CMakeLists.txt337
-rw-r--r--src/gui/accessible/linux/atspiadaptor.cpp758
-rw-r--r--src/gui/accessible/linux/atspiadaptor_p.h16
-rw-r--r--src/gui/accessible/linux/dbusconnection.cpp12
-rw-r--r--src/gui/accessible/linux/dbusxml/Socket.xml2
-rw-r--r--src/gui/accessible/linux/qspi_constant_mappings.cpp14
-rw-r--r--src/gui/accessible/linux/qspiaccessiblebridge.cpp12
-rw-r--r--src/gui/accessible/linux/qspiapplicationadaptor.cpp18
-rw-r--r--src/gui/accessible/qaccessible.cpp296
-rw-r--r--src/gui/accessible/qaccessible.h45
-rw-r--r--src/gui/accessible/qaccessible_base.h13
-rw-r--r--src/gui/accessible/qaccessiblecache.cpp14
-rw-r--r--src/gui/accessible/qaccessibleobject.cpp6
-rw-r--r--src/gui/accessible/qplatformaccessibility.cpp4
-rw-r--r--src/gui/accessible/windows/apisupport/qwindowsuiawrapper.cpp89
-rw-r--r--src/gui/accessible/windows/apisupport/qwindowsuiawrapper_p.h67
-rw-r--r--src/gui/accessible/windows/apisupport/uiaattributeids_p.h63
-rw-r--r--src/gui/accessible/windows/apisupport/uiaclientinterfaces_p.h230
-rw-r--r--src/gui/accessible/windows/apisupport/uiacontroltypeids_p.h60
-rw-r--r--src/gui/accessible/windows/apisupport/uiaerrorids_p.h26
-rw-r--r--src/gui/accessible/windows/apisupport/uiaeventids_p.h54
-rw-r--r--src/gui/accessible/windows/apisupport/uiageneralids_p.h21
-rw-r--r--src/gui/accessible/windows/apisupport/uiapatternids_p.h53
-rw-r--r--src/gui/accessible/windows/apisupport/uiapropertyids_p.h188
-rw-r--r--src/gui/accessible/windows/apisupport/uiaserverinterfaces_p.h367
-rw-r--r--src/gui/accessible/windows/apisupport/uiatypes_p.h157
-rw-r--r--src/gui/compat/removed_api.cpp59
-rw-r--r--src/gui/configure.cmake92
-rw-r--r--src/gui/doc/images/qpainter-concentriccircles.pngbin31294 -> 95529 bytes
-rw-r--r--src/gui/doc/includes/QtGuiDoc6
-rw-r--r--src/gui/doc/includes/windows.h2
-rw-r--r--src/gui/doc/qtgui.qdocconf6
-rw-r--r--src/gui/doc/snippets/code/doc_src_richtext.qdoc2
-rw-r--r--src/gui/doc/snippets/code/src_gui_image_qicon.cpp28
-rw-r--r--src/gui/doc/snippets/code/src_gui_kernel_qapplication.cpp83
-rw-r--r--src/gui/doc/snippets/code/src_gui_kernel_qguiapplication.cpp22
-rw-r--r--src/gui/doc/snippets/code/src_gui_kernel_qkeysequence.cpp2
-rw-r--r--src/gui/doc/snippets/code/src_gui_painting_qpainter.cpp33
-rw-r--r--src/gui/doc/snippets/code/src_gui_text_qfontmetrics.cpp4
-rw-r--r--src/gui/doc/snippets/code/src_gui_text_qtextdocument.cpp12
-rw-r--r--src/gui/doc/snippets/code/src_gui_text_qtextlayout.cpp40
-rw-r--r--src/gui/doc/snippets/code/src_gui_util_qdesktopservices.cpp9
-rw-r--r--src/gui/doc/snippets/code/src_gui_vulkan_qvulkanfunctions.cpp10
-rw-r--r--src/gui/doc/snippets/image/image.cpp7
-rw-r--r--src/gui/doc/snippets/qfileopenevent/main.cpp12
-rw-r--r--src/gui/doc/snippets/rhioffscreen/color.frag16
-rw-r--r--src/gui/doc/snippets/rhioffscreen/color.vert18
-rw-r--r--src/gui/doc/snippets/rhioffscreen/main.cpp151
-rw-r--r--src/gui/doc/snippets/separations/finalwidget.cpp3
-rw-r--r--src/gui/doc/snippets/separations/screenwidget.cpp3
-rw-r--r--src/gui/doc/snippets/separations/separations.qdoc2
-rw-r--r--src/gui/doc/snippets/textdocument-listitems/mainwindow.cpp8
-rw-r--r--src/gui/doc/snippets/textdocument-selections/mainwindow.cpp18
-rw-r--r--src/gui/doc/snippets/transform/main.cpp3
-rw-r--r--src/gui/doc/src/coordsys.qdoc35
-rw-r--r--src/gui/doc/src/dnd.qdoc14
-rw-r--r--src/gui/doc/src/external-resources.qdoc23
-rw-r--r--src/gui/doc/src/includes/qt-colors.qdocinc21
-rw-r--r--src/gui/doc/src/includes/svg-colors.qdocinc148
-rw-r--r--src/gui/doc/src/qt6-changes.qdoc36
-rw-r--r--src/gui/doc/src/qtgui-overview.qdoc85
-rw-r--r--src/gui/doc/src/qtgui.qdoc19
-rw-r--r--src/gui/doc/src/richtext.qdoc9
-rw-r--r--src/gui/image/qabstractfileiconengine.cpp2
-rw-r--r--src/gui/image/qabstractfileiconengine_p.h1
-rw-r--r--src/gui/image/qabstractfileiconprovider.cpp18
-rw-r--r--src/gui/image/qabstractfileiconprovider_p.h1
-rw-r--r--src/gui/image/qbitmap.cpp2
-rw-r--r--src/gui/image/qbmphandler.cpp78
-rw-r--r--src/gui/image/qicon.cpp696
-rw-r--r--src/gui/image/qicon.h165
-rw-r--r--src/gui/image/qicon_p.h4
-rw-r--r--src/gui/image/qiconengine.cpp75
-rw-r--r--src/gui/image/qiconengine_p.h59
-rw-r--r--src/gui/image/qiconloader.cpp328
-rw-r--r--src/gui/image/qiconloader_p.h49
-rw-r--r--src/gui/image/qimage.cpp873
-rw-r--r--src/gui/image/qimage.h14
-rw-r--r--src/gui/image/qimage_conversions.cpp76
-rw-r--r--src/gui/image/qimage_neon.cpp2
-rw-r--r--src/gui/image/qimage_p.h180
-rw-r--r--src/gui/image/qimageiohandler.cpp11
-rw-r--r--src/gui/image/qimagereader.cpp118
-rw-r--r--src/gui/image/qimagereaderwriterhelpers.cpp26
-rw-r--r--src/gui/image/qimagereaderwriterhelpers_p.h2
-rw-r--r--src/gui/image/qimagewriter.cpp7
-rw-r--r--src/gui/image/qmovie.cpp41
-rw-r--r--src/gui/image/qpicture.h2
-rw-r--r--src/gui/image/qpixmap.cpp20
-rw-r--r--src/gui/image/qpixmap.h5
-rw-r--r--src/gui/image/qpixmap_win.cpp22
-rw-r--r--src/gui/image/qpixmap_win_p.h38
-rw-r--r--src/gui/image/qpixmapcache.cpp139
-rw-r--r--src/gui/image/qpixmapcache.h26
-rw-r--r--src/gui/image/qpixmapcache_p.h3
-rw-r--r--src/gui/image/qplatformpixmap.cpp18
-rw-r--r--src/gui/image/qpnghandler.cpp24
-rw-r--r--src/gui/image/qppmhandler.cpp31
-rw-r--r--src/gui/image/qxbmhandler.cpp24
-rw-r--r--src/gui/image/qxpmhandler.cpp23
-rw-r--r--src/gui/itemmodels/qfileinfogatherer.cpp142
-rw-r--r--src/gui/itemmodels/qfileinfogatherer_p.h19
-rw-r--r--src/gui/itemmodels/qfilesystemmodel.cpp279
-rw-r--r--src/gui/itemmodels/qfilesystemmodel.h14
-rw-r--r--src/gui/itemmodels/qfilesystemmodel_p.h42
-rw-r--r--src/gui/itemmodels/qstandarditemmodel.cpp140
-rw-r--r--src/gui/itemmodels/qstandarditemmodel_p.h10
-rw-r--r--src/gui/kernel/qaction.cpp78
-rw-r--r--src/gui/kernel/qaction.h11
-rw-r--r--src/gui/kernel/qaction_p.h2
-rw-r--r--src/gui/kernel/qactiongroup.cpp4
-rw-r--r--src/gui/kernel/qactiongroup_p.h2
-rw-r--r--src/gui/kernel/qclipboard.cpp11
-rw-r--r--src/gui/kernel/qcursor.cpp32
-rw-r--r--src/gui/kernel/qcursor.h4
-rw-r--r--src/gui/kernel/qdnd_p.h2
-rw-r--r--src/gui/kernel/qdrag.cpp4
-rw-r--r--src/gui/kernel/qevent.cpp222
-rw-r--r--src/gui/kernel/qevent.h30
-rw-r--r--src/gui/kernel/qeventpoint.cpp6
-rw-r--r--src/gui/kernel/qeventpoint.h1
-rw-r--r--src/gui/kernel/qeventpoint_p.h2
-rw-r--r--src/gui/kernel/qgenericpluginfactory.cpp6
-rw-r--r--src/gui/kernel/qguiapplication.cpp412
-rw-r--r--src/gui/kernel/qguiapplication.h2
-rw-r--r--src/gui/kernel/qguiapplication_p.h50
-rw-r--r--src/gui/kernel/qguiapplication_platform.h26
-rw-r--r--src/gui/kernel/qguivariant.cpp4
-rw-r--r--src/gui/kernel/qhighdpiscaling.cpp56
-rw-r--r--src/gui/kernel/qhighdpiscaling_p.h7
-rw-r--r--src/gui/kernel/qinputdevice.cpp28
-rw-r--r--src/gui/kernel/qinputmethod.cpp6
-rw-r--r--src/gui/kernel/qinternalmimedata.cpp2
-rw-r--r--src/gui/kernel/qkeymapper.cpp74
-rw-r--r--src/gui/kernel/qkeymapper_p.h26
-rw-r--r--src/gui/kernel/qkeysequence.cpp220
-rw-r--r--src/gui/kernel/qkeysequence.h10
-rw-r--r--src/gui/kernel/qkeysequence_p.h8
-rw-r--r--src/gui/kernel/qoffscreensurface.h2
-rw-r--r--src/gui/kernel/qoffscreensurface_platform.h2
-rw-r--r--src/gui/kernel/qopenglcontext.cpp76
-rw-r--r--src/gui/kernel/qopenglcontext_p.h12
-rw-r--r--src/gui/kernel/qopenglcontext_platform.h12
-rw-r--r--src/gui/kernel/qpaintdevicewindow.cpp2
-rw-r--r--src/gui/kernel/qpaintdevicewindow_p.h4
-rw-r--r--src/gui/kernel/qpalette.cpp223
-rw-r--r--src/gui/kernel/qpalette.h7
-rw-r--r--src/gui/kernel/qpalette_p.h77
-rw-r--r--src/gui/kernel/qplatformdialoghelper.cpp93
-rw-r--r--src/gui/kernel/qplatformdialoghelper.h40
-rw-r--r--src/gui/kernel/qplatformgraphicsbufferhelper.cpp4
-rw-r--r--src/gui/kernel/qplatforminputcontext.cpp18
-rw-r--r--src/gui/kernel/qplatforminputcontext.h2
-rw-r--r--src/gui/kernel/qplatforminputcontextfactory.cpp35
-rw-r--r--src/gui/kernel/qplatforminputcontextfactory_p.h3
-rw-r--r--src/gui/kernel/qplatformintegration.cpp47
-rw-r--r--src/gui/kernel/qplatformintegration.h17
-rw-r--r--src/gui/kernel/qplatformintegrationfactory.cpp10
-rw-r--r--src/gui/kernel/qplatformkeymapper.cpp38
-rw-r--r--src/gui/kernel/qplatformkeymapper.h36
-rw-r--r--src/gui/kernel/qplatformmenu_p.h4
-rw-r--r--src/gui/kernel/qplatformscreen.cpp18
-rw-r--r--src/gui/kernel/qplatformscreen.h6
-rw-r--r--src/gui/kernel/qplatformscreen_p.h7
-rw-r--r--src/gui/kernel/qplatformservices.cpp26
-rw-r--r--src/gui/kernel/qplatformservices.h20
-rw-r--r--src/gui/kernel/qplatformsystemtrayicon.h3
-rw-r--r--src/gui/kernel/qplatformtheme.cpp152
-rw-r--r--src/gui/kernel/qplatformtheme.h38
-rw-r--r--src/gui/kernel/qplatformtheme_p.h2
-rw-r--r--src/gui/kernel/qplatformthemefactory.cpp15
-rw-r--r--src/gui/kernel/qplatformwindow.cpp42
-rw-r--r--src/gui/kernel/qplatformwindow_p.h52
-rw-r--r--src/gui/kernel/qpointingdevice.cpp55
-rw-r--r--src/gui/kernel/qpointingdevice.h8
-rw-r--r--src/gui/kernel/qpointingdevice_p.h4
-rw-r--r--src/gui/kernel/qrasterwindow.cpp16
-rw-r--r--src/gui/kernel/qrasterwindow.h1
-rw-r--r--src/gui/kernel/qscreen.cpp196
-rw-r--r--src/gui/kernel/qscreen.h2
-rw-r--r--src/gui/kernel/qscreen_p.h34
-rw-r--r--src/gui/kernel/qscreen_platform.h61
-rw-r--r--src/gui/kernel/qsessionmanager.cpp8
-rw-r--r--src/gui/kernel/qsessionmanager_p.h2
-rw-r--r--src/gui/kernel/qshapedpixmapdndwindow.cpp2
-rw-r--r--src/gui/kernel/qshortcut.cpp12
-rw-r--r--src/gui/kernel/qshortcut.h7
-rw-r--r--src/gui/kernel/qshortcut_p.h2
-rw-r--r--src/gui/kernel/qshortcutmap.cpp196
-rw-r--r--src/gui/kernel/qshortcutmap_p.h1
-rw-r--r--src/gui/kernel/qsimpledrag.cpp12
-rw-r--r--src/gui/kernel/qstylehints.cpp75
-rw-r--r--src/gui/kernel/qstylehints.h10
-rw-r--r--src/gui/kernel/qstylehints_p.h54
-rw-r--r--src/gui/kernel/qsurface.cpp4
-rw-r--r--src/gui/kernel/qsurfaceformat.cpp5
-rw-r--r--src/gui/kernel/qtestsupport_gui.cpp59
-rw-r--r--src/gui/kernel/qtestsupport_gui.h9
-rw-r--r--src/gui/kernel/qwindow.cpp227
-rw-r--r--src/gui/kernel/qwindow.h4
-rw-r--r--src/gui/kernel/qwindow_p.h19
-rw-r--r--src/gui/kernel/qwindowsysteminterface.cpp208
-rw-r--r--src/gui/kernel/qwindowsysteminterface.h39
-rw-r--r--src/gui/kernel/qwindowsysteminterface_p.h28
-rw-r--r--src/gui/math3d/qmatrix4x4.cpp41
-rw-r--r--src/gui/math3d/qmatrix4x4.h6
-rw-r--r--src/gui/math3d/qquaternion.cpp88
-rw-r--r--src/gui/math3d/qquaternion.h197
-rw-r--r--src/gui/math3d/qvectornd.h32
-rw-r--r--src/gui/opengl/platform/egl/qeglconvenience.cpp16
-rw-r--r--src/gui/opengl/platform/egl/qeglplatformcontext_p.h6
-rw-r--r--src/gui/opengl/platform/egl/qeglstreamconvenience_p.h4
-rw-r--r--src/gui/opengl/platform/unix/qglxconvenience.cpp2
-rw-r--r--src/gui/opengl/qopengl.cpp29
-rw-r--r--src/gui/opengl/qopengl.h41
-rw-r--r--src/gui/opengl/qopenglextensions_p.h15
-rw-r--r--src/gui/opengl/qopenglfunctions.cpp31
-rw-r--r--src/gui/opengl/qopenglfunctions.h2
-rw-r--r--src/gui/opengl/qopenglprogrambinarycache_p.h4
-rw-r--r--src/gui/opengl/qt_attribution.json4
-rw-r--r--src/gui/painting/qbackingstore.cpp10
-rw-r--r--src/gui/painting/qbackingstoredefaultcompositor.cpp260
-rw-r--r--src/gui/painting/qbackingstoredefaultcompositor_p.h44
-rw-r--r--src/gui/painting/qbackingstorerhisupport.cpp89
-rw-r--r--src/gui/painting/qbackingstorerhisupport_p.h3
-rw-r--r--src/gui/painting/qbezier_p.h1
-rw-r--r--src/gui/painting/qblendfunctions.cpp4
-rw-r--r--src/gui/painting/qblendfunctions_p.h4
-rw-r--r--src/gui/painting/qcmyk_p.h88
-rw-r--r--src/gui/painting/qcolor.cpp19
-rw-r--r--src/gui/painting/qcolor.h8
-rw-r--r--src/gui/painting/qcolorclut_p.h127
-rw-r--r--src/gui/painting/qcolormatrix_p.h250
-rw-r--r--src/gui/painting/qcolorspace.cpp517
-rw-r--r--src/gui/painting/qcolorspace.h19
-rw-r--r--src/gui/painting/qcolorspace_p.h30
-rw-r--r--src/gui/painting/qcolortransferfunction_p.h76
-rw-r--r--src/gui/painting/qcolortransfertable_p.h106
-rw-r--r--src/gui/painting/qcolortransform.cpp1164
-rw-r--r--src/gui/painting/qcolortransform_p.h27
-rw-r--r--src/gui/painting/qcolortrc_p.h19
-rw-r--r--src/gui/painting/qcolortrclut.cpp77
-rw-r--r--src/gui/painting/qcolortrclut_p.h86
-rw-r--r--src/gui/painting/qcompositionfunctions.cpp30
-rw-r--r--src/gui/painting/qcoregraphics.mm37
-rw-r--r--src/gui/painting/qcoregraphics_p.h19
-rw-r--r--src/gui/painting/qcosmeticstroker.cpp4
-rw-r--r--src/gui/painting/qcssutil.cpp21
-rw-r--r--src/gui/painting/qdatabuffer_p.h21
-rw-r--r--src/gui/painting/qdrawhelper.cpp273
-rw-r--r--src/gui/painting/qdrawhelper_avx2.cpp33
-rw-r--r--src/gui/painting/qdrawhelper_mips_dsp.cpp4
-rw-r--r--src/gui/painting/qdrawhelper_neon.cpp6
-rw-r--r--src/gui/painting/qdrawhelper_p.h30
-rw-r--r--src/gui/painting/qdrawhelper_sse2.cpp23
-rw-r--r--src/gui/painting/qdrawhelper_sse4.cpp8
-rw-r--r--src/gui/painting/qfixed_p.h89
-rw-r--r--src/gui/painting/qicc.cpp973
-rw-r--r--src/gui/painting/qimageeffects.cpp327
-rw-r--r--src/gui/painting/qimagescale.cpp7
-rw-r--r--src/gui/painting/qimagescale_neon.cpp7
-rw-r--r--src/gui/painting/qimagescale_sse4.cpp7
-rw-r--r--src/gui/painting/qoutlinemapper.cpp30
-rw-r--r--src/gui/painting/qoutlinemapper_p.h5
-rw-r--r--src/gui/painting/qpagelayout.cpp196
-rw-r--r--src/gui/painting/qpagelayout.h15
-rw-r--r--src/gui/painting/qpageranges.cpp8
-rw-r--r--src/gui/painting/qpagesize.cpp4
-rw-r--r--src/gui/painting/qpaintengine.cpp2
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp163
-rw-r--r--src/gui/painting/qpaintengine_raster_p.h16
-rw-r--r--src/gui/painting/qpaintengineex.cpp4
-rw-r--r--src/gui/painting/qpainter.cpp107
-rw-r--r--src/gui/painting/qpainterpath.cpp25
-rw-r--r--src/gui/painting/qpainterpath_p.h15
-rw-r--r--src/gui/painting/qpathclipper.cpp4
-rw-r--r--src/gui/painting/qpathclipper_p.h6
-rw-r--r--src/gui/painting/qpathsimplifier.cpp14
-rw-r--r--src/gui/painting/qpdf.cpp581
-rw-r--r--src/gui/painting/qpdf_p.h64
-rw-r--r--src/gui/painting/qpdfwriter.cpp47
-rw-r--r--src/gui/painting/qpdfwriter.h12
-rw-r--r--src/gui/painting/qpen.cpp132
-rw-r--r--src/gui/painting/qpen.h16
-rw-r--r--src/gui/painting/qpen_p.h5
-rw-r--r--src/gui/painting/qpixellayout.cpp136
-rw-r--r--src/gui/painting/qpixellayout_p.h8
-rw-r--r--src/gui/painting/qplatformbackingstore.cpp26
-rw-r--r--src/gui/painting/qplatformbackingstore.h29
-rw-r--r--src/gui/painting/qpolygon.cpp34
-rw-r--r--src/gui/painting/qrasterbackingstore.cpp2
-rw-r--r--src/gui/painting/qrasterizer.cpp19
-rw-r--r--src/gui/painting/qregion.cpp11
-rw-r--r--src/gui/painting/qregion.h2
-rw-r--r--src/gui/painting/qrgba64_p.h8
-rw-r--r--src/gui/painting/qrgbafloat.h88
-rw-r--r--src/gui/painting/qrgbafloat.qdoc14
-rw-r--r--src/gui/painting/qrhibackingstore.cpp30
-rw-r--r--src/gui/painting/qrhibackingstore_p.h1
-rw-r--r--src/gui/painting/qt_attribution.json16
-rw-r--r--src/gui/painting/qt_mips_asm_dsp_p.h2
-rw-r--r--src/gui/painting/qtextureglyphcache.cpp18
-rw-r--r--src/gui/painting/qtextureglyphcache_p.h5
-rw-r--r--src/gui/painting/qtransform.cpp281
-rw-r--r--src/gui/painting/qtransform.h9
-rw-r--r--src/gui/painting/qtriangulator.cpp2
-rw-r--r--src/gui/painting/shaders/backingstorecompose.frag8
-rw-r--r--src/gui/painting/shaders/backingstorecompose.frag.qsbbin1759 -> 1657 bytes
-rw-r--r--src/gui/painting/shaders/backingstorecompose.vert2
-rw-r--r--src/gui/painting/shaders/backingstorecompose.vert.qsbbin1627 -> 1408 bytes
-rw-r--r--src/gui/painting/webgradients.cpp3
-rw-r--r--src/gui/platform/android/qandroidnativeinterface.cpp18
-rw-r--r--src/gui/platform/darwin/qappleiconengine.mm464
-rw-r--r--src/gui/platform/darwin/qappleiconengine_p.h64
-rw-r--r--src/gui/platform/darwin/qapplekeymapper.mm165
-rw-r--r--src/gui/platform/darwin/qapplekeymapper_p.h14
-rw-r--r--src/gui/platform/darwin/qmacmime.mm1013
-rw-r--r--src/gui/platform/darwin/qmacmime_p.h63
-rw-r--r--src/gui/platform/darwin/qmacmimeregistry.mm118
-rw-r--r--src/gui/platform/darwin/qmacmimeregistry_p.h42
-rw-r--r--src/gui/platform/darwin/qutimimeconverter.h62
-rw-r--r--src/gui/platform/darwin/qutimimeconverter.mm823
-rw-r--r--src/gui/platform/ios/PrivacyInfo.xcprivacy23
-rw-r--r--src/gui/platform/macos/qcocoanativeinterface.mm6
-rw-r--r--src/gui/platform/unix/dbusmenu/qdbusmenuconnection.cpp10
-rw-r--r--src/gui/platform/unix/dbusmenu/qdbusmenuconnection_p.h4
-rw-r--r--src/gui/platform/unix/dbusmenu/qdbusmenuregistrarproxy_p.h2
-rw-r--r--src/gui/platform/unix/dbusmenu/qdbusmenutypes.cpp18
-rw-r--r--src/gui/platform/unix/dbustray/qdbustrayicon.cpp12
-rw-r--r--src/gui/platform/unix/dbustray/qdbustraytypes.cpp6
-rw-r--r--src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor.cpp6
-rw-r--r--src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor_p.h4
-rw-r--r--src/gui/platform/unix/dbustray/qxdgnotificationproxy_p.h2
-rw-r--r--src/gui/platform/unix/qgenericunixservices.cpp367
-rw-r--r--src/gui/platform/unix/qgenericunixservices_p.h10
-rw-r--r--src/gui/platform/unix/qgenericunixthemes.cpp516
-rw-r--r--src/gui/platform/unix/qgenericunixthemes_p.h2
-rw-r--r--src/gui/platform/unix/qunixnativeinterface.cpp97
-rw-r--r--src/gui/platform/unix/qxkbcommon.cpp57
-rw-r--r--src/gui/platform/unix/qxkbcommon_p.h57
-rw-r--r--src/gui/platform/wasm/qlocalfileapi.cpp211
-rw-r--r--src/gui/platform/wasm/qlocalfileapi_p.h94
-rw-r--r--src/gui/platform/wasm/qwasmlocalfileaccess.cpp265
-rw-r--r--src/gui/platform/wasm/qwasmlocalfileaccess_p.h7
-rw-r--r--src/gui/platform/wasm/qwasmnativeinterface.cpp17
-rw-r--r--src/gui/platform/windows/qwindowsguieventdispatcher.cpp2
-rw-r--r--src/gui/platform/windows/qwindowsmimeconverter.cpp170
-rw-r--r--src/gui/platform/windows/qwindowsmimeconverter.h (renamed from src/gui/platform/windows/qwindowsmime_p.h)43
-rw-r--r--src/gui/platform/windows/qwindowsnativeinterface.cpp35
-rw-r--r--src/gui/platform/windows/qwindowsthemecache.cpp79
-rw-r--r--src/gui/platform/windows/qwindowsthemecache_p.h35
-rw-r--r--src/gui/qt_cmdline.cmake3
-rw-r--r--src/gui/qtgui.tracepoints41
-rw-r--r--src/gui/rhi/MiniEngine_LICENSE.txt22
-rw-r--r--src/gui/rhi/cs_mipmap_p.h939
-rw-r--r--src/gui/rhi/cs_tdr_p.h192
-rw-r--r--src/gui/rhi/mipmap.hlsl117
-rw-r--r--src/gui/rhi/qrhi.cpp4784
-rw-r--r--src/gui/rhi/qrhi.h2026
-rw-r--r--src/gui/rhi/qrhi_p.h2315
-rw-r--r--src/gui/rhi/qrhi_p_p.h764
-rw-r--r--src/gui/rhi/qrhi_platform.h175
-rw-r--r--src/gui/rhi/qrhid3d11.cpp2002
-rw-r--r--src/gui/rhi/qrhid3d11_p.h855
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h798
-rw-r--r--src/gui/rhi/qrhid3d12.cpp6569
-rw-r--r--src/gui/rhi/qrhid3d12_p.h1248
-rw-r--r--src/gui/rhi/qrhid3dhelpers.cpp172
-rw-r--r--src/gui/rhi/qrhid3dhelpers_p.h53
-rw-r--r--src/gui/rhi/qrhigles2.cpp1496
-rw-r--r--src/gui/rhi/qrhigles2_p.h1125
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h1044
-rw-r--r--src/gui/rhi/qrhimetal.mm3436
-rw-r--r--src/gui/rhi/qrhimetal_p.h497
-rw-r--r--src/gui/rhi/qrhimetal_p_p.h464
-rw-r--r--src/gui/rhi/qrhinull.cpp86
-rw-r--r--src/gui/rhi/qrhinull_p.h275
-rw-r--r--src/gui/rhi/qrhinull_p_p.h295
-rw-r--r--src/gui/rhi/qrhivulkan.cpp2175
-rw-r--r--src/gui/rhi/qrhivulkan_p.h1031
-rw-r--r--src/gui/rhi/qrhivulkan_p_p.h995
-rw-r--r--src/gui/rhi/qrhivulkanext_p.h48
-rw-r--r--src/gui/rhi/qshader.cpp503
-rw-r--r--src/gui/rhi/qshader.h241
-rw-r--r--src/gui/rhi/qshader_p.h257
-rw-r--r--src/gui/rhi/qshader_p_p.h65
-rw-r--r--src/gui/rhi/qshaderdescription.cpp917
-rw-r--r--src/gui/rhi/qshaderdescription.h386
-rw-r--r--src/gui/rhi/qshaderdescription_p.h326
-rw-r--r--src/gui/rhi/qshaderdescription_p_p.h70
-rw-r--r--src/gui/rhi/qt_attribution.json16
-rw-r--r--src/gui/rhi/tdr.hlsl9
-rw-r--r--src/gui/rhi/vs_test_p.h2
-rw-r--r--src/gui/text/coretext/qcoretextfontdatabase.mm210
-rw-r--r--src/gui/text/coretext/qcoretextfontdatabase_p.h3
-rw-r--r--src/gui/text/coretext/qfontengine_coretext.mm79
-rw-r--r--src/gui/text/coretext/qfontengine_coretext_p.h5
-rw-r--r--src/gui/text/freetype/qfontengine_ft.cpp223
-rw-r--r--src/gui/text/freetype/qfontengine_ft_p.h9
-rw-r--r--src/gui/text/freetype/qfreetypefontdatabase.cpp165
-rw-r--r--src/gui/text/freetype/qfreetypefontdatabase_p.h14
-rw-r--r--src/gui/text/qabstracttextdocumentlayout.cpp4
-rw-r--r--src/gui/text/qabstracttextdocumentlayout.h2
-rw-r--r--src/gui/text/qabstracttextdocumentlayout_p.h13
-rw-r--r--src/gui/text/qcssparser.cpp212
-rw-r--r--src/gui/text/qcssparser_p.h14
-rw-r--r--src/gui/text/qcssscanner.cpp2
-rw-r--r--src/gui/text/qdistancefield.cpp8
-rw-r--r--src/gui/text/qfont.cpp594
-rw-r--r--src/gui/text/qfont.h78
-rw-r--r--src/gui/text/qfont_p.h30
-rw-r--r--src/gui/text/qfontdatabase.cpp208
-rw-r--r--src/gui/text/qfontdatabase.h5
-rw-r--r--src/gui/text/qfontdatabase_p.h8
-rw-r--r--src/gui/text/qfontengine.cpp204
-rw-r--r--src/gui/text/qfontengine_p.h48
-rw-r--r--src/gui/text/qfontengineglyphcache_p.h2
-rw-r--r--src/gui/text/qfontinfo.h3
-rw-r--r--src/gui/text/qfontmetrics.cpp94
-rw-r--r--src/gui/text/qfontmetrics.h3
-rw-r--r--src/gui/text/qfontsubset.cpp82
-rw-r--r--src/gui/text/qfontsubset_p.h5
-rw-r--r--src/gui/text/qglyphrun.cpp75
-rw-r--r--src/gui/text/qglyphrun.h8
-rw-r--r--src/gui/text/qglyphrun_p.h5
-rw-r--r--src/gui/text/qharfbuzzng.cpp8
-rw-r--r--src/gui/text/qinputcontrol.cpp2
-rw-r--r--src/gui/text/qplatformfontdatabase.cpp40
-rw-r--r--src/gui/text/qplatformfontdatabase.h12
-rw-r--r--src/gui/text/qrawfont.cpp34
-rw-r--r--src/gui/text/qrawfont.h3
-rw-r--r--src/gui/text/qsyntaxhighlighter.cpp20
-rw-r--r--src/gui/text/qt_attribution.json2
-rw-r--r--src/gui/text/qtextcursor.cpp12
-rw-r--r--src/gui/text/qtextdocument.cpp226
-rw-r--r--src/gui/text/qtextdocument.h8
-rw-r--r--src/gui/text/qtextdocument_p.cpp40
-rw-r--r--src/gui/text/qtextdocument_p.h11
-rw-r--r--src/gui/text/qtextdocumentfragment.cpp29
-rw-r--r--src/gui/text/qtextdocumentlayout.cpp77
-rw-r--r--src/gui/text/qtextdocumentwriter.cpp1
-rw-r--r--src/gui/text/qtextengine.cpp305
-rw-r--r--src/gui/text/qtextengine_p.h36
-rw-r--r--src/gui/text/qtextformat.cpp111
-rw-r--r--src/gui/text/qtextformat.h10
-rw-r--r--src/gui/text/qtextformat_p.h2
-rw-r--r--src/gui/text/qtexthtmlparser.cpp157
-rw-r--r--src/gui/text/qtexthtmlparser_p.h9
-rw-r--r--src/gui/text/qtextimagehandler.cpp152
-rw-r--r--src/gui/text/qtextlayout.cpp717
-rw-r--r--src/gui/text/qtextlayout.h28
-rw-r--r--src/gui/text/qtextlist.cpp43
-rw-r--r--src/gui/text/qtextmarkdownimporter.cpp169
-rw-r--r--src/gui/text/qtextmarkdownimporter_p.h10
-rw-r--r--src/gui/text/qtextmarkdownwriter.cpp328
-rw-r--r--src/gui/text/qtextmarkdownwriter_p.h3
-rw-r--r--src/gui/text/qtextobject.cpp10
-rw-r--r--src/gui/text/qtextodfwriter.cpp25
-rw-r--r--src/gui/text/qtextoption.cpp6
-rw-r--r--src/gui/text/qtexttable.cpp2
-rw-r--r--src/gui/text/qzip.cpp1352
-rw-r--r--src/gui/text/qzipreader_p.h91
-rw-r--r--src/gui/text/qzipwriter_p.h81
-rw-r--r--src/gui/text/unix/qfontconfigdatabase.cpp231
-rw-r--r--src/gui/text/unix/qfontconfigdatabase_p.h1
-rw-r--r--src/gui/text/unix/qfontenginemultifontconfig.cpp2
-rw-r--r--src/gui/text/unix/qgenericunixfontdatabase_p.h7
-rw-r--r--src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp701
-rw-r--r--src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h24
-rw-r--r--src/gui/text/windows/qwindowsfontdatabase.cpp205
-rw-r--r--src/gui/text/windows/qwindowsfontdatabase_ft.cpp19
-rw-r--r--src/gui/text/windows/qwindowsfontdatabase_ft_p.h3
-rw-r--r--src/gui/text/windows/qwindowsfontdatabase_p.h15
-rw-r--r--src/gui/text/windows/qwindowsfontdatabasebase.cpp240
-rw-r--r--src/gui/text/windows/qwindowsfontdatabasebase_p.h16
-rw-r--r--src/gui/text/windows/qwindowsfontengine.cpp34
-rw-r--r--src/gui/text/windows/qwindowsfontengine_p.h5
-rw-r--r--src/gui/text/windows/qwindowsfontenginedirectwrite.cpp201
-rw-r--r--src/gui/text/windows/qwindowsfontenginedirectwrite_p.h18
-rw-r--r--src/gui/util/qastchandler.cpp8
-rw-r--r--src/gui/util/qdesktopservices.cpp7
-rw-r--r--src/gui/util/qedidparser.cpp18
-rw-r--r--src/gui/util/qgraphicsframecapture.cpp123
-rw-r--r--src/gui/util/qgraphicsframecapture_p.h55
-rw-r--r--src/gui/util/qgraphicsframecapture_p_p.h67
-rw-r--r--src/gui/util/qgraphicsframecapturemetal.mm169
-rw-r--r--src/gui/util/qgraphicsframecapturemetal_p_p.h57
-rw-r--r--src/gui/util/qgraphicsframecapturerenderdoc.cpp310
-rw-r--r--src/gui/util/qgraphicsframecapturerenderdoc_p_p.h53
-rw-r--r--src/gui/util/qgridlayoutengine.cpp98
-rw-r--r--src/gui/util/qgridlayoutengine_p.h11
-rw-r--r--src/gui/util/qktxhandler.cpp231
-rw-r--r--src/gui/util/qktxhandler_p.h4
-rw-r--r--src/gui/util/qpkmhandler.cpp6
-rw-r--r--src/gui/util/qtexturefiledata.cpp4
-rw-r--r--src/gui/util/qundostack.cpp12
-rw-r--r--src/gui/util/qvalidator.cpp153
-rw-r--r--src/gui/vulkan/licenseheader.h.in (renamed from src/gui/vulkan/generated_header.txt)0
-rw-r--r--src/gui/vulkan/qbasicvulkanplatforminstance.cpp135
-rw-r--r--src/gui/vulkan/qbasicvulkanplatforminstance_p.h17
-rw-r--r--src/gui/vulkan/qplatformvulkaninstance.cpp16
-rw-r--r--src/gui/vulkan/qplatformvulkaninstance.h13
-rw-r--r--src/gui/vulkan/qt_attribution.json6
-rw-r--r--src/gui/vulkan/qvulkandefaultinstance.cpp26
-rw-r--r--src/gui/vulkan/qvulkanfunctions.cpp24
-rw-r--r--src/gui/vulkan/qvulkaninstance.cpp134
-rw-r--r--src/gui/vulkan/qvulkaninstance.h35
-rw-r--r--src/gui/vulkan/qvulkaninstance_p.h5
-rw-r--r--src/gui/vulkan/qvulkanwindow.cpp156
-rw-r--r--src/gui/vulkan/qvulkanwindow.h14
-rw-r--r--src/gui/vulkan/qvulkanwindow_p.h4
-rw-r--r--src/gui/vulkan/vk.xml9072
512 files changed, 59408 insertions, 23136 deletions
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 36dae764d7..cef71318d8 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -1,10 +1,6 @@
-# Generated from gui.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
-#####################################################################
-## Gui Module:
-#####################################################################
-
-# special case begin
qt_find_package(X11_XCB)
qt_find_package(WrapHarfbuzz PROVIDED_TARGETS WrapHarfbuzz::WrapHarfbuzz)
qt_find_package(WrapPNG PROVIDED_TARGETS WrapPNG::WrapPNG)
@@ -17,7 +13,7 @@ if (QT_FEATURE_gui)
set(_default_platform "android")
elseif(MACOS)
set(_default_platform "cocoa")
- elseif(TVOS OR IOS)
+ elseif(UIKIT)
set(_default_platform "ios")
elseif(WATCHOS)
set(_default_platform "minimal")
@@ -43,25 +39,30 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}
set_source_files_properties(../3rdparty/md4c/md4c.c PROPERTIES COMPILE_FLAGS "-Wno-unused-parameter -Wno-sign-compare -Wno-missing-field-initializers -Wno-missing-braces")
endif()
+# Silence AUTOMOC warning 'No relevant classes found. No output generated.'
+if (NOT UNIX)
+ set_source_files_properties(kernel/qplatformwindow_p.h
+ PROPERTIES SKIP_AUTOMOC TRUE)
+endif()
+
unset(qmake_module_config)
if(QT_FEATURE_opengl)
list(APPEND qmake_module_config opengl)
endif()
-# special case end
-
qt_internal_add_module(Gui
PLUGIN_TYPES accessiblebridge platforms platforms/darwin xcbglintegrations platformthemes platforminputcontexts generic iconengines imageformats egldeviceintegrations
- FEATURE_DEPENDENCIES # special case:
- Qt::Network # special case:
- QMAKE_MODULE_CONFIG "${qmake_module_config}" # special case
+ FEATURE_DEPENDENCIES
+ Qt::Network
+ QMAKE_MODULE_CONFIG "${qmake_module_config}"
SOURCES
+ compat/removed_api.cpp
image/qabstractfileiconengine.cpp image/qabstractfileiconengine_p.h
image/qabstractfileiconprovider.cpp image/qabstractfileiconprovider.h image/qabstractfileiconprovider_p.h
image/qbitmap.cpp image/qbitmap.h
image/qbmphandler.cpp image/qbmphandler_p.h
image/qicon.cpp image/qicon.h image/qicon_p.h
- image/qiconengine.cpp image/qiconengine.h
+ image/qiconengine.cpp image/qiconengine.h image/qiconengine_p.h
image/qiconengineplugin.cpp image/qiconengineplugin.h
image/qiconloader.cpp image/qiconloader_p.h
image/qimage.cpp image/qimage.h image/qimage_p.h
@@ -98,8 +99,9 @@ qt_internal_add_module(Gui
kernel/qkeymapper.cpp kernel/qkeymapper_p.h
kernel/qoffscreensurface.cpp kernel/qoffscreensurface.h kernel/qoffscreensurface_p.h
kernel/qoffscreensurface_platform.h
+ kernel/qopenglcontext.h
kernel/qpaintdevicewindow.cpp kernel/qpaintdevicewindow.h kernel/qpaintdevicewindow_p.h
- kernel/qpalette.cpp kernel/qpalette.h
+ kernel/qpalette.cpp kernel/qpalette.h kernel/qpalette_p.h
kernel/qpixelformat.cpp kernel/qpixelformat.h
kernel/qplatformclipboard.cpp kernel/qplatformclipboard.h
kernel/qplatformcursor.cpp kernel/qplatformcursor.h
@@ -112,9 +114,11 @@ qt_internal_add_module(Gui
kernel/qplatformintegration.cpp kernel/qplatformintegration.h
kernel/qplatformintegrationfactory.cpp kernel/qplatformintegrationfactory_p.h
kernel/qplatformintegrationplugin.cpp kernel/qplatformintegrationplugin.h
+ kernel/qplatformkeymapper.cpp kernel/qplatformkeymapper.h
kernel/qplatformmenu.cpp kernel/qplatformmenu.h kernel/qplatformmenu_p.h
kernel/qplatformnativeinterface.cpp kernel/qplatformnativeinterface.h
kernel/qplatformoffscreensurface.cpp kernel/qplatformoffscreensurface.h
+ kernel/qplatformopenglcontext.h
kernel/qplatformscreen.cpp kernel/qplatformscreen.h kernel/qplatformscreen_p.h
kernel/qplatformservices.cpp kernel/qplatformservices.h
kernel/qplatformsessionmanager.cpp kernel/qplatformsessionmanager.h
@@ -127,9 +131,9 @@ qt_internal_add_module(Gui
kernel/qplatformwindow.cpp kernel/qplatformwindow.h kernel/qplatformwindow_p.h
kernel/qpointingdevice.cpp kernel/qpointingdevice.h kernel/qpointingdevice_p.h
kernel/qrasterwindow.cpp kernel/qrasterwindow.h
- kernel/qscreen.cpp kernel/qscreen.h kernel/qscreen_p.h
+ kernel/qscreen.cpp kernel/qscreen.h kernel/qscreen_p.h kernel/qscreen_platform.h
kernel/qsessionmanager.cpp kernel/qsessionmanager.h kernel/qsessionmanager_p.h
- kernel/qstylehints.cpp kernel/qstylehints.h
+ kernel/qstylehints.cpp kernel/qstylehints.h kernel/qstylehints_p.h
kernel/qsurface.cpp kernel/qsurface.h
kernel/qsurfaceformat.cpp kernel/qsurfaceformat.h
kernel/qtestsupport_gui.cpp kernel/qtestsupport_gui.h
@@ -144,6 +148,10 @@ qt_internal_add_module(Gui
math3d/qvector3d.h
math3d/qvector4d.h
math3d/qvectornd.cpp math3d/qvectornd.h
+ opengl/qopengl.h
+ opengl/qopenglext.h
+ opengl/qopenglfunctions.h
+ opengl/qopenglextrafunctions.h
painting/qbackingstore.cpp painting/qbackingstore.h
painting/qbackingstoredefaultcompositor.cpp painting/qbackingstoredefaultcompositor_p.h
painting/qbackingstorerhisupport.cpp painting/qbackingstorerhisupport_p.h
@@ -152,6 +160,7 @@ qt_internal_add_module(Gui
painting/qblittable.cpp painting/qblittable_p.h
painting/qbrush.cpp painting/qbrush.h
painting/qcolor.cpp painting/qcolor.h painting/qcolor_p.h
+ painting/qcolorclut_p.h
painting/qcolormatrix_p.h
painting/qcolorspace.cpp painting/qcolorspace.h painting/qcolorspace_p.h
painting/qcolortransferfunction_p.h
@@ -161,6 +170,7 @@ qt_internal_add_module(Gui
painting/qcolortrclut.cpp painting/qcolortrclut_p.h
painting/qcompositionfunctions.cpp
painting/qcosmeticstroker.cpp painting/qcosmeticstroker_p.h
+ painting/qcmyk_p.h
painting/qdatabuffer_p.h
painting/qdrawhelper_p.h
painting/qdrawhelper_x86_p.h
@@ -169,6 +179,7 @@ qt_internal_add_module(Gui
painting/qfixed_p.h
painting/qgrayraster.c painting/qgrayraster_p.h
painting/qicc.cpp painting/qicc_p.h
+ painting/qimageeffects.cpp
painting/qimagescale.cpp painting/qimagescale_p.h
painting/qmath_p.h
painting/qmemrotate.cpp painting/qmemrotate_p.h
@@ -205,14 +216,10 @@ qt_internal_add_module(Gui
painting/qtriangulatingstroker.cpp painting/qtriangulatingstroker_p.h
painting/qtriangulator.cpp painting/qtriangulator_p.h
painting/qvectorpath_p.h
- rhi/qrhi.cpp rhi/qrhi_p.h
- rhi/qrhi_p_p.h
+ rhi/qrhi.cpp rhi/qrhi.h rhi/qrhi_platform.h rhi/qrhi_p.h
rhi/qrhinull.cpp rhi/qrhinull_p.h
- rhi/qrhinull_p_p.h
- rhi/qshader.cpp rhi/qshader_p.h
- rhi/qshader_p_p.h
- rhi/qshaderdescription.cpp rhi/qshaderdescription_p.h
- rhi/qshaderdescription_p_p.h
+ rhi/qshader.cpp rhi/qshader.h rhi/qshader_p.h
+ rhi/qshaderdescription.cpp rhi/qshaderdescription.h rhi/qshaderdescription_p.h
text/qabstracttextdocumentlayout.cpp text/qabstracttextdocumentlayout.h text/qabstracttextdocumentlayout_p.h
text/qdistancefield.cpp text/qdistancefield_p.h
text/qfont.cpp text/qfont.h text/qfont_p.h
@@ -256,41 +263,54 @@ qt_internal_add_module(Gui
util/qtexturefilereader.cpp util/qtexturefilereader_p.h
util/qvalidator.cpp util/qvalidator.h
DEFINES
+ QT_NO_CONTEXTLESS_CONNECT
QT_NO_FOREACH
QT_NO_USING_NAMESPACE
- QT_QPA_DEFAULT_PLATFORM_NAME="${QT_QPA_DEFAULT_PLATFORM}" # special case
+ QT_USE_NODISCARD_FILE_OPEN
+ QT_QPA_DEFAULT_PLATFORM_NAME="${QT_QPA_DEFAULT_PLATFORM}"
INCLUDE_DIRECTORIES
../3rdparty/VulkanMemoryAllocator
+ ../3rdparty/D3D12MemoryAllocator
LIBRARIES
Qt::CorePrivate
PUBLIC_LIBRARIES
Qt::Core
PRIVATE_MODULE_INTERFACE
Qt::CorePrivate
- NO_PCH_SOURCES # special case
- "painting/qdrawhelper.cpp" # special case
+ NO_PCH_SOURCES
+ compat/removed_api.cpp
+ painting/qdrawhelper.cpp
PRECOMPILED_HEADER
"kernel/qt_gui_pch.h"
GENERATE_CPP_EXPORTS
+ QPA_HEADER_FILTERS
+ "(^|/)qplatform.+\\.h$|(^|/)qwindowsystem.+\\.h$"
+ RHI_HEADER_FILTERS
+ "(^|/)qrhi\\.h$|(^|/)qrhi_platform\\.h$|(^|/)qshader\\.h$|(^|/)qshaderdescription\\.h$"
)
# Resources:
-set_source_files_properties("../3rdparty/icc/sRGB2014.icc"
- PROPERTIES QT_RESOURCE_ALIAS "sRGB2014.icc"
-)
-set(qpdf_resource_files
- "../3rdparty/icc/sRGB2014.icc"
- "painting/qpdfa_metadata.xml"
-)
+if(QT_FEATURE_pdf)
+ set_source_files_properties("../3rdparty/icc/sRGB2014.icc"
+ PROPERTIES QT_RESOURCE_ALIAS "sRGB2014.icc"
+ )
+ set(qpdf_resource_files
+ "../3rdparty/icc/sRGB2014.icc"
+ "painting/qpdfa_metadata.xml"
+ )
+ qt_internal_add_resource(Gui "qpdf"
+ PREFIX
+ "/qpdf/"
+ BASE
+ "painting"
+ FILES
+ ${qpdf_resource_files}
+ )
+endif()
-qt_internal_add_resource(Gui "qpdf"
- PREFIX
- "/qpdf/"
- BASE
- "painting"
- FILES
- ${qpdf_resource_files}
-)
+if(WIN32 OR (UNIX AND NOT APPLE))
+ set_target_properties(Gui PROPERTIES UNITY_BUILD OFF) # X11 define clashes/Windows oddities.
+endif()
qt_internal_add_resource(Gui "gui_shaders"
PREFIX
@@ -305,21 +325,11 @@ if(QT_FEATURE_reduce_relocations AND UNIX AND GCC)
"LINKER:--dynamic-list=${CMAKE_CURRENT_LIST_DIR}/QtGui.dynlist")
endif()
-# special case begin
qt_internal_extend_target(Gui CONDITION QT_FEATURE_standarditemmodel
SOURCES
itemmodels/qstandarditemmodel.cpp itemmodels/qstandarditemmodel.h itemmodels/qstandarditemmodel_p.h
)
-# special case end
-
-#### Keys ignored in scope 1:.:.:gui.pro:<TRUE>:
-# QMAKE_LIBS = "$$QMAKE_LIBS_GUI"
-
-## Scopes:
-#####################################################################
-
-# special case begin
# With qmake, gui's opengl.pri used CONFIG += opengl, where opengl.prf
# used direct public linkage against either libGLESv2 or libGL, depending
# on the opengl _feature_. This is done by hand now here (where the
@@ -333,35 +343,29 @@ if(QT_FEATURE_opengl)
target_link_libraries(Gui PUBLIC GLESv2::GLESv2)
if(INTEGRITY AND _qt_igy_gui_libs)
- find_package(IntegrityPlatformGraphics)
- target_link_libraries(Gui
- INTERFACE $<LINK_ONLY:IntegrityPlatformGraphics::IntegrityPlatformGraphics>)
+ qt_internal_extend_target(Gui
+ LIBRARIES
+ IntegrityPlatformGraphics::IntegrityPlatformGraphics
+ )
endif()
elseif(NOT QT_FEATURE_opengl_dynamic)
target_link_libraries(Gui PUBLIC WrapOpenGL::WrapOpenGL)
endif()
endif()
-# special case end
qt_internal_extend_target(Gui CONDITION QT_FEATURE_opengl
SOURCES
- kernel/qopenglcontext.cpp kernel/qopenglcontext.h kernel/qopenglcontext_p.h
+ kernel/qopenglcontext.cpp kernel/qopenglcontext_p.h
kernel/qopenglcontext_platform.h
- kernel/qplatformopenglcontext.cpp kernel/qplatformopenglcontext.h
- opengl/qopengl.cpp opengl/qopengl.h opengl/qopengl_p.h
- opengl/qopenglext.h
+ kernel/qplatformopenglcontext.cpp
+ opengl/qopengl.cpp opengl/qopengl_p.h
opengl/qopenglextensions_p.h
- opengl/qopenglextrafunctions.h
- opengl/qopenglfunctions.cpp opengl/qopenglfunctions.h
+ opengl/qopenglfunctions.cpp
opengl/qopenglprogrambinarycache.cpp opengl/qopenglprogrambinarycache_p.h
rhi/qrhigles2.cpp rhi/qrhigles2_p.h
- rhi/qrhigles2_p_p.h
)
-#### Keys ignored in scope 2:.:.:gui.pro:QT_FEATURE_opengl:
-# MODULE_CONFIG = "opengl"
-
qt_internal_extend_target(Gui CONDITION MACOS
SOURCES
platform/macos/qcocoanativeinterface.mm
@@ -372,14 +376,21 @@ qt_internal_extend_target(Gui CONDITION MACOS
${FWAppKit}
)
+qt_internal_extend_target(Gui CONDITION WASM
+ SOURCES
+ platform/wasm/qwasmnativeinterface.cpp
+)
+
qt_internal_extend_target(Gui CONDITION APPLE
SOURCES
image/qimage_darwin.mm
painting/qcoregraphics.mm painting/qcoregraphics_p.h
painting/qrasterbackingstore.cpp painting/qrasterbackingstore_p.h
painting/qrhibackingstore.cpp painting/qrhibackingstore_p.h
- platform/darwin/qmacmime.mm platform/darwin/qmacmime_p.h
+ platform/darwin/qmacmimeregistry.mm platform/darwin/qmacmimeregistry_p.h
+ platform/darwin/qutimimeconverter.mm platform/darwin/qutimimeconverter.h
platform/darwin/qapplekeymapper.mm platform/darwin/qapplekeymapper_p.h
+ platform/darwin/qappleiconengine.mm platform/darwin/qappleiconengine_p.h
text/coretext/qcoretextfontdatabase.mm text/coretext/qcoretextfontdatabase_p.h
text/coretext/qfontengine_coretext.mm text/coretext/qfontengine_coretext_p.h
LIBRARIES
@@ -398,15 +409,19 @@ qt_internal_extend_target(Gui CONDITION QT_FEATURE_animation
qt_internal_extend_target(Gui CONDITION WIN32
SOURCES
- image/qpixmap_win.cpp
+ image/qpixmap_win.cpp image/qpixmap_win_p.h
kernel/qwindowdefs_win.h
platform/windows/qwindowsguieventdispatcher.cpp platform/windows/qwindowsguieventdispatcher_p.h
- platform/windows/qwindowsmime_p.h
+ platform/windows/qwindowsmimeconverter.h platform/windows/qwindowsmimeconverter.cpp
platform/windows/qwindowsnativeinterface.cpp
- rhi/cs_tdr_p.h
+ platform/windows/qwindowsthemecache.cpp platform/windows/qwindowsthemecache_p.h
rhi/qrhid3d11.cpp rhi/qrhid3d11_p.h
- rhi/qrhid3d11_p_p.h
+ rhi/qrhid3dhelpers.cpp rhi/qrhid3dhelpers_p.h
rhi/vs_test_p.h
+ rhi/qrhid3d12.cpp rhi/qrhid3d12_p.h
+ rhi/cs_mipmap_p.h
+ ../3rdparty/D3D12MemoryAllocator/D3D12MemAlloc.h
+ ../3rdparty/D3D12MemoryAllocator/D3D12MemAlloc.cpp
text/windows/qwindowsfontdatabase.cpp text/windows/qwindowsfontdatabase_p.h
text/windows/qwindowsfontdatabasebase.cpp text/windows/qwindowsfontdatabasebase_p.h
text/windows/qwindowsfontengine.cpp text/windows/qwindowsfontengine_p.h
@@ -417,76 +432,66 @@ qt_internal_extend_target(Gui CONDITION WIN32
ole32
shell32
user32
+ uxtheme
PUBLIC_LIBRARIES
d3d11
dxgi
dxguid
+ d3d12
)
-#### Keys ignored in scope 7:.:.:gui.pro:WIN32:
-# CMAKE_WINDOWS_BUILD = "True"
+if(QT_FEATURE_graphicsframecapture)
+ qt_internal_extend_target(Gui
+ SOURCES
+ util/qgraphicsframecapture_p.h util/qgraphicsframecapture.cpp
+ util/qgraphicsframecapture_p_p.h
+ )
+
+ qt_internal_extend_target(Gui CONDITION (WIN32 OR (UNIX AND NOT APPLE)) AND QT_FEATURE_library
+ LIBRARIES
+ RenderDoc::RenderDoc
+ SOURCES
+ util/qgraphicsframecapturerenderdoc_p_p.h util/qgraphicsframecapturerenderdoc.cpp
+ )
+
+ qt_internal_extend_target(Gui CONDITION IOS OR MACOS
+ SOURCES
+ util/qgraphicsframecapturemetal_p_p.h util/qgraphicsframecapturemetal.mm
+ PUBLIC_LIBRARIES
+ ${FWMetal}
+ )
+endif()
-# special case begin
if(QT_FEATURE_egl)
qt_find_package(EGL)
endif()
-# special case end
qt_internal_extend_target(Gui CONDITION QT_FEATURE_egl
SOURCES
opengl/platform/egl/qeglconvenience.cpp opengl/platform/egl/qeglconvenience_p.h
opengl/platform/egl/qeglstreamconvenience.cpp opengl/platform/egl/qeglstreamconvenience_p.h
opengl/platform/egl/qt_egl_p.h
-# special case begin
LIBRARIES
- EGL::EGL # special case
-# special case end
+ EGL::EGL
)
-#### Keys ignored in scope 8:.:.:gui.pro:QT_FEATURE_egl:
-# CMAKE_EGL_LIBS = "$$cmakeProcessLibs($$QMAKE_LIBS_EGL)"
-
-#### Keys ignored in scope 9:.:.:gui.pro:NOT QMAKE_LIBDIR_EGL_ISEMPTY:
-# CMAKE_EGL_LIBDIR = "$$cmakeTargetPath($$QMAKE_LIBDIR_EGL)"
-
-#### Keys ignored in scope 10:.:.:gui.pro:QT_FEATURE_opengles2:
-# CMAKE_GL_HEADER_NAME = "GLES2/gl2.h"
-# CMAKE_OPENGL_INCDIRS = "$$cmakePortablePaths($$QMAKE_INCDIR_OPENGL_ES2)"
-# CMAKE_OPENGL_LIBS = "$$cmakeProcessLibs($$QMAKE_LIBS_OPENGL_ES2)"
-# CMAKE_QT_OPENGL_IMPLEMENTATION = "GLESv2"
-
-#### Keys ignored in scope 11:.:.:gui.pro:NOT QMAKE_INCDIR_OPENGL_ES2_ISEMPTY:
-# CMAKE_GL_INCDIRS = "$$cmakeTargetPaths($$QMAKE_INCDIR_OPENGL_ES2)"
-
-#### Keys ignored in scope 12:.:.:gui.pro:NOT QMAKE_LIBDIR_OPENGL_ES2_ISEMPTY:
-# CMAKE_OPENGL_LIBDIR = "$$cmakePortablePaths($$QMAKE_LIBDIR_OPENGL_ES2)"
-
-#### Keys ignored in scope 14:.:.:gui.pro:QT_FEATURE_opengl:
-# CMAKE_GL_HEADER_NAME = "GL/gl.h"
-# CMAKE_OPENGL_INCDIRS = "$$cmakePortablePaths($$QMAKE_INCDIR_OPENGL)"
-# CMAKE_QT_OPENGL_IMPLEMENTATION = "GL"
-
-#### Keys ignored in scope 15:.:.:gui.pro:NOT QMAKE_INCDIR_OPENGL_ISEMPTY:
-# CMAKE_GL_INCDIRS = "$$cmakeTargetPaths($$QMAKE_INCDIR_OPENGL)"
-
-#### Keys ignored in scope 16:.:.:gui.pro:NOT QT_FEATURE_dynamicgl:
-# CMAKE_OPENGL_LIBS = "$$cmakeProcessLibs($$QMAKE_LIBS_OPENGL)"
-
-#### Keys ignored in scope 17:.:.:gui.pro:NOT QMAKE_LIBDIR_OPENGL_ISEMPTY:
-# CMAKE_OPENGL_LIBDIR = "$$cmakePortablePaths($$QMAKE_LIBDIR_OPENGL)"
-
-#### Keys ignored in scope 18:.:.:gui.pro:APPLE:
-# CMAKE_GL_HEADER_NAME = "gl.h"
+# These two headers are always installed, their contents are guarded with
+# "#if QT_CONFIG(accessibility)", so if QT_FEATURE_accessibility is not
+# enabled, they are just duds.
+qt_internal_extend_target(Gui
+ SOURCES
+ accessible/qaccessible.h accessible/qplatformaccessibility.h
+)
qt_internal_extend_target(Gui CONDITION QT_FEATURE_accessibility
SOURCES
- accessible/qaccessible.cpp accessible/qaccessible.h accessible/qaccessible_base.h
+ accessible/qaccessible.cpp accessible/qaccessible_base.h
accessible/qaccessiblebridge.cpp accessible/qaccessiblebridge.h
accessible/qaccessiblebridgeutils.cpp accessible/qaccessiblebridgeutils_p.h
accessible/qaccessiblecache.cpp accessible/qaccessiblecache_p.h
accessible/qaccessibleobject.cpp accessible/qaccessibleobject.h
accessible/qaccessibleplugin.cpp accessible/qaccessibleplugin.h
- accessible/qplatformaccessibility.cpp accessible/qplatformaccessibility.h
+ accessible/qplatformaccessibility.cpp
)
qt_internal_extend_target(Gui CONDITION APPLE AND QT_FEATURE_accessibility
@@ -496,21 +501,6 @@ qt_internal_extend_target(Gui CONDITION APPLE AND QT_FEATURE_accessibility
${FWFoundation}
)
-qt_internal_extend_target(Gui CONDITION QT_FEATURE_accessibility AND WIN32
- SOURCES
- accessible/windows/apisupport/qwindowsuiawrapper.cpp accessible/windows/apisupport/qwindowsuiawrapper_p.h
- accessible/windows/apisupport/uiaattributeids_p.h
- accessible/windows/apisupport/uiaclientinterfaces_p.h
- accessible/windows/apisupport/uiacontroltypeids_p.h
- accessible/windows/apisupport/uiaerrorids_p.h
- accessible/windows/apisupport/uiaeventids_p.h
- accessible/windows/apisupport/uiageneralids_p.h
- accessible/windows/apisupport/uiapatternids_p.h
- accessible/windows/apisupport/uiapropertyids_p.h
- accessible/windows/apisupport/uiaserverinterfaces_p.h
- accessible/windows/apisupport/uiatypes_p.h
-)
-
if(QT_FEATURE_accessibility AND QT_FEATURE_accessibility_atspi_bridge)
set(atspi_accessibility ON)
else()
@@ -526,7 +516,6 @@ qt_internal_extend_target(Gui CONDITION atspi_accessibility
accessible/linux/qspiaccessiblebridge.cpp accessible/linux/qspiaccessiblebridge_p.h
accessible/linux/qspiapplicationadaptor.cpp accessible/linux/qspiapplicationadaptor_p.h
accessible/linux/qspidbuscache.cpp accessible/linux/qspidbuscache_p.h
-# special case begin
DBUS_ADAPTOR_SOURCES
accessible/linux/dbusxml/Cache.xml
accessible/linux/dbusxml/DeviceEventController.xml
@@ -537,7 +526,6 @@ qt_internal_extend_target(Gui CONDITION atspi_accessibility
accessible/linux/dbusxml/Socket.xml
DBUS_INTERFACE_FLAGS
"-i" "QtGui/private/qspi_struct_marshallers_p.h"
-# special case end
)
if(atspi_accessibility)
@@ -578,10 +566,10 @@ qt_internal_extend_target(Gui CONDITION QT_FEATURE_png
WrapPNG::WrapPNG
)
-#### Keys ignored in scope 40:.:image:image/image.pri:WIN32 AND MINGW:
-# GCC_VERSION = "$${QMAKE_GCC_MAJOR_VERSION}.$${QMAKE_GCC_MINOR_VERSION}.$${QMAKE_GCC_PATCH_VERSION}"
-
-qt_internal_extend_target(Gui CONDITION ((QT_FEATURE_png) AND (WIN32 AND MINGW)) AND (GCC_VERSION___equals___8.1.0)
+qt_internal_extend_target(Gui
+ CONDITION
+ QT_FEATURE_png AND WIN32 AND MINGW AND
+ (CMAKE_CXX_COMPILER_VESION VERSION_EQUAL "8.1.0")
COMPILE_OPTIONS
-fno-reorder-blocks-and-partition
)
@@ -643,9 +631,11 @@ endif()
qt_internal_extend_target(Gui CONDITION ANDROID
SOURCES
platform/android/qandroidnativeinterface.cpp
+ painting/qrasterbackingstore.cpp painting/qrasterbackingstore_p.h
+ painting/qrhibackingstore.cpp painting/qrhibackingstore_p.h
)
-qt_internal_extend_target(Gui CONDITION ANDROID AND (TEST_architecture_arch STREQUAL arm64 OR TEST_architecture_arch STREQUAL arm) # special case
+qt_internal_extend_target(Gui CONDITION ANDROID AND (TEST_architecture_arch STREQUAL arm64 OR TEST_architecture_arch STREQUAL arm)
SOURCES
image/qimage_neon.cpp
painting/qdrawhelper_neon.cpp painting/qdrawhelper_neon_p.h
@@ -663,14 +653,12 @@ qt_internal_extend_target(Gui CONDITION ANDROID AND (TEST_architecture_arch STRE
QT_COMPILER_SUPPORTS_SSSE3 QT_COMPILER_SUPPORTS_SSSE3
)
-# special case begin
if (MINGW AND CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 8.1.0)
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86048
set_source_files_properties(image/qpnghandler.cpp
PROPERTIES COMPILE_OPTIONS -fno-reorder-blocks-and-partition
)
endif()
-# special case end
qt_internal_extend_target(Gui CONDITION QT_FEATURE_harfbuzz
SOURCES
@@ -679,22 +667,14 @@ qt_internal_extend_target(Gui CONDITION QT_FEATURE_harfbuzz
WrapHarfbuzz::WrapHarfbuzz
)
-# special case begin
-# Replicate what src/3rdparty/harfbuzz-ng/harfbuzz-ng.pro does, which is link CoreText
-# when targeting uikit.
-
qt_internal_extend_target(Gui CONDITION QT_FEATURE_harfbuzz AND UIKIT
LIBRARIES
${FWCoreText}
)
-# special case end
qt_internal_extend_target(Gui CONDITION QT_FEATURE_textodfwriter
SOURCES
text/qtextodfwriter.cpp text/qtextodfwriter_p.h
- text/qzip.cpp
- text/qzipreader_p.h
- text/qzipwriter_p.h
)
qt_internal_extend_target(Gui CONDITION QT_FEATURE_textmarkdownreader
@@ -735,7 +715,7 @@ qt_internal_extend_target(Gui CONDITION QT_FEATURE_freetype
WrapFreetype::WrapFreetype
)
-qt_internal_extend_target(Gui CONDITION QT_FEATURE_freetype AND UNIX AND NOT APPLE
+qt_internal_extend_target(Gui CONDITION UNIX AND NOT APPLE
SOURCES
text/unix/qgenericunixfontdatabase_p.h
)
@@ -776,12 +756,12 @@ qt_internal_extend_target(Gui CONDITION QT_FEATURE_direct2d AND QT_FEATURE_direc
SOURCES
text/windows/qwindowsdirectwritefontdatabase.cpp text/windows/qwindowsdirectwritefontdatabase_p.h
LIBRARIES
- dwrite # special case
+ dwrite
)
qt_internal_extend_target(Gui CONDITION QT_FEATURE_direct2d AND QT_FEATURE_directwrite AND WIN32 AND NOT QT_FEATURE_directwrite3
LIBRARIES
- dwrite # special case
+ dwrite
)
qt_internal_extend_target(Gui CONDITION MINGW AND WIN32
@@ -847,7 +827,7 @@ qt_internal_extend_target(Gui CONDITION QT_FEATURE_opengles2
opengl/qopengles2ext.h
)
-qt_internal_extend_target(Gui CONDITION QT_FEATURE_egl AND QT_FEATURE_opengl
+qt_internal_extend_target(Gui CONDITION QT_FEATURE_egl AND QT_FEATURE_opengl AND NOT WASM
SOURCES
opengl/platform/egl/qeglpbuffer.cpp opengl/platform/egl/qeglpbuffer_p.h
opengl/platform/egl/qeglplatformcontext.cpp opengl/platform/egl/qeglplatformcontext_p.h
@@ -877,8 +857,6 @@ qt_internal_extend_target(Gui CONDITION QT_FEATURE_filesystemmodel
qt_internal_extend_target(Gui CONDITION QT_FEATURE_vulkan
SOURCES
rhi/qrhivulkan.cpp rhi/qrhivulkan_p.h
- rhi/qrhivulkan_p_p.h
- rhi/qrhivulkanext_p.h
vulkan/qbasicvulkanplatforminstance.cpp vulkan/qbasicvulkanplatforminstance_p.h
vulkan/qplatformvulkaninstance.cpp vulkan/qplatformvulkaninstance.h
vulkan/qvulkandefaultinstance.cpp vulkan/qvulkandefaultinstance_p.h
@@ -891,8 +869,6 @@ if(QT_FEATURE_vulkan)
Gui WrapVulkanHeaders::WrapVulkanHeaders)
endif()
-#### Keys ignored in scope 111:.:vulkan:vulkan/vulkan.pri:QT_FEATURE_vkgen:
-# special case begin
# We must always generate syncqt-injected header files,
# because we added a custom command earlier for those for framework builds.
set(vulkan_fun "qvulkanfunctions.h")
@@ -919,7 +895,7 @@ if (QT_FEATURE_vulkan)
list(APPEND vulkan_fun_command_content
COMMAND "${qvkgen}"
"${CMAKE_CURRENT_SOURCE_DIR}/vulkan/vk.xml"
- "${CMAKE_CURRENT_SOURCE_DIR}/vulkan/generated_header.txt"
+ "${CMAKE_CURRENT_SOURCE_DIR}/vulkan/licenseheader.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/vulkan/qvulkanfunctions"
DEPENDS vulkan/vk.xml ${qvkgen}
COMMENT "Generating vulkan data"
@@ -943,41 +919,23 @@ add_custom_command(
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
VERBATIM
)
-# special case end
-
-#### Keys ignored in scope 68:.:vulkan:vulkan/vulkan.pri:QT_FEATURE_vkgen:
-# QMAKE_EXTRA_COMPILERS = "qvkgen_h" "qvkgen_ph" "qvkgen_pimpl"
-# QMAKE_QVKGEN_INPUT = "vulkan/vk.xml"
-# QMAKE_QVKGEN_LICENSE_HEADER = "$$QT_SOURCE_TREE/header.LGPL"
-# qvkgen_h.commands = "$$QMAKE_QVKGEN" "${QMAKE_FILE_IN}" "$$shell_quote($$QMAKE_QVKGEN_LICENSE_HEADER)" "${QMAKE_FILE_OUT_PATH}/${QMAKE_FILE_OUT_BASE}"
-# qvkgen_h.input = "QMAKE_QVKGEN_INPUT"
-# qvkgen_h.output = "$$OUT_PWD/vulkan/qvulkanfunctions.h"
-# qvkgen_ph.commands = "$$escape_expand(\\n)"
-# qvkgen_ph.depends = "$$OUT_PWD/vulkan/qvulkanfunctions.h"
-# qvkgen_ph.input = "QMAKE_QVKGEN_INPUT"
-# qvkgen_ph.output = "$$OUT_PWD/vulkan/qvulkanfunctions_p.h"
-# qvkgen_pimpl.commands = "$$escape_expand(\\n)"
-# qvkgen_pimpl.depends = "$$OUT_PWD/vulkan/qvulkanfunctions_p.h"
-# qvkgen_pimpl.input = "QMAKE_QVKGEN_INPUT"
-# qvkgen_pimpl.output = "$$OUT_PWD/vulkan/qvulkanfunctions_p.cpp"
-
-#### Keys ignored in scope 112:.:vulkan:vulkan/vulkan.pri:QT_FEATURE_vulkan:
-# qvkgen_h.variable_out = "HEADERS"
-
-#### Keys ignored in scope 113:.:vulkan:vulkan/vulkan.pri:else:
-# qvkgen_h.CONFIG = "target_predeps" "no_link"
qt_internal_extend_target(Gui CONDITION WASM
SOURCES
+ platform/wasm/qlocalfileapi.cpp platform/wasm/qlocalfileapi_p.h
platform/wasm/qwasmlocalfileaccess.cpp platform/wasm/qwasmlocalfileaccess_p.h
)
qt_internal_extend_target(Gui CONDITION UNIX
SOURCES
+ platform/unix/qunixnativeinterface.cpp
+)
+
+qt_internal_extend_target(Gui CONDITION UNIX AND NOT WASM
+ SOURCES
platform/unix/qgenericunixeventdispatcher.cpp platform/unix/qgenericunixeventdispatcher_p.h
platform/unix/qunixeventdispatcher.cpp
platform/unix/qunixeventdispatcher_qpa_p.h
- platform/unix/qunixnativeinterface.cpp
)
qt_internal_extend_target(Gui CONDITION QT_FEATURE_glib AND UNIX
@@ -1031,39 +989,42 @@ qt_internal_extend_target(Gui CONDITION QT_FEATURE_xkbcommon AND UNIX
platform/unix/qxkbcommon_3rdparty.cpp
LIBRARIES
XKB::XKB
-# special case begin
PRIVATE_MODULE_INTERFACE
XKB::XKB
-# special case end
)
-qt_internal_extend_target(Gui CONDITION IOS OR MACOS
+qt_internal_extend_target(Gui CONDITION QT_FEATURE_metal
SOURCES
rhi/qrhimetal.mm rhi/qrhimetal_p.h
- rhi/qrhimetal_p_p.h
PUBLIC_LIBRARIES
${FWMetal}
)
-qt_internal_extend_target(Gui # special case CONDITION NOT GCC OR NOT QT_COMPILER_VERSION_MAJOR STREQUAL 5 # source subtraction gone wrong
+qt_internal_extend_target(Gui
SOURCES
painting/qdrawhelper.cpp painting/qdrawhelper_neon_p.h
NO_PCH_SOURCES
"painting/qdrawhelper.cpp"
)
-qt_internal_extend_target(Gui CONDITION (QT_FEATURE_eglfs OR QT_FEATURE_xcb)
+qt_internal_extend_target(Gui CONDITION (QT_FEATURE_eglfs OR QT_FEATURE_xcb OR QT_FEATURE_direct2d OR WIN32)
SOURCES
util/qedidparser.cpp util/qedidparser_p.h
util/qedidvendortable_p.h
)
-
-qt_internal_create_tracepoints(Gui qtgui.tracepoints)
+qt_internal_generate_tracepoints(Gui gui
+ SOURCES
+ image/qimage.cpp image/qimagereader.cpp image/qpixmap.cpp kernel/qguiapplication.cpp
+ text/qfontdatabase.cpp
+)
qt_internal_add_docs(Gui
doc/qtgui.qdocconf
)
-# special case begin
+if(IOS)
+ qt_internal_set_apple_privacy_manifest(Gui
+ "${CMAKE_CURRENT_SOURCE_DIR}/platform/ios/PrivacyInfo.xcprivacy")
+endif()
+
qt_internal_add_optimize_full_flags()
-# special case end
diff --git a/src/gui/accessible/linux/atspiadaptor.cpp b/src/gui/accessible/linux/atspiadaptor.cpp
index 426be1203e..b3269a2a95 100644
--- a/src/gui/accessible/linux/atspiadaptor.cpp
+++ b/src/gui/accessible/linux/atspiadaptor.cpp
@@ -11,10 +11,12 @@
#include <qclipboard.h>
#include <QtCore/qloggingcategory.h>
+#include <QtCore/qtversion.h>
#if QT_CONFIG(accessibility)
#include "socket_interface.h"
#include "qspi_constant_mappings_p.h"
+#include <QtCore/private/qstringiterator_p.h>
#include <QtGui/private/qaccessiblebridgeutils_p.h>
#include "qspiapplicationadaptor_p.h"
@@ -28,6 +30,11 @@
It sends notifications coming from Qt via dbus and listens to incoming dbus requests.
*/
+// ATSPI_COORD_TYPE_PARENT was added in at-spi 2.30, define here for older versions
+#if ATSPI_COORD_TYPE_COUNT < 3
+#define ATSPI_COORD_TYPE_PARENT 2
+#endif
+
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
@@ -161,6 +168,9 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <arg direction=\"out\" type=\"(so)\"/>\n"
" <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
" </method>\n"
+ " <method name=\"GetAccessibleId\">\n"
+ " <arg direction=\"out\" type=\"s\"/>\n"
+ " </method>\n"
" </interface>\n"
);
@@ -301,6 +311,39 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" </interface>\n"
);
+ static const QLatin1StringView selectionIntrospection(
+ " <interface name=\"org.a11y.atspi.Selection\">\n"
+ " <property name=\"NSelectedChildren\" type=\"i\" access=\"read\"/>\n"
+ " <method name=\"GetSelectedChild\">\n"
+ " <arg direction=\"in\" name=\"selectedChildIndex\" type=\"i\"/>\n"
+ " <arg direction=\"out\" type=\"(so)\"/>\n"
+ " <annotation name=\"org.qtproject.QtDBus.QtTypeName.Out0\" value=\"QSpiObjectReference\"/>\n"
+ " </method>\n"
+ " <method name=\"SelectChild\">\n"
+ " <arg direction=\"in\" name=\"childIndex\" type=\"i\"/>\n"
+ " <arg direction=\"out\" type=\"b\"/>\n"
+ " </method>\n"
+ " <method name=\"DeselectSelectedChild\">\n"
+ " <arg direction=\"in\" name=\"selectedChildIndex\" type=\"i\"/>\n"
+ " <arg direction=\"out\" type=\"b\"/>\n"
+ " </method>\n"
+ " <method name=\"IsChildSelected\">\n"
+ " <arg direction=\"in\" name=\"childIndex\" type=\"i\"/>\n"
+ " <arg direction=\"out\" type=\"b\"/>\n"
+ " </method>\n"
+ " <method name=\"SelectAll\">\n"
+ " <arg direction=\"out\" type=\"b\"/>\n"
+ " </method>\n"
+ " <method name=\"ClearSelection\">\n"
+ " <arg direction=\"out\" type=\"b\"/>\n"
+ " </method>\n"
+ " <method name=\"DeselectChild\">\n"
+ " <arg direction=\"in\" name=\"childIndex\" type=\"i\"/>\n"
+ " <arg direction=\"out\" type=\"b\"/>\n"
+ " </method>\n"
+ " </interface>\n"
+ );
+
static const QLatin1StringView tableIntrospection(
" <interface name=\"org.a11y.atspi.Table\">\n"
" <property access=\"read\" type=\"i\" name=\"NRows\"/>\n"
@@ -409,10 +452,45 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" </interface>\n"
);
+ static const QLatin1StringView tableCellIntrospection(
+ " <interface name=\"org.a11y.atspi.TableCell\">\n"
+ " <property access=\"read\" name=\"ColumnSpan\" type=\"i\" />\n"
+ " <property access=\"read\" name=\"Position\" type=\"(ii)\">\n"
+ " <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"QPoint\"/>\n"
+ " </property>\n"
+ " <property access=\"read\" name=\"RowSpan\" type=\"i\" />\n"
+ " <property access=\"read\" name=\"Table\" type=\"(so)\" >\n"
+ " <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"QSpiObjectReference\"/>\n"
+ " </property>\n"
+ " <method name=\"GetRowColumnSpan\">\n"
+ " <arg direction=\"out\" type=\"b\" />\n"
+ " <arg direction=\"out\" name=\"row\" type=\"i\" />\n"
+ " <arg direction=\"out\" name=\"col\" type=\"i\" />\n"
+ " <arg direction=\"out\" name=\"row_extents\" type=\"i\" />\n"
+ " <arg direction=\"out\" name=\"col_extents\" type=\"i\" />\n"
+ " </method>\n"
+ " <method name=\"GetColumnHeaderCells\">\n"
+ " <arg direction=\"out\" type=\"a(so)\"/>\n"
+ " <annotation value=\"QSpiObjectReferenceArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+ " </method>\n"
+ " <method name=\"GetRowHeaderCells\">\n"
+ " <arg direction=\"out\" type=\"a(so)\"/>\n"
+ " <annotation value=\"QSpiObjectReferenceArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+ " </method>\n"
+ " </interface>\n"
+ );
+
static const QLatin1StringView textIntrospection(
" <interface name=\"org.a11y.atspi.Text\">\n"
" <property access=\"read\" type=\"i\" name=\"CharacterCount\"/>\n"
" <property access=\"read\" type=\"i\" name=\"CaretOffset\"/>\n"
+ " <method name=\"GetStringAtOffset\">\n"
+ " <arg direction=\"in\" name=\"offset\" type=\"i\"/>\n"
+ " <arg direction=\"in\" name=\"granularity\" type=\"u\"/>\n"
+ " <arg direction=\"out\" type=\"s\"/>\n"
+ " <arg direction=\"out\" name=\"startOffset\" type=\"i\"/>\n"
+ " <arg direction=\"out\" name=\"endOffset\" type=\"i\"/>\n"
+ " </method>\n"
" <method name=\"GetText\">\n"
" <arg direction=\"in\" type=\"i\" name=\"startOffset\"/>\n"
" <arg direction=\"in\" type=\"i\" name=\"endOffset\"/>\n"
@@ -451,9 +529,6 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
" <arg direction=\"in\" type=\"s\" name=\"attributeName\"/>\n"
" <arg direction=\"out\" type=\"s\"/>\n"
- " <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
- " <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
- " <arg direction=\"out\" type=\"b\" name=\"defined\"/>\n"
" </method>\n"
" <method name=\"GetAttributes\">\n"
" <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
@@ -482,6 +557,7 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" </method>\n"
" <method name=\"GetNSelections\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
+ " </method>\n"
" <method name=\"GetSelection\">\n"
" <arg direction=\"in\" type=\"i\" name=\"selectionNum\"/>\n"
" <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
@@ -534,6 +610,12 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <arg direction=\"out\" type=\"a{ss}\"/>\n"
" <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
" </method>\n"
+ " <method name=\"ScrollSubstringTo\">\n"
+ " <arg direction=\"in\" name=\"startOffset\" type=\"i\"/>\n"
+ " <arg direction=\"in\" name=\"endOffset\" type=\"i\"/>\n"
+ " <arg direction=\"in\" name=\"type\" type=\"u\"/>\n"
+ " <arg direction=\"out\" type=\"b\"/>\n"
+ " </method>\n"
" </interface>\n"
);
@@ -551,7 +633,7 @@ QString AtSpiAdaptor::introspect(const QString &path) const
QAccessibleInterface * interface = interfaceFromPath(path);
if (!interface) {
- qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find accessible on path: " << path;
+ qCWarning(lcAccessibilityAtspi) << "Could not find accessible on path:" << path;
return QString();
}
@@ -568,8 +650,12 @@ QString AtSpiAdaptor::introspect(const QString &path) const
xml.append(editableTextIntrospection);
if (interfaces.contains(ATSPI_DBUS_INTERFACE_ACTION ""_L1))
xml.append(actionIntrospection);
+ if (interfaces.contains(ATSPI_DBUS_INTERFACE_SELECTION ""_L1))
+ xml.append(selectionIntrospection);
if (interfaces.contains(ATSPI_DBUS_INTERFACE_TABLE ""_L1))
xml.append(tableIntrospection);
+ if (interfaces.contains(ATSPI_DBUS_INTERFACE_TABLE_CELL ""_L1))
+ xml.append(tableCellIntrospection);
if (interfaces.contains(ATSPI_DBUS_INTERFACE_VALUE ""_L1))
xml.append(valueIntrospection);
if (path == QSPI_OBJECT_PATH_ROOT ""_L1)
@@ -660,7 +746,7 @@ void AtSpiAdaptor::setBitFlag(const QString &flag)
|| right.startsWith("VisibledataChanged"_L1)) { // typo in libatspi
sendObject_visible_data_changed = 1;
} else {
- qCDebug(lcAccessibilityAtspi) << "WARNING: subscription string not handled:" << flag;
+ qCWarning(lcAccessibilityAtspi) << "Subscription string not handled:" << flag;
}
}
break;
@@ -706,7 +792,7 @@ void AtSpiAdaptor::setBitFlag(const QString &flag)
} else if (right.startsWith("DesktopDestroy"_L1)) {
// ignore this one
} else {
- qCDebug(lcAccessibilityAtspi) << "WARNING: subscription string not handled:" << flag;
+ qCWarning(lcAccessibilityAtspi) << "Subscription string not handled:" << flag;
}
}
break;
@@ -725,7 +811,7 @@ void AtSpiAdaptor::setBitFlag(const QString &flag)
break;
}
default:
- qCDebug(lcAccessibilityAtspi) << "WARNING: subscription string not handled:" << flag;
+ qCWarning(lcAccessibilityAtspi) << "Subscription string not handled:" << flag;
}
}
@@ -878,8 +964,17 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event)
}
case QAccessible::NameChanged: {
if (sendObject || sendObject_property_change || sendObject_property_change_accessible_name) {
- QString path = pathForInterface(event->accessibleInterface());
- QVariantList args = packDBusSignalArguments("accessible-name"_L1, 0, 0, variantForPath(path));
+ QAccessibleInterface *iface = event->accessibleInterface();
+ if (!iface) {
+ qCDebug(lcAccessibilityAtspi,
+ "NameChanged event from invalid accessible.");
+ return;
+ }
+
+ QString path = pathForInterface(iface);
+ QVariantList args = packDBusSignalArguments(
+ "accessible-name"_L1, 0, 0,
+ QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Name))));
sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
"PropertyChange"_L1, args);
}
@@ -887,8 +982,17 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event)
}
case QAccessible::DescriptionChanged: {
if (sendObject || sendObject_property_change || sendObject_property_change_accessible_description) {
- QString path = pathForInterface(event->accessibleInterface());
- QVariantList args = packDBusSignalArguments("accessible-description"_L1, 0, 0, variantForPath(path));
+ QAccessibleInterface *iface = event->accessibleInterface();
+ if (!iface) {
+ qCDebug(lcAccessibilityAtspi,
+ "DescriptionChanged event from invalid accessible.");
+ return;
+ }
+
+ QString path = pathForInterface(iface);
+ QVariantList args = packDBusSignalArguments(
+ "accessible-description"_L1, 0, 0,
+ QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Description))));
sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
"PropertyChange"_L1, args);
}
@@ -905,7 +1009,7 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event)
if (sendObject || sendObject_text_changed) {
QAccessibleInterface * iface = event->accessibleInterface();
if (!iface || !iface->textInterface()) {
- qCDebug(lcAccessibilityAtspi) << "Received text event for invalid interface.";
+ qCWarning(lcAccessibilityAtspi) << "Received text event for invalid interface.";
return;
}
QString path = pathForInterface(iface);
@@ -937,14 +1041,14 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event)
if (!textRemoved.isEmpty()) {
data.setVariant(QVariant::fromValue(textRemoved));
- QVariantList args = packDBusSignalArguments("delete"_L1, changePosition, textRemoved.length(), QVariant::fromValue(data));
+ QVariantList args = packDBusSignalArguments("delete"_L1, changePosition, textRemoved.size(), QVariant::fromValue(data));
sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
"TextChanged"_L1, args);
}
if (!textInserted.isEmpty()) {
data.setVariant(QVariant::fromValue(textInserted));
- QVariantList args = packDBusSignalArguments("insert"_L1, changePosition, textInserted.length(), QVariant::fromValue(data));
+ QVariantList args = packDBusSignalArguments("insert"_L1, changePosition, textInserted.size(), QVariant::fromValue(data));
sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
"TextChanged"_L1, args);
}
@@ -1003,7 +1107,9 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event)
// Combo Box with AT-SPI likes to be special
// It requires a name-change to update caches and then selection-changed
QString path = pathForInterface(iface);
- QVariantList args1 = packDBusSignalArguments("accessible-name"_L1, 0, 0, variantForPath(path));
+ QVariantList args1 = packDBusSignalArguments(
+ "accessible-name"_L1, 0, 0,
+ QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Name))));
sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
"PropertyChange"_L1, args1);
QVariantList args2 = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(0))));
@@ -1023,13 +1129,37 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event)
qCWarning(lcAccessibilityAtspi) << "Selection event from invalid accessible.";
return;
}
+ // send event for change of selected state for the interface itself
QString path = pathForInterface(iface);
int selected = iface->state().selected ? 1 : 0;
QVariantList stateArgs = packDBusSignalArguments("selected"_L1, selected, 0, variantForPath(path));
sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "StateChanged"_L1, stateArgs);
+
+ // send SelectionChanged event for the parent
+ QAccessibleInterface* parent = iface->parent();
+ if (!parent) {
+ qCDebug(lcAccessibilityAtspi) << "No valid parent in selection event.";
+ return;
+ }
+
+ QString parentPath = pathForInterface(parent);
+ QVariantList args = packDBusSignalArguments(QString(), 0, 0, variantForPath(parentPath));
+ sendDBusSignal(parentPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+ QLatin1String("SelectionChanged"), args);
break;
}
+ case QAccessible::SelectionWithin: {
+ QAccessibleInterface * iface = event->accessibleInterface();
+ if (!iface) {
+ qCWarning(lcAccessibilityAtspi) << "SelectionWithin event from invalid accessible.";
+ return;
+ }
+ QString path = pathForInterface(iface);
+ QVariantList args = packDBusSignalArguments(QString(), 0, 0, variantForPath(path));
+ sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("SelectionChanged"), args);
+ break;
+ }
case QAccessible::StateChanged: {
if (sendObject || sendObject_state_changed || sendWindow || sendWindow_activate) {
QAccessible::State stateChange = static_cast<QAccessibleStateChangeEvent*>(event)->changedStates();
@@ -1061,15 +1191,80 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event)
notifyStateChange(iface, "enabled"_L1, enabled);
notifyStateChange(iface, "sensitive"_L1, enabled);
+ } else if (stateChange.focused) {
+ QAccessibleInterface *iface = event->accessibleInterface();
+ QAccessible::State state = iface->state();
+ bool focused = state.focused;
+ notifyStateChange(iface, "focused"_L1, focused);
}
}
break;
}
+ case QAccessible::TableModelChanged: {
+ QAccessibleInterface *interface = event->accessibleInterface();
+ if (!interface || !interface->isValid()) {
+ qCWarning(lcAccessibilityAtspi) << "TableModelChanged event from invalid accessible.";
+ return;
+ }
+
+ const QString path = pathForInterface(interface);
+ QAccessibleTableModelChangeEvent *tableModelEvent = static_cast<QAccessibleTableModelChangeEvent*>(event);
+ switch (tableModelEvent->modelChangeType()) {
+ case QAccessibleTableModelChangeEvent::ColumnsInserted: {
+ if (sendObject || sendObject_column_inserted) {
+ const int firstColumn = tableModelEvent->firstColumn();
+ const int insertedColumnCount = tableModelEvent->lastColumn() - firstColumn + 1;
+ QVariantList args = packDBusSignalArguments(QString(), firstColumn, insertedColumnCount, QVariant::fromValue(QDBusVariant(QVariant(QString()))));
+ sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "ColumnInserted"_L1, args);
+ }
+ break;
+ }
+ case QAccessibleTableModelChangeEvent::ColumnsRemoved: {
+ if (sendObject || sendObject_column_deleted) {
+ const int firstColumn = tableModelEvent->firstColumn();
+ const int removedColumnCount = tableModelEvent->lastColumn() - firstColumn + 1;
+ QVariantList args = packDBusSignalArguments(QString(), firstColumn, removedColumnCount, QVariant::fromValue(QDBusVariant(QVariant(QString()))));
+ sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "ColumnDeleted"_L1, args);
+ }
+ break;
+ }
+ case QAccessibleTableModelChangeEvent::RowsInserted: {
+ if (sendObject || sendObject_row_inserted) {
+ const int firstRow = tableModelEvent->firstRow();
+ const int insertedRowCount = tableModelEvent->lastRow() - firstRow + 1;
+ QVariantList args = packDBusSignalArguments(QString(), firstRow, insertedRowCount, QVariant::fromValue(QDBusVariant(QVariant(QString()))));
+ sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "RowInserted"_L1, args);
+ }
+ break;
+ }
+ case QAccessibleTableModelChangeEvent::RowsRemoved: {
+ if (sendObject || sendObject_row_deleted) {
+ const int firstRow = tableModelEvent->firstRow();
+ const int removedRowCount = tableModelEvent->lastRow() - firstRow + 1;
+ QVariantList args = packDBusSignalArguments(QString(), firstRow, removedRowCount, QVariant::fromValue(QDBusVariant(QVariant(QString()))));
+ sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "RowDeleted"_L1, args);
+ }
+ break;
+ }
+ case QAccessibleTableModelChangeEvent::ModelChangeType::ModelReset: {
+ if (sendObject || sendObject_model_changed) {
+ QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(QString()))));
+ sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "ModelChanged"_L1, args);
+ }
+ break;
+ }
+ case QAccessibleTableModelChangeEvent::DataChanged: {
+ if (sendObject || sendObject_visible_data_changed) {
+ QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(QString()))));
+ sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "VisibleDataChanged"_L1, args);
+ }
+ break;
+ }
+ }
+ break;
+ }
+
// For now we ignore these events
- case QAccessible::TableModelChanged:
- // For tables, setting manages_descendants should
- // indicate to the client that it cannot cache these
- // interfaces.
case QAccessible::ParentChanged:
case QAccessible::DialogStart:
case QAccessible::DialogEnd:
@@ -1114,7 +1309,6 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event)
case QAccessible::TextAttributeChanged:
case QAccessible::TextColumnChanged:
case QAccessible::VisibleDataChanged:
- case QAccessible::SelectionWithin:
case QAccessible::LocationChanged:
case QAccessible::HelpChanged:
case QAccessible::DefaultActionChanged:
@@ -1160,9 +1354,6 @@ void AtSpiAdaptor::childrenChanged(QAccessibleInterface *interface) const
void AtSpiAdaptor::notifyAboutCreation(QAccessibleInterface *interface) const
{
-// // say hello to d-bus
-// cache->emitAddAccessible(accessible->getCacheItem());
-
// notify about the new child of our parent
QAccessibleInterface * parent = interface->parent();
if (!parent) {
@@ -1211,11 +1402,11 @@ bool AtSpiAdaptor::handleMessage(const QDBusMessage &message, const QDBusConnect
// get accessible interface
QAccessibleInterface * accessible = interfaceFromPath(message.path());
if (!accessible) {
- qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find accessible on path: " << message.path();
+ qCWarning(lcAccessibilityAtspi) << "Could not find accessible on path:" << message.path();
return false;
}
if (!accessible->isValid()) {
- qCWarning(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Accessible invalid: " << accessible << message.path();
+ qCWarning(lcAccessibilityAtspi) << "Accessible invalid:" << accessible << message.path();
return false;
}
@@ -1245,6 +1436,8 @@ bool AtSpiAdaptor::handleMessage(const QDBusMessage &message, const QDBusConnect
return componentInterface(accessible, function, message, connection);
if (interface == ATSPI_DBUS_INTERFACE_ACTION ""_L1)
return actionInterface(accessible, function, message, connection);
+ if (interface == ATSPI_DBUS_INTERFACE_SELECTION ""_L1)
+ return selectionInterface(accessible, function, message, connection);
if (interface == ATSPI_DBUS_INTERFACE_TEXT ""_L1)
return textInterface(accessible, function, message, connection);
if (interface == ATSPI_DBUS_INTERFACE_EDITABLE_TEXT ""_L1)
@@ -1253,6 +1446,8 @@ bool AtSpiAdaptor::handleMessage(const QDBusMessage &message, const QDBusConnect
return valueInterface(accessible, function, message, connection);
if (interface == ATSPI_DBUS_INTERFACE_TABLE ""_L1)
return tableInterface(accessible, function, message, connection);
+ if (interface == ATSPI_DBUS_INTERFACE_TABLE_CELL ""_L1)
+ return tableCellInterface(accessible, function, message, connection);
qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::handleMessage with unknown interface: " << message.path() << interface << function;
return false;
@@ -1262,7 +1457,7 @@ bool AtSpiAdaptor::handleMessage(const QDBusMessage &message, const QDBusConnect
bool AtSpiAdaptor::applicationInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
{
if (message.path() != ATSPI_DBUS_PATH_ROOT ""_L1) {
- qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find application interface for: " << message.path() << interface;
+ qCWarning(lcAccessibilityAtspi) << "Could not find application interface for:" << message.path() << interface;
return false;
}
@@ -1314,13 +1509,33 @@ void AtSpiAdaptor::registerApplication()
const QSpiObjectReference &socket = reply.value();
accessibilityRegistry = QSpiObjectReference(socket);
} else {
- qCDebug(lcAccessibilityAtspi) << "Error in contacting registry: "
+ qCWarning(lcAccessibilityAtspi) << "Error in contacting registry:"
<< reply.error().name()
<< reply.error().message();
}
delete registry;
}
+namespace {
+QString accessibleIdForAccessible(QAccessibleInterface *accessible)
+{
+ QString result;
+ while (accessible) {
+ if (!result.isEmpty())
+ result.prepend(u'.');
+ if (auto obj = accessible->object()) {
+ const QString name = obj->objectName();
+ if (!name.isEmpty())
+ result.prepend(name);
+ else
+ result.prepend(QString::fromUtf8(obj->metaObject()->className()));
+ }
+ accessible = accessible->parent();
+ }
+ return result;
+}
+} // namespace
+
// Accessible
bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
{
@@ -1374,6 +1589,9 @@ bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QS
} else if (function == "GetState"_L1) {
quint64 spiState = spiStatesFromQState(interface->state());
if (interface->tableInterface()) {
+ // For tables, setting manages_descendants should
+ // indicate to the client that it cannot cache these
+ // interfaces.
setSpiStateBit(&spiState, ATSPI_STATE_MANAGES_DESCENDANTS);
}
QAccessible::Role role = interface->role();
@@ -1404,8 +1622,11 @@ bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QS
children << ref;
}
connection.send(message.createReply(QVariant::fromValue(children)));
+ } else if (function == "GetAccessibleId"_L1) {
+ sendReply(connection, message,
+ QVariant::fromValue(QDBusVariant(accessibleIdForAccessible(interface))));
} else {
- qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::accessibleInterface does not implement " << function << message.path();
+ qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::accessibleInterface does not implement" << function << message.path();
return false;
}
return true;
@@ -1422,7 +1643,7 @@ QStringList AtSpiAdaptor::accessibleInterfaces(QAccessibleInterface *interface)
{
QStringList ifaces;
qCDebug(lcAccessibilityAtspiCreation) << "AtSpiAdaptor::accessibleInterfaces create: " << interface->object();
- ifaces << ATSPI_DBUS_INTERFACE_ACCESSIBLE ""_L1;
+ ifaces << u"" ATSPI_DBUS_INTERFACE_ACCESSIBLE ""_s;
if ( (!interface->rect().isEmpty()) ||
(interface->object() && interface->object()->isWidgetType()) ||
@@ -1432,27 +1653,33 @@ QStringList AtSpiAdaptor::accessibleInterfaces(QAccessibleInterface *interface)
(interface->role() == QAccessible::Row) ||
(interface->object() && interface->object()->inherits("QSGItem"))
) {
- ifaces << ATSPI_DBUS_INTERFACE_COMPONENT ""_L1;
+ ifaces << u"" ATSPI_DBUS_INTERFACE_COMPONENT ""_s;
} else {
qCDebug(lcAccessibilityAtspiCreation) << " IS NOT a component";
}
if (interface->role() == QAccessible::Application)
- ifaces << ATSPI_DBUS_INTERFACE_APPLICATION ""_L1;
+ ifaces << u"" ATSPI_DBUS_INTERFACE_APPLICATION ""_s;
if (interface->actionInterface() || interface->valueInterface())
- ifaces << ATSPI_DBUS_INTERFACE_ACTION ""_L1;
+ ifaces << u"" ATSPI_DBUS_INTERFACE_ACTION ""_s;
+
+ if (interface->selectionInterface())
+ ifaces << ATSPI_DBUS_INTERFACE_SELECTION ""_L1;
if (interface->textInterface())
- ifaces << ATSPI_DBUS_INTERFACE_TEXT ""_L1;
+ ifaces << u"" ATSPI_DBUS_INTERFACE_TEXT ""_s;
if (interface->editableTextInterface())
- ifaces << ATSPI_DBUS_INTERFACE_EDITABLE_TEXT ""_L1;
+ ifaces << u"" ATSPI_DBUS_INTERFACE_EDITABLE_TEXT ""_s;
if (interface->valueInterface())
- ifaces << ATSPI_DBUS_INTERFACE_VALUE ""_L1;
+ ifaces << u"" ATSPI_DBUS_INTERFACE_VALUE ""_s;
if (interface->tableInterface())
- ifaces << ATSPI_DBUS_INTERFACE_TABLE ""_L1;
+ ifaces << u"" ATSPI_DBUS_INTERFACE_TABLE ""_s;
+
+ if (interface->tableCellInterface())
+ ifaces << u"" ATSPI_DBUS_INTERFACE_TABLE_CELL ""_s;
return ifaces;
}
@@ -1489,7 +1716,7 @@ QString AtSpiAdaptor::pathForObject(QObject *object) const
Q_ASSERT(object);
if (inheritsQAction(object)) {
- qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::pathForObject: warning: creating path with QAction as object.";
+ qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::pathForObject: Creating path with QAction as object.";
}
QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(object);
@@ -1499,9 +1726,9 @@ QString AtSpiAdaptor::pathForObject(QObject *object) const
QString AtSpiAdaptor::pathForInterface(QAccessibleInterface *interface) const
{
if (!interface || !interface->isValid())
- return ATSPI_DBUS_PATH_NULL ""_L1;
+ return u"" ATSPI_DBUS_PATH_NULL ""_s;
if (interface->role() == QAccessible::Application)
- return QSPI_OBJECT_PATH_ROOT ""_L1;
+ return u"" QSPI_OBJECT_PATH_ROOT ""_s;
QAccessible::Id id = QAccessible::uniqueId(interface);
Q_ASSERT((int)id < 0);
@@ -1523,31 +1750,14 @@ bool AtSpiAdaptor::inheritsQAction(QObject *object)
// Component
static QAccessibleInterface * getWindow(QAccessibleInterface * interface)
{
- if (interface->role() == QAccessible::Window)
- return interface;
-
- QAccessibleInterface * parent = interface->parent();
- while (parent && parent->role() != QAccessible::Window)
- parent = parent->parent();
-
- return parent;
-}
-
-static QRect getRelativeRect(QAccessibleInterface *interface)
-{
- QAccessibleInterface * window;
- QRect wr, cr;
-
- cr = interface->rect();
-
- window = getWindow(interface);
- if (window) {
- wr = window->rect();
-
- cr.setX(cr.x() - wr.x());
- cr.setY(cr.x() - wr.y());
- }
- return cr;
+ // find top-level window in a11y hierarchy (either has a
+ // corresponding role or is a direct child of the application object)
+ QAccessibleInterface* app = QAccessible::queryAccessibleInterface(qApp);
+ while (interface && interface->role() != QAccessible::Dialog
+ && interface->role() != QAccessible::Window && interface->parent() != app)
+ interface = interface->parent();
+
+ return interface;
}
bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
@@ -1557,28 +1767,22 @@ bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QSt
int x = message.arguments().at(0).toInt();
int y = message.arguments().at(1).toInt();
uint coordType = message.arguments().at(2).toUInt();
- if (coordType == ATSPI_COORD_TYPE_SCREEN)
- ret = interface->rect().contains(x, y);
- else
- ret = getRelativeRect(interface).contains(x, y);
+ if (!isValidCoordType(coordType))
+ return false;
+ ret = getExtents(interface, coordType).contains(x, y);
sendReply(connection, message, ret);
} else if (function == "GetAccessibleAtPoint"_L1) {
- int x = message.arguments().at(0).toInt();
- int y = message.arguments().at(1).toInt();
+ QPoint point(message.arguments().at(0).toInt(), message.arguments().at(1).toInt());
uint coordType = message.arguments().at(2).toUInt();
- if (coordType == ATSPI_COORD_TYPE_WINDOW) {
- QWindow * window = interface->window();
- if (window) {
- x += window->position().x();
- y += window->position().y();
- }
- }
+ if (!isValidCoordType(coordType))
+ return false;
+ QPoint screenPos = translateToScreenCoordinates(interface, point, coordType);
- QAccessibleInterface * childInterface(interface->childAt(x, y));
+ QAccessibleInterface * childInterface(interface->childAt(screenPos.x(), screenPos.y()));
QAccessibleInterface * iface = nullptr;
while (childInterface) {
iface = childInterface;
- childInterface = iface->childAt(x, y);
+ childInterface = iface->childAt(screenPos.x(), screenPos.y());
}
if (iface) {
QString path = pathForInterface(iface);
@@ -1592,6 +1796,8 @@ bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QSt
sendReply(connection, message, (double) 1.0);
} else if (function == "GetExtents"_L1) {
uint coordType = message.arguments().at(0).toUInt();
+ if (!isValidCoordType(coordType))
+ return false;
sendReply(connection, message, QVariant::fromValue(getExtents(interface, coordType)));
} else if (function == "GetLayer"_L1) {
sendReply(connection, message, QVariant::fromValue((uint)1));
@@ -1599,11 +1805,9 @@ bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QSt
sendReply(connection, message, QVariant::fromValue((short)0));
} else if (function == "GetPosition"_L1) {
uint coordType = message.arguments().at(0).toUInt();
- QRect rect;
- if (coordType == ATSPI_COORD_TYPE_SCREEN)
- rect = interface->rect();
- else
- rect = getRelativeRect(interface);
+ if (!isValidCoordType(coordType))
+ return false;
+ QRect rect = getExtents(interface, coordType);
QVariantList pos;
pos << rect.x() << rect.y();
connection.send(message.createReply(pos));
@@ -1640,7 +1844,7 @@ bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QSt
qCDebug(lcAccessibilityAtspi) << "SetSize is not implemented.";
sendReply(connection, message, false);
} else {
- qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::componentInterface does not implement " << function << message.path();
+ qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::componentInterface does not implement" << function << message.path();
return false;
}
return true;
@@ -1648,19 +1852,19 @@ bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QSt
QRect AtSpiAdaptor::getExtents(QAccessibleInterface *interface, uint coordType)
{
- return (coordType == ATSPI_COORD_TYPE_SCREEN) ? interface->rect() : getRelativeRect(interface);
+ return translateFromScreenCoordinates(interface, interface->rect(), coordType);
}
// Action interface
bool AtSpiAdaptor::actionInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
{
if (function == "GetNActions"_L1) {
- int count = QAccessibleBridgeUtils::effectiveActionNames(interface).count();
+ int count = QAccessibleBridgeUtils::effectiveActionNames(interface).size();
sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(count))));
} else if (function == "DoAction"_L1) {
int index = message.arguments().at(0).toInt();
const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface);
- if (index < 0 || index >= actionNames.count())
+ if (index < 0 || index >= actionNames.size())
return false;
const QString actionName = actionNames.at(index);
bool success = QAccessibleBridgeUtils::performEffectiveAction(interface, actionName);
@@ -1670,13 +1874,13 @@ bool AtSpiAdaptor::actionInterface(QAccessibleInterface *interface, const QStrin
} else if (function == "GetName"_L1) {
int index = message.arguments().at(0).toInt();
const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface);
- if (index < 0 || index >= actionNames.count())
+ if (index < 0 || index >= actionNames.size())
return false;
sendReply(connection, message, actionNames.at(index));
} else if (function == "GetDescription"_L1) {
int index = message.arguments().at(0).toInt();
const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface);
- if (index < 0 || index >= actionNames.count())
+ if (index < 0 || index >= actionNames.size())
return false;
QString description;
if (QAccessibleActionInterface *actionIface = interface->actionInterface())
@@ -1687,7 +1891,7 @@ bool AtSpiAdaptor::actionInterface(QAccessibleInterface *interface, const QStrin
} else if (function == "GetKeyBinding"_L1) {
int index = message.arguments().at(0).toInt();
const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface);
- if (index < 0 || index >= actionNames.count())
+ if (index < 0 || index >= actionNames.size())
return false;
QStringList keyBindings;
if (QAccessibleActionInterface *actionIface = interface->actionInterface())
@@ -1697,12 +1901,12 @@ bool AtSpiAdaptor::actionInterface(QAccessibleInterface *interface, const QStrin
if (!acc.isEmpty())
keyBindings.append(acc);
}
- if (keyBindings.length() > 0)
+ if (keyBindings.size() > 0)
sendReply(connection, message, keyBindings.join(u';'));
else
sendReply(connection, message, QString());
} else {
- qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::actionInterface does not implement " << function << message.path();
+ qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::actionInterface does not implement" << function << message.path();
return false;
}
return true;
@@ -1759,7 +1963,7 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
} else if (function == "GetAttributeValue"_L1) {
int offset = message.arguments().at(0).toInt();
QString attributeName = message.arguments().at(1).toString();
- connection.send(message.createReply(getAttributeValue(interface, offset, attributeName)));
+ connection.send(message.createReply(QVariant(getAttributeValue(interface, offset, attributeName))));
} else if (function == "GetAttributes"_L1) {
int offset = message.arguments().at(0).toInt();
connection.send(message.createReply(getAttributes(interface, offset, true)));
@@ -1784,8 +1988,13 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
int offset = message.arguments().at(0).toInt();
int start;
int end;
- QString result = interface->textInterface()->textAtOffset(offset, QAccessible::CharBoundary, &start, &end);
- sendReply(connection, message, (int) *(qPrintable (result)));
+ const QString charString = interface->textInterface()
+ ->textAtOffset(offset, QAccessible::CharBoundary, &start, &end);
+ int codePoint = 0;
+ QStringIterator stringIt(charString);
+ if (stringIt.hasNext())
+ codePoint = static_cast<int>(stringIt.peekNext());
+ sendReply(connection, message, codePoint);
} else if (function == "GetCharacterExtents"_L1) {
int offset = message.arguments().at(0).toInt();
int coordType = message.arguments().at(1).toUInt();
@@ -1801,11 +2010,10 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
Q_ASSERT(!message.signature().isEmpty());
QPoint point(message.arguments().at(0).toInt(), message.arguments().at(1).toInt());
uint coordType = message.arguments().at(2).toUInt();
- if (coordType == ATSPI_COORD_TYPE_WINDOW) {
- QWindow *win = interface->window();
- point -= QPoint(win->x(), win->y());
- }
- int offset = interface->textInterface()->offsetAtPoint(point);
+ if (!isValidCoordType(coordType))
+ return false;
+ QPoint screenPos = translateToScreenCoordinates(interface, point, coordType);
+ int offset = interface->textInterface()->offsetAtPoint(screenPos);
sendReply(connection, message, offset);
} else if (function == "GetRangeExtents"_L1) {
int startOffset = message.arguments().at(0).toInt();
@@ -1821,6 +2029,16 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
QVariantList sel;
sel << start << end;
connection.send(message.createReply(sel));
+ } else if (function == "GetStringAtOffset"_L1) {
+ int offset = message.arguments().at(0).toInt();
+ uint granularity = message.arguments().at(1).toUInt();
+ if (!isValidAtspiTextGranularity(granularity))
+ return false;
+ int startOffset, endOffset;
+ QString text = interface->textInterface()->textAtOffset(offset, qAccessibleBoundaryTypeFromAtspiTextGranularity(granularity), &startOffset, &endOffset);
+ QVariantList ret;
+ ret << text << startOffset << endOffset;
+ connection.send(message.createReply(ret));
} else if (function == "GetText"_L1) {
int startOffset = message.arguments().at(0).toInt();
int endOffset = message.arguments().at(1).toInt();
@@ -1831,7 +2049,7 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
int offset = message.arguments().at(0).toInt();
int type = message.arguments().at(1).toUInt();
int startOffset, endOffset;
- QString text = interface->textInterface()->textAfterOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset);
+ QString text = interface->textInterface()->textAfterOffset(offset, qAccessibleBoundaryTypeFromAtspiBoundaryType(type), &startOffset, &endOffset);
QVariantList ret;
ret << text << startOffset << endOffset;
connection.send(message.createReply(ret));
@@ -1839,7 +2057,7 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
int offset = message.arguments().at(0).toInt();
int type = message.arguments().at(1).toUInt();
int startOffset, endOffset;
- QString text = interface->textInterface()->textAtOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset);
+ QString text = interface->textInterface()->textAtOffset(offset, qAccessibleBoundaryTypeFromAtspiBoundaryType(type), &startOffset, &endOffset);
QVariantList ret;
ret << text << startOffset << endOffset;
connection.send(message.createReply(ret));
@@ -1847,7 +2065,7 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
int offset = message.arguments().at(0).toInt();
int type = message.arguments().at(1).toUInt();
int startOffset, endOffset;
- QString text = interface->textInterface()->textBeforeOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset);
+ QString text = interface->textInterface()->textBeforeOffset(offset, qAccessibleBoundaryTypeFromAtspiBoundaryType(type), &startOffset, &endOffset);
QVariantList ret;
ret << text << startOffset << endOffset;
connection.send(message.createReply(ret));
@@ -1859,6 +2077,13 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
int offset = message.arguments().at(0).toInt();
interface->textInterface()->setCursorPosition(offset);
sendReply(connection, message, true);
+ } else if (function == "ScrollSubstringTo"_L1) {
+ int startOffset = message.arguments().at(0).toInt();
+ int endOffset = message.arguments().at(1).toInt();
+ // ignore third parameter (scroll type), since QAccessibleTextInterface::scrollToSubstring doesn't have that
+ qCInfo(lcAccessibilityAtspi) << "AtSpiAdaptor::ScrollSubstringTo doesn'take take scroll type into account.";
+ interface->textInterface()->scrollToSubstring(startOffset, endOffset);
+ sendReply(connection, message, true);
} else if (function == "SetSelection"_L1) {
int selectionNum = message.arguments().at(0).toInt();
int startOffset = message.arguments().at(1).toInt();
@@ -1866,13 +2091,13 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
interface->textInterface()->setSelection(selectionNum, startOffset, endOffset);
sendReply(connection, message, true);
} else {
- qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::textInterface does not implement " << function << message.path();
+ qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::textInterface does not implement" << function << message.path();
return false;
}
return true;
}
-QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryType(int atspiTextBoundaryType) const
+QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryTypeFromAtspiBoundaryType(int atspiTextBoundaryType)
{
switch (atspiTextBoundaryType) {
case ATSPI_TEXT_BOUNDARY_CHAR:
@@ -1891,6 +2116,38 @@ QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryType(int atspiTex
return QAccessible::CharBoundary;
}
+bool AtSpiAdaptor::isValidAtspiTextGranularity(uint atspiTextGranularity)
+{
+ if (atspiTextGranularity == ATSPI_TEXT_GRANULARITY_CHAR
+ || atspiTextGranularity == ATSPI_TEXT_GRANULARITY_WORD
+ || atspiTextGranularity == ATSPI_TEXT_GRANULARITY_SENTENCE
+ || atspiTextGranularity == ATSPI_TEXT_GRANULARITY_LINE
+ || atspiTextGranularity == ATSPI_TEXT_GRANULARITY_PARAGRAPH)
+ return true;
+
+ qCWarning(lcAccessibilityAtspi) << "Unknown value" << atspiTextGranularity << "for AT-SPI text granularity type";
+ return false;
+}
+
+QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryTypeFromAtspiTextGranularity(uint atspiTextGranularity)
+{
+ Q_ASSERT(isValidAtspiTextGranularity(atspiTextGranularity));
+
+ switch (atspiTextGranularity) {
+ case ATSPI_TEXT_GRANULARITY_CHAR:
+ return QAccessible::CharBoundary;
+ case ATSPI_TEXT_GRANULARITY_WORD:
+ return QAccessible::WordBoundary;
+ case ATSPI_TEXT_GRANULARITY_SENTENCE:
+ return QAccessible::SentenceBoundary;
+ case ATSPI_TEXT_GRANULARITY_LINE:
+ return QAccessible::LineBoundary;
+ case ATSPI_TEXT_GRANULARITY_PARAGRAPH:
+ return QAccessible::ParagraphBoundary;
+ }
+ return QAccessible::CharBoundary;
+}
+
namespace
{
struct AtSpiAttribute {
@@ -1903,13 +2160,13 @@ namespace
QString atspiColor(const QString &ia2Color)
{
// "rgb(%u,%u,%u)" -> "%u,%u,%u"
- return ia2Color.mid(4, ia2Color.length() - (4+1));
+ return ia2Color.mid(4, ia2Color.size() - (4+1)).replace(u"\\,"_s, u","_s);
}
QString atspiSize(const QString &ia2Size)
{
// "%fpt" -> "%f"
- return ia2Size.left(ia2Size.length() - 2);
+ return ia2Size.left(ia2Size.size() - 2);
}
AtSpiAttribute atspiTextAttribute(const QString &ia2Name, const QString &ia2Value)
@@ -1917,9 +2174,9 @@ namespace
QString name = ia2Name;
QString value = ia2Value;
- // IAccessible2: http://www.linuxfoundation.org/collaborate/workgroups/accessibility/iaccessible2/textattributes
- // ATK attribute names: https://git.gnome.org/browse/orca/tree/src/orca/text_attribute_names.py
- // ATK attribute values: https://developer.gnome.org/atk/unstable/AtkText.html#AtkTextAttribute
+ // IAccessible2: https://wiki.linuxfoundation.org/accessibility/iaccessible2/textattributes
+ // ATK attribute names: https://gitlab.gnome.org/GNOME/orca/-/blob/master/src/orca/text_attribute_names.py
+ // ATK attribute values: https://gnome.pages.gitlab.gnome.org/atk/AtkText.html#AtkTextAttribute
// https://bugzilla.gnome.org/show_bug.cgi?id=744553 "ATK docs provide no guidance for allowed values of some text attributes"
// specifically for "weight", "invalid", "language" and value range for colors
@@ -1965,6 +2222,13 @@ namespace
// (on which it produces traceback and fails to read any following text attributes),
// but that is the default value, so omit it anyway
value = QString();
+ } else if (((ia2Name == "text-line-through-style"_L1 || ia2Name == "text-line-through-type"_L1) && (ia2Value != "none"_L1))
+ || (ia2Name == "text-line-through-text"_L1 && !ia2Value.isEmpty())) {
+ // if any of the above is set, set "strikethrough" to true, but don't explicitly set
+ // to false otherwise, since any of the others might still be set to indicate strikethrough
+ // and no strikethrough is assumed anyway when nothing is explicitly set
+ name = QStringLiteral("strikethrough");
+ value = QStringLiteral("true");
} else if (ia2Name == "text-position"_L1) {
name = QStringLiteral("vertical-align");
if (value != "baseline"_L1 && value != "super"_L1 && value != "sub"_L1) {
@@ -2012,11 +2276,13 @@ QVariantList AtSpiAdaptor::getAttributes(QAccessibleInterface *interface, int of
QString joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset);
const QStringList attributes = joined.split(u';', Qt::SkipEmptyParts, Qt::CaseSensitive);
for (const QString &attr : attributes) {
- QStringList items;
- items = attr.split(u':', Qt::SkipEmptyParts, Qt::CaseSensitive);
- AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]);
- if (!attribute.isNull())
- set[attribute.name] = attribute.value;
+ QStringList items = attr.split(u':', Qt::SkipEmptyParts, Qt::CaseSensitive);
+ if (items.count() == 2)
+ {
+ AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]);
+ if (!attribute.isNull())
+ set[attribute.name] = attribute.value;
+ }
}
QVariantList list;
@@ -2025,9 +2291,8 @@ QVariantList AtSpiAdaptor::getAttributes(QAccessibleInterface *interface, int of
return list;
}
-QVariantList AtSpiAdaptor::getAttributeValue(QAccessibleInterface *interface, int offset, const QString &attributeName) const
+QString AtSpiAdaptor::getAttributeValue(QAccessibleInterface *interface, int offset, const QString &attributeName) const
{
- QString mapped;
QString joined;
QSpiAttributeSet map;
int startOffset;
@@ -2042,20 +2307,13 @@ QVariantList AtSpiAdaptor::getAttributeValue(QAccessibleInterface *interface, in
if (!attribute.isNull())
map[attribute.name] = attribute.value;
}
- mapped = map[attributeName];
- const bool defined = !mapped.isEmpty();
- QVariantList list;
- list << mapped << startOffset << endOffset << defined;
- return list;
+ return map[attributeName];
}
QList<QVariant> AtSpiAdaptor::getCharacterExtents(QAccessibleInterface *interface, int offset, uint coordType) const
{
QRect rect = interface->textInterface()->characterRect(offset);
-
- if (coordType == ATSPI_COORD_TYPE_WINDOW)
- rect = translateRectToWindowCoordinates(interface, rect);
-
+ rect = translateFromScreenCoordinates(interface, rect, coordType);
return QList<QVariant>() << rect.x() << rect.y() << rect.width() << rect.height();
}
@@ -2073,22 +2331,52 @@ QList<QVariant> AtSpiAdaptor::getRangeExtents(QAccessibleInterface *interface,
for (int i=startOffset + 1; i <= endOffset; i++)
rect = rect | textInterface->characterRect(i);
- // relative to window
- if (coordType == ATSPI_COORD_TYPE_WINDOW)
- rect = translateRectToWindowCoordinates(interface, rect);
-
+ rect = translateFromScreenCoordinates(interface, rect, coordType);
return QList<QVariant>() << rect.x() << rect.y() << rect.width() << rect.height();
}
-QRect AtSpiAdaptor::translateRectToWindowCoordinates(QAccessibleInterface *interface, const QRect &rect)
+bool AtSpiAdaptor::isValidCoordType(uint coordType)
{
- QAccessibleInterface * window = getWindow(interface);
- if (window)
- return rect.translated(-window->rect().x(), -window->rect().y());
+ if (coordType == ATSPI_COORD_TYPE_SCREEN || coordType == ATSPI_COORD_TYPE_WINDOW || coordType == ATSPI_COORD_TYPE_PARENT)
+ return true;
+
+ qCWarning(lcAccessibilityAtspi) << "Unknown value" << coordType << "for AT-SPI coord type";
+ return false;
+}
+
+QRect AtSpiAdaptor::translateFromScreenCoordinates(QAccessibleInterface *interface, const QRect &screenRect, uint targetCoordType)
+{
+ Q_ASSERT(isValidCoordType(targetCoordType));
+
+ QAccessibleInterface *upper = nullptr;
+ if (targetCoordType == ATSPI_COORD_TYPE_WINDOW)
+ upper = getWindow(interface);
+ else if (targetCoordType == ATSPI_COORD_TYPE_PARENT)
+ upper = interface->parent();
+
+ QRect rect = screenRect;
+ if (upper)
+ rect.translate(-upper->rect().x(), -upper->rect().y());
return rect;
}
+QPoint AtSpiAdaptor::translateToScreenCoordinates(QAccessibleInterface *interface, const QPoint &pos, uint fromCoordType)
+{
+ Q_ASSERT(isValidCoordType(fromCoordType));
+
+ QAccessibleInterface *upper = nullptr;
+ if (fromCoordType == ATSPI_COORD_TYPE_WINDOW)
+ upper = getWindow(interface);
+ else if (fromCoordType == ATSPI_COORD_TYPE_PARENT)
+ upper = interface->parent();
+
+ QPoint screenPos = pos;
+ if (upper)
+ screenPos += upper->rect().topLeft();
+
+ return screenPos;
+}
// Editable Text interface
static QString textForRange(QAccessibleInterface *accessible, int startOffset, int endOffset)
@@ -2100,7 +2388,7 @@ static QString textForRange(QAccessibleInterface *accessible, int startOffset, i
}
QString txt = accessible->text(QAccessible::Value);
if (endOffset == -1)
- endOffset = txt.length();
+ endOffset = txt.size();
return txt.mid(startOffset, endOffset - startOffset);
}
@@ -2108,7 +2396,7 @@ static void replaceTextFallback(QAccessibleInterface *accessible, long startOffs
{
QString t = textForRange(accessible, 0, -1);
if (endOffset == -1)
- endOffset = t.length();
+ endOffset = t.size();
if (endOffset - startOffset == 0)
t.insert(startOffset, txt);
else
@@ -2173,10 +2461,10 @@ bool AtSpiAdaptor::editableTextInterface(QAccessibleInterface *interface, const
else
replaceTextFallback(interface, 0, -1, newContents);
connection.send(message.createReply(true));
- } else if (function == ""_L1) {
+ } else if (function.isEmpty()) {
connection.send(message.createReply());
} else {
- qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::editableTextInterface does not implement " << function << message.path();
+ qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::editableTextInterface does not implement" << function << message.path();
return false;
}
return true;
@@ -2195,7 +2483,7 @@ bool AtSpiAdaptor::valueInterface(QAccessibleInterface *interface, const QString
//Temporary fix
//See https://bugzilla.gnome.org/show_bug.cgi?id=652596
valueIface->setCurrentValue(value);
- connection.send(message.createReply()); // FIXME is the reply needed?
+ connection.send(message.createReply());
} else {
QVariant value;
if (function == "GetCurrentValue"_L1)
@@ -2207,11 +2495,11 @@ bool AtSpiAdaptor::valueInterface(QAccessibleInterface *interface, const QString
else if (function == "GetMinimumValue"_L1)
value = valueIface->minimumValue();
else {
- qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::valueInterface does not implement " << function << message.path();
+ qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::valueInterface does not implement" << function << message.path();
return false;
}
if (!value.canConvert<double>()) {
- qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::valueInterface: Could not convert to double: " << function;
+ qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::valueInterface: Could not convert to double:" << function;
}
// explicitly convert to dbus-variant containing one double since atspi expects that
@@ -2222,24 +2510,79 @@ bool AtSpiAdaptor::valueInterface(QAccessibleInterface *interface, const QString
return true;
}
+// Selection interface
+bool AtSpiAdaptor::selectionInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
+{
+ QAccessibleSelectionInterface* selectionInterface = interface->selectionInterface();
+ if (!selectionInterface) {
+ qCWarning(lcAccessibilityAtspi) << "Could not find selection interface for: " << message.path() << interface;
+ return false;
+ }
+
+ if (function == "ClearSelection"_L1 ) {
+ connection.send(message.createReply(QVariant::fromValue((selectionInterface->clear()))));
+ } else if (function == "DeselectChild"_L1 ) {
+ int childIndex = message.arguments().at(0).toInt();
+ bool ret = false;
+ QAccessibleInterface *child = interface->child(childIndex);
+ if (child)
+ ret = selectionInterface->unselect(child);
+ connection.send(message.createReply(QVariant::fromValue(ret)));
+ } else if (function == "DeselectSelectedChild"_L1 ) {
+ int selectionIndex = message.arguments().at(0).toInt();
+ bool ret = false;
+ QAccessibleInterface *selectedChild = selectionInterface->selectedItem(selectionIndex);
+ if (selectedChild)
+ ret = selectionInterface->unselect(selectedChild);
+ connection.send(message.createReply(QVariant::fromValue(ret)));
+ } else if (function == "GetNSelectedChildren"_L1) {
+ connection.send(message.createReply(QVariant::fromValue(QDBusVariant(
+ QVariant::fromValue(selectionInterface->selectedItemCount())))));
+ } else if (function == "GetSelectedChild"_L1) {
+ int selectionIndex = message.arguments().at(0).toInt();
+ QSpiObjectReference ref(connection, QDBusObjectPath(pathForInterface(selectionInterface->selectedItem(selectionIndex))));
+ connection.send(message.createReply(QVariant::fromValue(ref)));
+ } else if (function == "IsChildSelected"_L1 ) {
+ int childIndex = message.arguments().at(0).toInt();
+ bool ret = false;
+ QAccessibleInterface *child = interface->child(childIndex);
+ if (child)
+ ret = selectionInterface->isSelected(child);
+ connection.send(message.createReply(QVariant::fromValue(ret)));
+ } else if (function == "SelectAll"_L1 ) {
+ connection.send(message.createReply(QVariant::fromValue(selectionInterface->selectAll())));
+ } else if (function == "SelectChild"_L1 ) {
+ int childIndex = message.arguments().at(0).toInt();
+ bool ret = false;
+ QAccessibleInterface *child = interface->child(childIndex);
+ if (child)
+ ret = selectionInterface->select(child);
+ connection.send(message.createReply(QVariant::fromValue(ret)));
+ } else {
+ qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::selectionInterface does not implement " << function << message.path();
+ return false;
+ }
+
+ return true;
+}
+
+
// Table interface
bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
{
if (!(interface->tableInterface() || interface->tableCellInterface())) {
- qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find table interface for: " << message.path() << interface;
+ qCWarning(lcAccessibilityAtspi) << "Qt AtSpiAdaptor: Could not find table interface for:" << message.path() << interface;
return false;
}
- if (0) {
- // properties
- } else if (function == "GetCaption"_L1) {
+ if (function == "GetCaption"_L1) {
QAccessibleInterface * captionInterface= interface->tableInterface()->caption();
if (captionInterface) {
QSpiObjectReference ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(captionInterface)));
- sendReply(connection, message, QVariant::fromValue(ref));
+ sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(ref))));
} else {
- sendReply(connection, message, QVariant::fromValue(
- QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL))));
+ sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(
+ QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL))))));
}
} else if (function == "GetNColumns"_L1) {
connection.send(message.createReply(QVariant::fromValue(QDBusVariant(
@@ -2264,7 +2607,7 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString
(column < 0) ||
(row >= interface->tableInterface()->rowCount()) ||
(column >= interface->tableInterface()->columnCount())) {
- qCDebug(lcAccessibilityAtspi) << "WARNING: invalid index for tableInterface GetAccessibleAt (" << row << ", " << column << ')';
+ qCWarning(lcAccessibilityAtspi) << "Invalid index for tableInterface GetAccessibleAt (" << row << "," << column << ')';
return false;
}
@@ -2273,7 +2616,7 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString
if (cell) {
ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(cell)));
} else {
- qCDebug(lcAccessibilityAtspi) << "WARNING: no cell interface returned for " << interface->object() << row << column;
+ qCWarning(lcAccessibilityAtspi) << "No cell interface returned for" << interface->object() << row << column;
ref = QSpiObjectReference();
}
connection.send(message.createReply(QVariant::fromValue(ref)));
@@ -2283,7 +2626,7 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString
int column = message.arguments().at(1).toInt();
QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column);
if (!cell) {
- qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::GetIndexAt(" << row << ',' << column << ") did not find a cell. " << interface;
+ qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::GetIndexAt(" << row << ',' << column << ") did not find a cell." << interface;
return false;
}
int index = interface->indexOfChild(cell);
@@ -2303,7 +2646,7 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString
ret = -1;
} else {
if (!cell->tableCellInterface()) {
- qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No table cell interface: " << cell;
+ qCWarning(lcAccessibilityAtspi).nospace() << "AtSpiAdaptor::" << function << " No table cell interface: " << cell;
return false;
}
ret = cell->tableCellInterface()->columnIndex();
@@ -2315,14 +2658,14 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString
ret = index % interface->tableInterface()->columnCount();
} else {
if (!cell->tableCellInterface()) {
- qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No table cell interface: " << cell;
+ qCWarning(lcAccessibilityAtspi).nospace() << "AtSpiAdaptor::" << function << " No table cell interface: " << cell;
return false;
}
ret = cell->tableCellInterface()->rowIndex();
}
}
} else {
- qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No cell at index: " << index << interface;
+ qCWarning(lcAccessibilityAtspi).nospace() << "AtSpiAdaptor::" << function << " No cell at index: " << index << " " << interface;
return false;
}
}
@@ -2351,14 +2694,15 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString
if (cols > 0) {
row = index / cols;
col = index % cols;
- QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, col)->tableCellInterface();
- if (cell) {
- row = cell->rowIndex();
- col = cell->columnIndex();
- rowExtents = cell->rowExtent();
- colExtents = cell->columnExtent();
- isSelected = cell->isSelected();
- success = true;
+ if (QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, col)) {
+ if (QAccessibleTableCellInterface *cellIface = cell->tableCellInterface()) {
+ row = cellIface->rowIndex();
+ col = cellIface->columnIndex();
+ rowExtents = cellIface->rowExtent();
+ colExtents = cellIface->columnExtent();
+ isSelected = cellIface->isSelected();
+ success = true;
+ }
}
}
QVariantList list;
@@ -2368,12 +2712,22 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString
} else if (function == "GetColumnExtentAt"_L1) {
int row = message.arguments().at(0).toInt();
int column = message.arguments().at(1).toInt();
- connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->columnExtent()));
+ int columnExtent = 0;
+ if (QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column)) {
+ if (QAccessibleTableCellInterface *cellIface = cell->tableCellInterface())
+ columnExtent = cellIface->columnExtent();
+ }
+ connection.send(message.createReply(columnExtent));
} else if (function == "GetRowExtentAt"_L1) {
int row = message.arguments().at(0).toInt();
int column = message.arguments().at(1).toInt();
- connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->rowExtent()));
+ int rowExtent = 0;
+ if (QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column)) {
+ if (QAccessibleTableCellInterface *cellIface = cell->tableCellInterface())
+ rowExtent = cellIface->rowExtent();
+ }
+ connection.send(message.createReply(rowExtent));
} else if (function == "GetColumnHeader"_L1) {
int column = message.arguments().at(0).toInt();
@@ -2391,9 +2745,9 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString
} else if (function == "GetRowHeader"_L1) {
int row = message.arguments().at(0).toInt();
QSpiObjectReference ref;
- QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, 0)->tableCellInterface();
- if (cell) {
- QList<QAccessibleInterface*> header = cell->rowHeaderCells();
+ QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, 0);
+ if (cell && cell->tableCellInterface()) {
+ QList<QAccessibleInterface*> header = cell->tableCellInterface()->rowHeaderCells();
if (header.size() > 0) {
ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(header.takeAt(0))));
}
@@ -2413,8 +2767,12 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString
} else if (function == "IsSelected"_L1) {
int row = message.arguments().at(0).toInt();
int column = message.arguments().at(1).toInt();
- QAccessibleTableCellInterface* cell = interface->tableInterface()->cellAt(row, column)->tableCellInterface();
- connection.send(message.createReply(cell->isSelected()));
+ bool selected = false;
+ if (QAccessibleInterface* cell = interface->tableInterface()->cellAt(row, column)) {
+ if (QAccessibleTableCellInterface *cellIface = cell->tableCellInterface())
+ selected = cellIface->isSelected();
+ }
+ connection.send(message.createReply(selected));
} else if (function == "AddColumnSelection"_L1) {
int column = message.arguments().at(0).toInt();
connection.send(message.createReply(interface->tableInterface()->selectColumn(column)));
@@ -2428,9 +2786,67 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString
int row = message.arguments().at(0).toInt();
connection.send(message.createReply(interface->tableInterface()->unselectRow(row)));
} else {
- qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::tableInterface does not implement " << function << message.path();
+ qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::tableInterface does not implement" << function << message.path();
+ return false;
+ }
+ return true;
+}
+
+// Table cell interface
+bool AtSpiAdaptor::tableCellInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
+{
+ QAccessibleTableCellInterface* cellInterface = interface->tableCellInterface();
+ if (!cellInterface) {
+ qCWarning(lcAccessibilityAtspi) << "Could not find table cell interface for: " << message.path() << interface;
+ return false;
+ }
+
+ if (function == "GetColumnHeaderCells"_L1) {
+ QSpiObjectReferenceArray headerCells;
+ const auto headerCellInterfaces = cellInterface->columnHeaderCells();
+ headerCells.reserve(headerCellInterfaces.size());
+ for (QAccessibleInterface *cell : headerCellInterfaces) {
+ const QString childPath = pathForInterface(cell);
+ const QSpiObjectReference ref(connection, QDBusObjectPath(childPath));
+ headerCells << ref;
+ }
+ connection.send(message.createReply(QVariant::fromValue(headerCells)));
+ } else if (function == "GetColumnSpan"_L1) {
+ connection.send(message.createReply(QVariant::fromValue(QDBusVariant(
+ QVariant::fromValue(cellInterface->columnExtent())))));
+ } else if (function == "GetPosition"_L1) {
+ const int row = cellInterface->rowIndex();
+ const int column = cellInterface->columnIndex();
+ connection.send(message.createReply(QVariant::fromValue(QDBusVariant(
+ QVariant::fromValue(QPoint(row, column))))));
+ } else if (function == "GetRowHeaderCells"_L1) {
+ QSpiObjectReferenceArray headerCells;
+ const auto headerCellInterfaces = cellInterface->rowHeaderCells();
+ headerCells.reserve(headerCellInterfaces.size());
+ for (QAccessibleInterface *cell : headerCellInterfaces) {
+ const QString childPath = pathForInterface(cell);
+ const QSpiObjectReference ref(connection, QDBusObjectPath(childPath));
+ headerCells << ref;
+ }
+ connection.send(message.createReply(QVariant::fromValue(headerCells)));
+ } else if (function == "GetRowSpan"_L1) {
+ connection.send(message.createReply(QVariant::fromValue(QDBusVariant(
+ QVariant::fromValue(cellInterface->rowExtent())))));
+ } else if (function == "GetRowColumnSpan"_L1) {
+ QVariantList list;
+ list << cellInterface->rowIndex() << cellInterface->columnIndex() << cellInterface->rowExtent() << cellInterface->columnExtent();
+ connection.send(message.createReply(list));
+ } else if (function == "GetTable"_L1) {
+ QSpiObjectReference ref;
+ QAccessibleInterface* table = cellInterface->table();
+ if (table && table->tableInterface())
+ ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(table)));
+ connection.send(message.createReply(QVariant::fromValue(QDBusVariant(QVariant::fromValue(ref)))));
+ } else {
+ qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::tableCellInterface does not implement" << function << message.path();
return false;
}
+
return true;
}
diff --git a/src/gui/accessible/linux/atspiadaptor_p.h b/src/gui/accessible/linux/atspiadaptor_p.h
index 3d785e4c25..3a890f3d7d 100644
--- a/src/gui/accessible/linux/atspiadaptor_p.h
+++ b/src/gui/accessible/linux/atspiadaptor_p.h
@@ -19,7 +19,6 @@
#include <atspi/atspi-constants.h>
#include <QtGui/private/qtguiglobal_p.h>
-#include <QtCore/qsharedpointer.h>
#include <QtDBus/qdbusvirtualobject.h>
#include <QtGui/qaccessible.h>
@@ -31,7 +30,6 @@ QT_REQUIRE_CONFIG(accessibility);
QT_BEGIN_NAMESPACE
class QAccessibleInterface;
-class QSpiAccessibleInterface;
class QSpiApplicationAdaptor;
@@ -48,8 +46,6 @@ public:
bool handleMessage(const QDBusMessage &message, const QDBusConnection &connection) override;
void notify(QAccessibleEvent *event);
- void init();
- void checkInitializedAndEnabled();
public Q_SLOTS:
void eventListenerRegistered(const QString &bus, const QString &path);
void eventListenerDeregistered(const QString &bus, const QString &path);
@@ -77,7 +73,9 @@ private:
bool textInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
bool editableTextInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
bool valueInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
+ bool selectionInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
bool tableInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
+ bool tableCellInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
void sendReply(const QDBusConnection &connection, const QDBusMessage &message, const QVariant &argument) const;
@@ -94,17 +92,21 @@ private:
// component helper functions
static QRect getExtents(QAccessibleInterface *interface, uint coordType);
- static QRect translateRectToWindowCoordinates(QAccessibleInterface *interface, const QRect &rect);
+ static bool isValidCoordType(uint coordType);
+ static QRect translateFromScreenCoordinates(QAccessibleInterface *interface, const QRect &rect, uint targetCoordType);
+ static QPoint translateToScreenCoordinates(QAccessibleInterface *interface, const QPoint &pos, uint fromCoordType);
// action helper functions
QSpiActionArray getActions(QAccessibleInterface *interface) const;
// text helper functions
QVariantList getAttributes(QAccessibleInterface *, int offset, bool includeDefaults) const;
- QVariantList getAttributeValue(QAccessibleInterface *, int offset, const QString &attributeName) const;
+ QString getAttributeValue(QAccessibleInterface *, int offset, const QString &attributeName) const;
QList<QVariant> getCharacterExtents(QAccessibleInterface *, int offset, uint coordType) const;
QList<QVariant> getRangeExtents(QAccessibleInterface *, int startOffset, int endOffset, uint coordType) const;
- QAccessible::TextBoundaryType qAccessibleBoundaryType(int atspiTextBoundaryType) const;
+ static QAccessible::TextBoundaryType qAccessibleBoundaryTypeFromAtspiBoundaryType(int atspiTextBoundaryType);
+ static bool isValidAtspiTextGranularity(uint coordType);
+ static QAccessible::TextBoundaryType qAccessibleBoundaryTypeFromAtspiTextGranularity(uint atspiTextGranularity);
static bool inheritsQAction(QObject *object);
// private vars
diff --git a/src/gui/accessible/linux/dbusconnection.cpp b/src/gui/accessible/linux/dbusconnection.cpp
index b4a8643474..10bd10927e 100644
--- a/src/gui/accessible/linux/dbusconnection.cpp
+++ b/src/gui/accessible/linux/dbusconnection.cpp
@@ -56,11 +56,13 @@ DBusConnection::DBusConnection(QObject *parent)
if (c.interface()->isServiceRegistered(A11Y_SERVICE))
serviceRegistered();
- // In addition try if there is an xatom exposing the bus address, this allows applications run as root to work
- QString address = getAddressFromXCB();
- if (!address.isEmpty()) {
- m_enabled = true;
- connectA11yBus(address);
+ if (QGuiApplication::platformName().startsWith("xcb"_L1)) {
+ // In addition try if there is an xatom exposing the bus address, this allows applications run as root to work
+ QString address = getAddressFromXCB();
+ if (!address.isEmpty()) {
+ m_enabled = true;
+ connectA11yBus(address);
+ }
}
}
diff --git a/src/gui/accessible/linux/dbusxml/Socket.xml b/src/gui/accessible/linux/dbusxml/Socket.xml
index 75ec99f994..f9ac76d2c8 100644
--- a/src/gui/accessible/linux/dbusxml/Socket.xml
+++ b/src/gui/accessible/linux/dbusxml/Socket.xml
@@ -17,7 +17,7 @@
<signal name="Available">
<arg direction="in" name="socket" type="(so)"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QSpiObjectReference"/>
- </method>
+ </signal>
</interface>
</node>
diff --git a/src/gui/accessible/linux/qspi_constant_mappings.cpp b/src/gui/accessible/linux/qspi_constant_mappings.cpp
index b3e8816df5..e5b6e3f634 100644
--- a/src/gui/accessible/linux/qspi_constant_mappings.cpp
+++ b/src/gui/accessible/linux/qspi_constant_mappings.cpp
@@ -36,6 +36,8 @@ quint64 spiStatesFromQState(QAccessible::State state)
setSpiStateBit(&spiState, ATSPI_STATE_FOCUSED);
if (state.pressed)
setSpiStateBit(&spiState, ATSPI_STATE_PRESSED);
+ if (state.checkable)
+ setSpiStateBit(&spiState, ATSPI_STATE_CHECKABLE);
if (state.checked)
setSpiStateBit(&spiState, ATSPI_STATE_CHECKED);
if (state.checkStateMixed)
@@ -75,7 +77,8 @@ quint64 spiStatesFromQState(QAccessible::State state)
if (state.extSelectable)
setSpiStateBit(&spiState, ATSPI_STATE_SELECTABLE);
// if (state.Protected)
- // if (state.HasPopup)
+ if (state.hasPopup)
+ setSpiStateBit(&spiState, ATSPI_STATE_HAS_POPUP);
if (state.modal)
setSpiStateBit(&spiState, ATSPI_STATE_MODAL);
if (state.multiLine)
@@ -97,6 +100,7 @@ QSpiUIntList spiStateSetFromSpiStates(quint64 states)
AtspiRelationType qAccessibleRelationToAtSpiRelation(QAccessible::Relation relation)
{
+ // direction of the relation is "inversed" in Qt and AT-SPI
switch (relation) {
case QAccessible::Label:
return ATSPI_RELATION_LABELLED_BY;
@@ -106,6 +110,14 @@ AtspiRelationType qAccessibleRelationToAtSpiRelation(QAccessible::Relation relat
return ATSPI_RELATION_CONTROLLED_BY;
case QAccessible::Controlled:
return ATSPI_RELATION_CONTROLLER_FOR;
+ case QAccessible::DescriptionFor:
+ return ATSPI_RELATION_DESCRIBED_BY;
+ case QAccessible::Described:
+ return ATSPI_RELATION_DESCRIPTION_FOR;
+ case QAccessible::FlowsFrom:
+ return ATSPI_RELATION_FLOWS_TO;
+ case QAccessible::FlowsTo:
+ return ATSPI_RELATION_FLOWS_FROM;
default:
qWarning() << "Cannot return AT-SPI relation for:" << relation;
}
diff --git a/src/gui/accessible/linux/qspiaccessiblebridge.cpp b/src/gui/accessible/linux/qspiaccessiblebridge.cpp
index 8961055f1b..de2e7d5fc0 100644
--- a/src/gui/accessible/linux/qspiaccessiblebridge.cpp
+++ b/src/gui/accessible/linux/qspiaccessiblebridge.cpp
@@ -33,6 +33,14 @@ QSpiAccessibleBridge::QSpiAccessibleBridge()
{
dbusConnection = new DBusConnection();
connect(dbusConnection, SIGNAL(enabledChanged(bool)), this, SLOT(enabledChanged(bool)));
+ // Now that we have connected the signal, make sure we didn't miss a change,
+ // e.g. when running as root or when AT_SPI_BUS_ADDRESS is set by hand.
+ // But do that only on next loop, once dbus is really settled.
+ QTimer::singleShot(
+ 0, this, [this]{
+ if (dbusConnection->isEnabled() && dbusConnection->connection().isConnected())
+ enabledChanged(true);
+ });
}
void QSpiAccessibleBridge::enabledChanged(bool enabled)
@@ -197,7 +205,11 @@ static RoleMapping map[] = {
//: Role of an accessible object
{ QAccessible::ButtonDropDown, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button with drop down") },
//: Role of an accessible object
+#if ATSPI_ROLE_COUNT > 130
+ { QAccessible::ButtonMenu, ATSPI_ROLE_PUSH_BUTTON_MENU, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button menu") },
+#else
{ QAccessible::ButtonMenu, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button menu") },
+#endif
//: Role of an accessible object - a button that expands a grid.
{ QAccessible::ButtonDropGrid, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button with drop down grid") },
//: Role of an accessible object - blank space between other objects.
diff --git a/src/gui/accessible/linux/qspiapplicationadaptor.cpp b/src/gui/accessible/linux/qspiapplicationadaptor.cpp
index 1cac4598ca..37d7648984 100644
--- a/src/gui/accessible/linux/qspiapplicationadaptor.cpp
+++ b/src/gui/accessible/linux/qspiapplicationadaptor.cpp
@@ -12,12 +12,16 @@
#include "deviceeventcontroller_adaptor.h"
#include "atspi/atspi-constants.h"
+#if __has_include(<xcb/xproto.h>)
#include <xcb/xproto.h>
+#endif
//#define KEYBOARD_DEBUG
QT_BEGIN_NAMESPACE
+using namespace Qt::Literals::StringLiterals;
+
/*!
\class QSpiApplicationAdaptor
\internal
@@ -125,9 +129,13 @@ bool QSpiApplicationAdaptor::eventFilter(QObject *target, QEvent *event)
de.modifiers = 0;
if ((keyEvent->modifiers() & Qt::ShiftModifier) && (keyEvent->key() != Qt::Key_Shift))
de.modifiers |= 1 << ATSPI_MODIFIER_SHIFT;
- // TODO rather introduce Qt::CapslockModifier into KeyboardModifier
- if (keyEvent->nativeModifiers() & XCB_MOD_MASK_LOCK )
- de.modifiers |= 1 << ATSPI_MODIFIER_SHIFTLOCK;
+#ifdef XCB_MOD_MASK_LOCK
+ if (QGuiApplication::platformName().startsWith("xcb"_L1)) {
+ // TODO rather introduce Qt::CapslockModifier into KeyboardModifier
+ if (keyEvent->nativeModifiers() & XCB_MOD_MASK_LOCK )
+ de.modifiers |= 1 << ATSPI_MODIFIER_SHIFTLOCK;
+ }
+#endif
if ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->key() != Qt::Key_Control))
de.modifiers |= 1 << ATSPI_MODIFIER_CONTROL;
if ((keyEvent->modifiers() & Qt::AltModifier) && (keyEvent->key() != Qt::Key_Alt))
@@ -173,11 +181,11 @@ QKeyEvent* QSpiApplicationAdaptor::copyKeyEvent(QKeyEvent* old)
void QSpiApplicationAdaptor::notifyKeyboardListenerCallback(const QDBusMessage& message)
{
- if (!keyEvents.length()) {
+ if (!keyEvents.size()) {
qWarning("QSpiApplication::notifyKeyboardListenerCallback with no queued key called");
return;
}
- Q_ASSERT(message.arguments().length() == 1);
+ Q_ASSERT(message.arguments().size() == 1);
if (message.arguments().at(0).toBool() == true) {
QPair<QPointer<QObject>, QKeyEvent*> event = keyEvents.dequeue();
delete event.second;
diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp
index 286d27b15b..46bca16dad 100644
--- a/src/gui/accessible/qaccessible.cpp
+++ b/src/gui/accessible/qaccessible.cpp
@@ -218,7 +218,7 @@ Q_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core");
\value ObjectHide An object is hidden; for example, with QWidget::hide().
Any children the object that is hidden has do not send
this event. It is not sent when an object is hidden as
- it is being obcured by others.
+ it is being obscured by others.
\value ObjectReorder A layout or item view has added, removed, or moved an
object (Qt does not use this event).
\value ObjectShow An object is displayed; for example, with
@@ -354,14 +354,29 @@ Q_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core");
\enum QAccessible::RelationFlag
This enum type defines bit flags that can be combined to indicate
- the relationship between two accessible objects.
-
- \value Label The first object is the label of the second object.
- \value Labelled The first object is labelled by the second object.
- \value Controller The first object controls the second object.
- \value Controlled The first object is controlled by the second object.
- \value AllRelations Used as a mask to specify that we are interesting in information
- about all relations
+ the relationship between two accessible objects. It is used by
+ the relations() function, which returns a list of all the related
+ interfaces of the calling object, together with the relations
+ for each object.
+
+ Each entry in the list is a QPair where the \c second member stores
+ the relation type(s) between the \c returned object represented by the
+ \c first member and the \c origin (the caller) interface/object.
+
+ In the table below, the \c returned object refers to the object in
+ the returned list, and the \c origin object is the one represented
+ by the calling interface.
+
+ \value Label The \c returned object is the label for the \c origin object.
+ \value Labelled The \c returned object is labelled by the \c origin object.
+ \value Controller The \c returned object controls the \c origin object.
+ \value Controlled The \c returned object is controlled by the \c origin object.
+ \value [since 6.6] DescriptionFor The \c returned object provides a description for the \c origin object.
+ \value [since 6.6] Described The \c returned object is described by the \c origin object.
+ \value [since 6.6] FlowsFrom Content logically flows from the \c returned object to the \c origin object.
+ \value [since 6.6] FlowsTo Content logically flows to the \c returned object from the \c origin object.
+ \value AllRelations Used as a mask to specify that we are interesting in information
+ about all relations
Implementations of relations() return a combination of these flags.
Some values are mutually exclusive.
@@ -397,6 +412,43 @@ Q_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core");
\sa QAccessibleTextInterface
*/
+/*! \enum QAccessible::Attribute
+ This enum describes different types of attributes used by the
+ \l QAccessibleAttributesInterface.
+ \since 6.8
+
+ These attributes are comparable to the concept of properties/(object)
+ attributes found in ARIA, AT-SPI2, IAccessible, UIA and NSAccessibility
+ and are mapped to their platform counterpart where applicable.
+
+ Each attribute is handled as a key-value pair, with the values of this
+ enumeration being used as keys.
+
+ Attribute values are represented in a \l QVariant. The type of the value
+ stored in the \l QVariant is fixed and specified below for each of the
+ attribute types.
+
+ \value Custom value type: \a QHash<QString, QString>
+ The \a Custom attribute is special in that
+ it can effectively represent multiple attributes at
+ once, since it itself is a \l QHash used to represent
+ key-value pairs.
+ For platforms supporting custom key-value pairs for
+ attributes, those set in the \a Custom attribute
+ are bridged to the platform layer without applying any
+ translation to platform-specific attributes. In general,
+ the other, more strongly typed attributes should be used.
+ This attribute can e.g. be used for prototyping
+ before officially adding an official new enumeration value
+ for a specific feature.
+ \value Level value type: \a int
+ Defines the hierarchical level of an element within a structure,
+ e.g. the heading level of a heading. This attribute conceptually
+ matches the "aria-level" property in ARIA.
+
+ \sa QAccessibleAttributesInterface
+*/
+
/*!
\enum QAccessible::InterfaceType
@@ -415,8 +467,10 @@ Q_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core");
\value TableInterface For lists, tables and trees.
\value TableCellInterface For cells in a TableInterface object.
\value HyperlinkInterface For hyperlink nodes (usually embedded as children of text nodes)
+ \value [since 6.5] SelectionInterface For non-text objects that support selection of child objects.
+ \value [since 6.8] AttributesInterface For objects that support object-specific attributes.
- \sa QAccessibleInterface::interface_cast(), QAccessibleTextInterface, QAccessibleValueInterface, QAccessibleActionInterface, QAccessibleTableInterface, QAccessibleTableCellInterface
+ \sa QAccessibleInterface::interface_cast(), QAccessibleTextInterface, QAccessibleValueInterface, QAccessibleActionInterface, QAccessibleTableInterface, QAccessibleTableCellInterface, QAccessibleSelectionInterface, QAccessibleAttributesInterface
*/
#if QT_CONFIG(accessibility)
@@ -436,7 +490,7 @@ QAccessibleInterface::~QAccessibleInterface()
/* accessible widgets plugin discovery stuff */
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, acLoader,
(QAccessibleFactoryInterface_iid, "/accessible"_L1))
typedef QHash<QString, QAccessiblePlugin*> QAccessiblePluginsHash;
Q_GLOBAL_STATIC(QAccessiblePluginsHash, qAccessiblePlugins)
@@ -670,7 +724,7 @@ QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object)
const QString cn = QLatin1StringView(mo->className());
// Check if the class has a InterfaceFactory installed.
- for (int i = qAccessibleFactories()->count(); i > 0; --i) {
+ for (int i = qAccessibleFactories()->size(); i > 0; --i) {
InterfaceFactory factory = qAccessibleFactories()->at(i - 1);
if (QAccessibleInterface *iface = factory(cn, object)) {
QAccessibleCache::instance()->insert(object, iface);
@@ -682,9 +736,9 @@ QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object)
// no entry in the cache try to create it using the plugin loader.
if (!qAccessiblePlugins()->contains(cn)) {
QAccessiblePlugin *factory = nullptr; // 0 means "no plugin found". This is cached as well.
- const int index = loader()->indexOf(cn);
+ const int index = acLoader()->indexOf(cn);
if (index != -1)
- factory = qobject_cast<QAccessiblePlugin *>(loader()->instance(index));
+ factory = qobject_cast<QAccessiblePlugin *>(acLoader()->instance(index));
qAccessiblePlugins()->insert(cn, factory);
}
@@ -787,7 +841,7 @@ bool QAccessible::isActive()
*/
void QAccessible::setActive(bool active)
{
- for (int i = 0; i < qAccessibleActivationObservers()->count() ;++i)
+ for (int i = 0; i < qAccessibleActivationObservers()->size() ;++i)
qAccessibleActivationObservers()->at(i)->accessibilityActiveChanged(active);
}
@@ -850,11 +904,11 @@ void QAccessible::updateAccessibility(QAccessibleEvent *event)
if (iface->tableInterface())
iface->tableInterface()->modelChange(static_cast<QAccessibleTableModelChangeEvent*>(event));
}
+ }
- if (updateHandler) {
- updateHandler(event);
- return;
- }
+ if (updateHandler) {
+ updateHandler(event);
+ return;
}
if (QPlatformAccessibility *pfAccessibility = platformAccessibility())
@@ -1273,6 +1327,11 @@ QColor QAccessibleInterface::backgroundColor() const
*/
/*!
+ \fn QAccessibleSelectionInterface *QAccessibleInterface::selectionInterface()
+ \since 6.5
+*/
+
+/*!
\class QAccessibleEvent
\ingroup accessibility
\inmodule QtGui
@@ -1634,8 +1693,8 @@ QAccessibleTextRemoveEvent::~QAccessibleTextRemoveEvent()
/*!
\fn QAccessibleTextInsertEvent::QAccessibleTextInsertEvent(QAccessibleInterface *iface, int position, const QString &text)
- Constructs a new QAccessibleTextInsertEvent event for \a iface. The text has been inserted at
- \a position.
+ Constructs a new QAccessibleTextInsertEvent event for \a iface. The \a text has been inserted
+ at \a position.
*/
/*!
@@ -1828,13 +1887,13 @@ Q_GUI_EXPORT QDebug operator<<(QDebug d, const QAccessibleInterface *iface)
QStringList stateStrings;
QAccessible::State st = iface->state();
if (st.focusable)
- stateStrings << "focusable"_L1;
+ stateStrings << u"focusable"_s;
if (st.focused)
- stateStrings << "focused"_L1;
+ stateStrings << u"focused"_s;
if (st.selected)
- stateStrings << "selected"_L1;
+ stateStrings << u"selected"_s;
if (st.invisible)
- stateStrings << "invisible"_L1;
+ stateStrings << u"invisible"_s;
if (!stateStrings.isEmpty())
d << stateStrings.join(u'|');
@@ -2008,7 +2067,7 @@ static QString textLineBoundary(int beforeAtAfter, const QString &text, int offs
{
Q_ASSERT(beforeAtAfter >= -1 && beforeAtAfter <= 1);
Q_ASSERT(*startOffset == -1 && *endOffset == -1);
- int length = text.length();
+ int length = text.size();
Q_ASSERT(offset >= 0 && offset <= length);
// move offset into the right range (if asking for line before or after
@@ -2057,10 +2116,10 @@ QString QAccessibleTextInterface::textBeforeOffset(int offset, QAccessible::Text
const QString txt = text(0, characterCount());
if (offset == -1)
- offset = txt.length();
+ offset = txt.size();
*startOffset = *endOffset = -1;
- if (txt.isEmpty() || offset <= 0 || offset > txt.length())
+ if (txt.isEmpty() || offset <= 0 || offset > txt.size())
return QString();
// type initialized just to silence a compiler warning [-Werror=maybe-uninitialized]
@@ -2131,10 +2190,10 @@ QString QAccessibleTextInterface::textAfterOffset(int offset, QAccessible::TextB
const QString txt = text(0, characterCount());
if (offset == -1)
- offset = txt.length();
+ offset = txt.size();
*startOffset = *endOffset = -1;
- if (txt.isEmpty() || offset < 0 || offset >= txt.length())
+ if (txt.isEmpty() || offset < 0 || offset >= txt.size())
return QString();
// type initialized just to silence a compiler warning [-Werror=maybe-uninitialized]
@@ -2169,20 +2228,20 @@ QString QAccessibleTextInterface::textAfterOffset(int offset, QAccessible::TextB
int toNext = boundary.toNextBoundary();
if ((boundary.boundaryReasons() & (QTextBoundaryFinder::StartOfItem | QTextBoundaryFinder::EndOfItem)))
break;
- if (toNext < 0 || toNext >= txt.length())
+ if (toNext < 0 || toNext >= txt.size())
break; // not found, the boundary might not exist
}
- Q_ASSERT(boundary.position() <= txt.length());
+ Q_ASSERT(boundary.position() <= txt.size());
*startOffset = boundary.position();
while (true) {
int toNext = boundary.toNextBoundary();
if ((boundary.boundaryReasons() & (QTextBoundaryFinder::StartOfItem | QTextBoundaryFinder::EndOfItem)))
break;
- if (toNext < 0 || toNext >= txt.length())
+ if (toNext < 0 || toNext >= txt.size())
break; // not found, the boundary might not exist
}
- Q_ASSERT(boundary.position() <= txt.length());
+ Q_ASSERT(boundary.position() <= txt.size());
*endOffset = boundary.position();
if ((*startOffset == -1) || (*endOffset == -1) || (*startOffset == *endOffset)) {
@@ -2216,13 +2275,13 @@ QString QAccessibleTextInterface::textAtOffset(int offset, QAccessible::TextBoun
const QString txt = text(0, characterCount());
if (offset == -1)
- offset = txt.length();
+ offset = txt.size();
*startOffset = *endOffset = -1;
- if (txt.isEmpty() || offset < 0 || offset > txt.length())
+ if (txt.isEmpty() || offset < 0 || offset > txt.size())
return QString();
- if (offset == txt.length() && boundaryType == QAccessible::CharBoundary)
+ if (offset == txt.size() && boundaryType == QAccessible::CharBoundary)
return QString();
// type initialized just to silence a compiler warning [-Werror=maybe-uninitialized]
@@ -2243,7 +2302,7 @@ QString QAccessibleTextInterface::textAtOffset(int offset, QAccessible::TextBoun
return textLineBoundary(0, txt, offset, startOffset, endOffset);
case QAccessible::NoBoundary:
*startOffset = 0;
- *endOffset = txt.length();
+ *endOffset = txt.size();
return txt;
default:
Q_UNREACHABLE();
@@ -2261,11 +2320,11 @@ QString QAccessibleTextInterface::textAtOffset(int offset, QAccessible::TextBoun
Q_ASSERT(boundary.position() >= 0);
*startOffset = boundary.position();
- while (boundary.toNextBoundary() < txt.length()) {
+ while (boundary.toNextBoundary() < txt.size()) {
if ((boundary.boundaryReasons() & (QTextBoundaryFinder::StartOfItem | QTextBoundaryFinder::EndOfItem)))
break;
}
- Q_ASSERT(boundary.position() <= txt.length());
+ Q_ASSERT(boundary.position() <= txt.size());
*endOffset = boundary.position();
return txt.mid(*startOffset, *endOffset - *startOffset);
@@ -2588,7 +2647,7 @@ QAccessibleTableInterface::~QAccessibleTableInterface()
/*!
\fn virtual QList<int> QAccessibleTableInterface::selectedRows() const
- Returns the list of currently selected columns.
+ Returns the list of currently selected rows.
*/
/*!
@@ -2941,6 +3000,161 @@ QString QAccessibleActionInterface::nextPageAction()
return accessibleActionStrings()->nextPageAction;
}
+
+/*!
+ \since 6.5
+ \class QAccessibleSelectionInterface
+ \inmodule QtGui
+ \ingroup accessibility
+
+ \brief The QAccessibleSelectionInterface class implements support for
+ selection handling.
+
+ It provides methods for both, retrieving the current selection
+ as well as modifying the selection.
+
+ Only selections of direct children are supported.
+*/
+
+/*!
+
+ Destroys the QAccessibleSelectionInterface.
+*/
+QAccessibleSelectionInterface::~QAccessibleSelectionInterface()
+{
+}
+
+/*!
+ \fn virtual int QAccessibleSelectionInterface::selectedItemCount() const
+
+ Returns the total number of selected accessible items.
+*/
+
+/*!
+ \fn virtual QList<QAccessibleInterface *> QAccessibleSelectionInterface::selectedItems() const
+
+ Returns the list of selected accessible items.
+*/
+
+/*!
+ Returns the selected accessible item at index \a selectionIndex in the selection.
+
+ Note that the index refers to the n-th selected accessible item (i.e. the index in the current selection),
+ which generally differs from the index that would be passed to \l QAccessibleInterface::child()
+ in order to retrieve the same item.
+
+ The default implementation uses \a selectionIndex to retrieve the item from the list
+ of selected items retrieved by \l QAccessibleSelectionInterface::selectedItems().
+
+ In particular for implementations dealing with many selected items, reimplementing
+ this method in a more efficient way may be desirable for performance reasons.
+*/
+QAccessibleInterface* QAccessibleSelectionInterface::selectedItem(int selectionIndex) const
+{
+ QList<QAccessibleInterface*> items = selectedItems();
+ if (selectionIndex < 0 || selectionIndex > items.length() -1) {
+ qCWarning(lcAccessibilityCore) << "Selection index" << selectionIndex << "out of range.";
+ return nullptr;
+ }
+
+ return items.at(selectionIndex);
+}
+
+/*!
+ Returns whether \a childItem is part of the current selection.
+
+ The default implementation checks whether \a childItem is contained
+ in the list of items retrieved by \l QAccessibleSelectionInterface::selectedItems.
+*/
+bool QAccessibleSelectionInterface::isSelected(QAccessibleInterface *childItem) const
+{
+ return selectedItems().contains(childItem);
+}
+
+/*!
+ \fn virtual bool QAccessibleSelectionInterface::select(QAccessibleInterface *childItem)
+
+ Adds \a childItem to the selection.
+ Returns whether \a childItem has actually been added to the selection.
+
+ For implementations that only allow single selections,
+ this may replace the current selection.
+*/
+
+/*!
+ \fn virtual bool QAccessibleSelectionInterface::unselect(QAccessibleInterface *childItem)
+
+ Removes \a childItem from the selection.
+
+ Returns whether the accessible item has actually been removed from the selection.
+*/
+
+/*!
+ \fn virtual bool QAccessibleSelectionInterface::selectAll()
+
+ Selects all accessible child items.
+
+ Returns whether all accessible child items have actually been added to the selection.
+*/
+
+/*!
+ \fn virtual bool QAccessibleSelectionInterface::clear()
+
+ Unselects all accessible child items.
+
+ Returns whether all accessible child items have actually been removed from the selection,
+ i.e. whether the selection is empty after this method has been called.
+*/
+
+
+/*!
+ \since 6.8
+ \class QAccessibleAttributesInterface
+ \inmodule QtGui
+ \ingroup accessibility
+
+ \brief The QAccessibleAttributesInterface class implements support for
+ reporting attributes for an accessible object.
+
+ Attributes are key-value pairs. Values are stored in \l QVariant.
+
+ The \a QAccessible::Attributes enumeration describes the available keys and
+ documents which type to use for the value of each key.
+
+ While the text-specific attributes handled by \l QAccessibleTextInterface::attributes
+ are specific to objects implementing text and are specific to a specific text
+ position/offset, the attributes handled by the \l QAccessibleAttributesInterface
+ can be used for objects of any role and apply for the whole object.
+
+ Classes already implementing \l QAccessibleTextInterface for text-specific attrtibutes
+ may want to implement \l QAccessibleAttributesInterface in addition for object-specific
+ attributes.
+*/
+
+/*!
+
+ Destroys the QAccessibleAttributesInterface.
+*/
+QAccessibleAttributesInterface::~QAccessibleAttributesInterface()
+{
+}
+
+/*!
+ \fn QList<QAccessible::Attribute> QAccessibleAttributesInterface::attributeKeys() const
+
+ Returns the keys of all attributes the object supports. The \l QAccessible::Attribute
+ enumeration describes available keys.
+*/
+
+/*!
+ \fn QVariant QAccessibleAttributesInterface::attributeValue(QAccessible::Attribute key) const
+
+ Returns the value of the attribute \a key of this object.
+
+ If the specificed attribute is not set for this object, an invalid
+ \l QVariant is returned.
+*/
+
/*! \internal */
QString qAccessibleLocalizedActionDescription(const QString &actionName)
{
diff --git a/src/gui/accessible/qaccessible.h b/src/gui/accessible/qaccessible.h
index 2700b5c080..0a92e76c73 100644
--- a/src/gui/accessible/qaccessible.h
+++ b/src/gui/accessible/qaccessible.h
@@ -42,6 +42,8 @@ class QAccessibleImageInterface;
class QAccessibleTableInterface;
class QAccessibleTableCellInterface;
class QAccessibleHyperlinkInterface;
+class QAccessibleSelectionInterface;
+class QAccessibleAttributesInterface;
class QAccessibleTableModelChangeEvent;
class Q_GUI_EXPORT QAccessibleInterface
@@ -102,6 +104,12 @@ public:
inline QAccessibleHyperlinkInterface *hyperlinkInterface()
{ return reinterpret_cast<QAccessibleHyperlinkInterface *>(interface_cast(QAccessible::HyperlinkInterface)); }
+ inline QAccessibleSelectionInterface *selectionInterface()
+ { return reinterpret_cast<QAccessibleSelectionInterface *>(interface_cast(QAccessible::SelectionInterface)); }
+
+ inline QAccessibleAttributesInterface *attributesInterface()
+ { return reinterpret_cast<QAccessibleAttributesInterface *>(interface_cast(QAccessible::AttributesInterface)); }
+
virtual void virtual_hook(int id, void *data);
virtual void *interface_cast(QAccessible::InterfaceType)
@@ -265,6 +273,30 @@ public:
virtual bool isValid() const = 0;
};
+class Q_GUI_EXPORT QAccessibleSelectionInterface
+{
+public:
+ virtual ~QAccessibleSelectionInterface();
+
+ virtual int selectedItemCount() const = 0;
+ virtual QList<QAccessibleInterface*> selectedItems() const = 0;
+ virtual QAccessibleInterface* selectedItem(int selectionIndex) const;
+ virtual bool isSelected(QAccessibleInterface *childItem) const;
+ virtual bool select(QAccessibleInterface *childItem) = 0;
+ virtual bool unselect(QAccessibleInterface *childItem) = 0;
+ virtual bool selectAll() = 0;
+ virtual bool clear() = 0;
+};
+
+class Q_GUI_EXPORT QAccessibleAttributesInterface
+{
+public:
+ virtual ~QAccessibleAttributesInterface();
+ virtual QList<QAccessible::Attribute> attributeKeys() const = 0;
+ virtual QVariant attributeValue(QAccessible::Attribute key) const = 0;
+};
+
+
class Q_GUI_EXPORT QAccessibleEvent
{
Q_DISABLE_COPY(QAccessibleEvent)
@@ -287,7 +319,7 @@ public:
}
inline QAccessibleEvent(QAccessibleInterface *iface, QAccessible::Event typ)
- : m_type(typ), m_object(nullptr)
+ : m_type(typ)
{
Q_ASSERT(iface);
Q_ASSERT(m_type != QAccessible::ValueChanged);
@@ -299,6 +331,7 @@ public:
Q_ASSERT(m_type != QAccessible::TextUpdated);
Q_ASSERT(m_type != QAccessible::TableModelChanged);
m_uniqueId = QAccessible::uniqueId(iface);
+ m_object = iface->object();
}
virtual ~QAccessibleEvent();
@@ -408,13 +441,13 @@ class Q_GUI_EXPORT QAccessibleTextInsertEvent : public QAccessibleTextCursorEven
{
public:
inline QAccessibleTextInsertEvent(QObject *obj, int position, const QString &text)
- : QAccessibleTextCursorEvent(obj, position + int(text.length()))
+ : QAccessibleTextCursorEvent(obj, position + int(text.size()))
, m_position(position), m_text(text)
{
m_type = QAccessible::TextInserted;
}
inline QAccessibleTextInsertEvent(QAccessibleInterface *iface, int position, const QString &text)
- : QAccessibleTextCursorEvent(iface, position + int(text.length()))
+ : QAccessibleTextCursorEvent(iface, position + int(text.size()))
, m_position(position), m_text(text)
{
m_type = QAccessible::TextInserted;
@@ -468,13 +501,13 @@ class Q_GUI_EXPORT QAccessibleTextUpdateEvent : public QAccessibleTextCursorEven
{
public:
inline QAccessibleTextUpdateEvent(QObject *obj, int position, const QString &oldText, const QString &text)
- : QAccessibleTextCursorEvent(obj, position + int(text.length()))
+ : QAccessibleTextCursorEvent(obj, position + int(text.size()))
, m_position(position), m_oldText(oldText), m_text(text)
{
m_type = QAccessible::TextUpdated;
}
inline QAccessibleTextUpdateEvent(QAccessibleInterface *iface, int position, const QString &oldText, const QString &text)
- : QAccessibleTextCursorEvent(iface, position + int(text.length()))
+ : QAccessibleTextCursorEvent(iface, position + int(text.size()))
, m_position(position), m_oldText(oldText), m_text(text)
{
m_type = QAccessible::TextUpdated;
@@ -572,7 +605,7 @@ protected:
int m_lastColumn;
};
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
#define QAccessibleInterface_iid "org.qt-project.Qt.QAccessibleInterface"
Q_DECLARE_INTERFACE(QAccessibleInterface, QAccessibleInterface_iid)
#endif
diff --git a/src/gui/accessible/qaccessible_base.h b/src/gui/accessible/qaccessible_base.h
index cf5a65143b..2d2b1de316 100644
--- a/src/gui/accessible/qaccessible_base.h
+++ b/src/gui/accessible/qaccessible_base.h
@@ -331,6 +331,10 @@ public:
Labelled = 0x00000002,
Controller = 0x00000004,
Controlled = 0x00000008,
+ DescriptionFor = 0x00000010,
+ Described = 0x00000020,
+ FlowsFrom = 0x00000040,
+ FlowsTo = 0x00000080,
AllRelations = 0xffffffff
};
Q_DECLARE_FLAGS(Relation, RelationFlag)
@@ -344,7 +348,9 @@ public:
ImageInterface,
TableInterface,
TableCellInterface,
- HyperlinkInterface
+ HyperlinkInterface,
+ SelectionInterface,
+ AttributesInterface,
};
enum TextBoundaryType {
@@ -356,6 +362,11 @@ public:
NoBoundary
};
+ enum class Attribute {
+ Custom,
+ Level,
+ };
+
typedef QAccessibleInterface*(*InterfaceFactory)(const QString &key, QObject*);
typedef void(*UpdateHandler)(QAccessibleEvent *event);
typedef void(*RootObjectHandler)(QObject*);
diff --git a/src/gui/accessible/qaccessiblecache.cpp b/src/gui/accessible/qaccessiblecache.cpp
index d9586519c7..b41a2481f9 100644
--- a/src/gui/accessible/qaccessiblecache.cpp
+++ b/src/gui/accessible/qaccessiblecache.cpp
@@ -48,18 +48,18 @@ QAccessibleCache *QAccessibleCache::instance()
QAccessible::Id QAccessibleCache::acquireId() const
{
static const QAccessible::Id FirstId = QAccessible::Id(INT_MAX) + 1;
- static QAccessible::Id lastUsedId = FirstId;
+ static QAccessible::Id nextId = FirstId;
- while (idToInterface.contains(lastUsedId)) {
+ while (idToInterface.contains(nextId)) {
// (wrap back when when we reach UINT_MAX - 1)
// -1 because on Android -1 is taken for the "View" so just avoid it completely for consistency
- if (lastUsedId == UINT_MAX - 1)
- lastUsedId = FirstId;
+ if (nextId == UINT_MAX - 1)
+ nextId = FirstId;
else
- ++lastUsedId;
+ ++nextId;
}
- return lastUsedId;
+ return nextId++;
}
QAccessibleInterface *QAccessibleCache::interfaceForId(QAccessible::Id id) const
@@ -130,7 +130,7 @@ void QAccessibleCache::objectDestroyed(QObject* obj)
/*
In some cases we might add a not fully-constructed object to the cache. This might happen with
for instance QWidget subclasses that are in the construction phase. If updateAccessibility() is
- called in the constructor of QWidget (directly or indirectly), it it will end up asking for the
+ called in the constructor of QWidget (directly or indirectly), it will end up asking for the
classname of that widget in order to know which accessibility interface subclass the
accessibility factory should instantiate and return. However, since that requires a virtual
call to metaObject(), it will return the metaObject() of QWidget (not for the subclass), and so
diff --git a/src/gui/accessible/qaccessibleobject.cpp b/src/gui/accessible/qaccessibleobject.cpp
index af9d3aa0fc..61d3a90632 100644
--- a/src/gui/accessible/qaccessibleobject.cpp
+++ b/src/gui/accessible/qaccessibleobject.cpp
@@ -124,7 +124,7 @@ static QObjectList topLevelObjects()
{
QObjectList list;
const QWindowList tlw(QGuiApplication::topLevelWindows());
- for (int i = 0; i < tlw.count(); ++i) {
+ for (int i = 0; i < tlw.size(); ++i) {
QWindow *w = tlw.at(i);
if (w->type() != Qt::Popup && w->type() != Qt::Desktop) {
if (QAccessibleInterface *root = w->accessibleRoot()) {
@@ -140,7 +140,7 @@ static QObjectList topLevelObjects()
/*! \reimp */
int QAccessibleApplication::childCount() const
{
- return topLevelObjects().count();
+ return topLevelObjects().size();
}
/*! \reimp */
@@ -160,7 +160,7 @@ QAccessibleInterface *QAccessibleApplication::parent() const
QAccessibleInterface *QAccessibleApplication::child(int index) const
{
const QObjectList tlo(topLevelObjects());
- if (index >= 0 && index < tlo.count())
+ if (index >= 0 && index < tlo.size())
return QAccessible::queryAccessibleInterface(tlo.at(index));
return nullptr;
}
diff --git a/src/gui/accessible/qplatformaccessibility.cpp b/src/gui/accessible/qplatformaccessibility.cpp
index 92936c74e6..ae7635ff7c 100644
--- a/src/gui/accessible/qplatformaccessibility.cpp
+++ b/src/gui/accessible/qplatformaccessibility.cpp
@@ -50,7 +50,7 @@ void QPlatformAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
if (!bridges() || bridges()->isEmpty())
return;
- for (int i = 0; i < bridges()->count(); ++i)
+ for (int i = 0; i < bridges()->size(); ++i)
bridges()->at(i)->notifyAccessibilityUpdate(event);
}
@@ -63,7 +63,7 @@ void QPlatformAccessibility::setRootObject(QObject *o)
if (!o)
return;
- for (int i = 0; i < bridges()->count(); ++i) {
+ for (int i = 0; i < bridges()->size(); ++i) {
QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(o);
bridges()->at(i)->setRootObject(iface);
}
diff --git a/src/gui/accessible/windows/apisupport/qwindowsuiawrapper.cpp b/src/gui/accessible/windows/apisupport/qwindowsuiawrapper.cpp
deleted file mode 100644
index d9ff723a61..0000000000
--- a/src/gui/accessible/windows/apisupport/qwindowsuiawrapper.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include <initguid.h>
-
-#include "qwindowsuiawrapper_p.h"
-#include <QtCore/private/qsystemlibrary_p.h>
-
-QT_BEGIN_NAMESPACE
-
-// private constructor
-QWindowsUiaWrapper::QWindowsUiaWrapper()
-{
- QSystemLibrary uiaLib(QStringLiteral("UIAutomationCore"));
- if (uiaLib.load()) {
- m_pUiaReturnRawElementProvider = reinterpret_cast<PtrUiaReturnRawElementProvider>(uiaLib.resolve("UiaReturnRawElementProvider"));
- m_pUiaHostProviderFromHwnd = reinterpret_cast<PtrUiaHostProviderFromHwnd>(uiaLib.resolve("UiaHostProviderFromHwnd"));
- m_pUiaRaiseAutomationPropertyChangedEvent = reinterpret_cast<PtrUiaRaiseAutomationPropertyChangedEvent>(uiaLib.resolve("UiaRaiseAutomationPropertyChangedEvent"));
- m_pUiaRaiseAutomationEvent = reinterpret_cast<PtrUiaRaiseAutomationEvent>(uiaLib.resolve("UiaRaiseAutomationEvent"));
- m_pUiaRaiseNotificationEvent = reinterpret_cast<PtrUiaRaiseNotificationEvent>(uiaLib.resolve("UiaRaiseNotificationEvent"));
- m_pUiaClientsAreListening = reinterpret_cast<PtrUiaClientsAreListening>(uiaLib.resolve("UiaClientsAreListening"));
- }
-}
-
-QWindowsUiaWrapper::~QWindowsUiaWrapper()
-{
-}
-
-// shared instance
-QWindowsUiaWrapper *QWindowsUiaWrapper::instance()
-{
- static QWindowsUiaWrapper wrapper;
- return &wrapper;
-}
-
-// True if most symbols resolved (UiaRaiseNotificationEvent is optional).
-BOOL QWindowsUiaWrapper::ready()
-{
- return m_pUiaReturnRawElementProvider
- && m_pUiaHostProviderFromHwnd
- && m_pUiaRaiseAutomationPropertyChangedEvent
- && m_pUiaRaiseAutomationEvent
- && m_pUiaClientsAreListening;
-}
-
-BOOL QWindowsUiaWrapper::clientsAreListening()
-{
- if (!m_pUiaClientsAreListening)
- return FALSE;
- return m_pUiaClientsAreListening();
-}
-
-LRESULT QWindowsUiaWrapper::returnRawElementProvider(HWND hwnd, WPARAM wParam, LPARAM lParam, IRawElementProviderSimple *el)
-{
- if (!m_pUiaReturnRawElementProvider)
- return static_cast<LRESULT>(NULL);
- return m_pUiaReturnRawElementProvider(hwnd, wParam, lParam, el);
-}
-
-HRESULT QWindowsUiaWrapper::hostProviderFromHwnd(HWND hwnd, IRawElementProviderSimple **ppProvider)
-{
- if (!m_pUiaHostProviderFromHwnd)
- return UIA_E_NOTSUPPORTED;
- return m_pUiaHostProviderFromHwnd(hwnd, ppProvider);
-}
-
-HRESULT QWindowsUiaWrapper::raiseAutomationPropertyChangedEvent(IRawElementProviderSimple *pProvider, PROPERTYID id, VARIANT oldValue, VARIANT newValue)
-{
- if (!m_pUiaRaiseAutomationPropertyChangedEvent)
- return UIA_E_NOTSUPPORTED;
- return m_pUiaRaiseAutomationPropertyChangedEvent(pProvider, id, oldValue, newValue);
-}
-
-HRESULT QWindowsUiaWrapper::raiseAutomationEvent(IRawElementProviderSimple *pProvider, EVENTID id)
-{
- if (!m_pUiaRaiseAutomationEvent)
- return UIA_E_NOTSUPPORTED;
- return m_pUiaRaiseAutomationEvent(pProvider, id);
-}
-
-HRESULT QWindowsUiaWrapper::raiseNotificationEvent(IRawElementProviderSimple *provider, NotificationKind notificationKind, NotificationProcessing notificationProcessing, BSTR displayString, BSTR activityId)
-{
- if (!m_pUiaRaiseNotificationEvent)
- return UIA_E_NOTSUPPORTED;
- return m_pUiaRaiseNotificationEvent(provider, notificationKind, notificationProcessing, displayString, activityId);
-}
-
-QT_END_NAMESPACE
-
diff --git a/src/gui/accessible/windows/apisupport/qwindowsuiawrapper_p.h b/src/gui/accessible/windows/apisupport/qwindowsuiawrapper_p.h
deleted file mode 100644
index 05b93f8393..0000000000
--- a/src/gui/accessible/windows/apisupport/qwindowsuiawrapper_p.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef QWINDOWSUIAWRAPPER_H
-#define QWINDOWSUIAWRAPPER_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of other Qt classes. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtGui/private/qtguiglobal_p.h>
-
-#include "uiatypes_p.h"
-#include "uiaattributeids_p.h"
-#include "uiacontroltypeids_p.h"
-#include "uiaerrorids_p.h"
-#include "uiaeventids_p.h"
-#include "uiageneralids_p.h"
-#include "uiapatternids_p.h"
-#include "uiapropertyids_p.h"
-#include "uiaserverinterfaces_p.h"
-#include "uiaclientinterfaces_p.h"
-
-QT_REQUIRE_CONFIG(accessibility);
-
-QT_BEGIN_NAMESPACE
-
-class Q_GUI_EXPORT QWindowsUiaWrapper
-{
- QWindowsUiaWrapper();
- virtual ~QWindowsUiaWrapper();
-public:
- static QWindowsUiaWrapper *instance();
- BOOL ready();
- BOOL clientsAreListening();
- LRESULT returnRawElementProvider(HWND hwnd, WPARAM wParam, LPARAM lParam, IRawElementProviderSimple *el);
- HRESULT hostProviderFromHwnd(HWND hwnd, IRawElementProviderSimple **ppProvider);
- HRESULT raiseAutomationPropertyChangedEvent(IRawElementProviderSimple *pProvider, PROPERTYID id, VARIANT oldValue, VARIANT newValue);
- HRESULT raiseAutomationEvent(IRawElementProviderSimple *pProvider, EVENTID id);
- HRESULT raiseNotificationEvent(IRawElementProviderSimple *provider, NotificationKind notificationKind, NotificationProcessing notificationProcessing, BSTR displayString, BSTR activityId);
-
-private:
- typedef LRESULT (WINAPI *PtrUiaReturnRawElementProvider)(HWND, WPARAM, LPARAM, IRawElementProviderSimple *);
- typedef HRESULT (WINAPI *PtrUiaHostProviderFromHwnd)(HWND, IRawElementProviderSimple **);
- typedef HRESULT (WINAPI *PtrUiaRaiseAutomationPropertyChangedEvent)(IRawElementProviderSimple *, PROPERTYID, VARIANT, VARIANT);
- typedef HRESULT (WINAPI *PtrUiaRaiseAutomationEvent)(IRawElementProviderSimple *, EVENTID);
- typedef HRESULT (WINAPI *PtrUiaRaiseNotificationEvent)(IRawElementProviderSimple *, NotificationKind, NotificationProcessing, BSTR, BSTR);
- typedef BOOL (WINAPI *PtrUiaClientsAreListening)();
- PtrUiaReturnRawElementProvider m_pUiaReturnRawElementProvider = nullptr;
- PtrUiaHostProviderFromHwnd m_pUiaHostProviderFromHwnd = nullptr;
- PtrUiaRaiseAutomationPropertyChangedEvent m_pUiaRaiseAutomationPropertyChangedEvent = nullptr;
- PtrUiaRaiseAutomationEvent m_pUiaRaiseAutomationEvent = nullptr;
- PtrUiaRaiseNotificationEvent m_pUiaRaiseNotificationEvent = nullptr;
- PtrUiaClientsAreListening m_pUiaClientsAreListening = nullptr;
-};
-
-QT_END_NAMESPACE
-
-#endif //QWINDOWSUIAWRAPPER_H
-
diff --git a/src/gui/accessible/windows/apisupport/uiaattributeids_p.h b/src/gui/accessible/windows/apisupport/uiaattributeids_p.h
deleted file mode 100644
index 2078351a98..0000000000
--- a/src/gui/accessible/windows/apisupport/uiaattributeids_p.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef UIAATTRIBUTEIDS_H
-#define UIAATTRIBUTEIDS_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.
-//
-
-#define UIA_AnimationStyleAttributeId 40000
-#define UIA_BackgroundColorAttributeId 40001
-#define UIA_BulletStyleAttributeId 40002
-#define UIA_CapStyleAttributeId 40003
-#define UIA_CultureAttributeId 40004
-#define UIA_FontNameAttributeId 40005
-#define UIA_FontSizeAttributeId 40006
-#define UIA_FontWeightAttributeId 40007
-#define UIA_ForegroundColorAttributeId 40008
-#define UIA_HorizontalTextAlignmentAttributeId 40009
-#define UIA_IndentationFirstLineAttributeId 40010
-#define UIA_IndentationLeadingAttributeId 40011
-#define UIA_IndentationTrailingAttributeId 40012
-#define UIA_IsHiddenAttributeId 40013
-#define UIA_IsItalicAttributeId 40014
-#define UIA_IsReadOnlyAttributeId 40015
-#define UIA_IsSubscriptAttributeId 40016
-#define UIA_IsSuperscriptAttributeId 40017
-#define UIA_MarginBottomAttributeId 40018
-#define UIA_MarginLeadingAttributeId 40019
-#define UIA_MarginTopAttributeId 40020
-#define UIA_MarginTrailingAttributeId 40021
-#define UIA_OutlineStylesAttributeId 40022
-#define UIA_OverlineColorAttributeId 40023
-#define UIA_OverlineStyleAttributeId 40024
-#define UIA_StrikethroughColorAttributeId 40025
-#define UIA_StrikethroughStyleAttributeId 40026
-#define UIA_TabsAttributeId 40027
-#define UIA_TextFlowDirectionsAttributeId 40028
-#define UIA_UnderlineColorAttributeId 40029
-#define UIA_UnderlineStyleAttributeId 40030
-#define UIA_AnnotationTypesAttributeId 40031
-#define UIA_AnnotationObjectsAttributeId 40032
-#define UIA_StyleNameAttributeId 40033
-#define UIA_StyleIdAttributeId 40034
-#define UIA_LinkAttributeId 40035
-#define UIA_IsActiveAttributeId 40036
-#define UIA_SelectionActiveEndAttributeId 40037
-#define UIA_CaretPositionAttributeId 40038
-#define UIA_CaretBidiModeAttributeId 40039
-#define UIA_LineSpacingAttributeId 40040
-#define UIA_BeforeParagraphSpacingAttributeId 40041
-#define UIA_AfterParagraphSpacingAttributeId 40042
-#define UIA_SayAsInterpretAsAttributeId 40043
-
-#endif
diff --git a/src/gui/accessible/windows/apisupport/uiaclientinterfaces_p.h b/src/gui/accessible/windows/apisupport/uiaclientinterfaces_p.h
deleted file mode 100644
index fb74042bfa..0000000000
--- a/src/gui/accessible/windows/apisupport/uiaclientinterfaces_p.h
+++ /dev/null
@@ -1,230 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef UIACLIENTINTERFACES_H
-#define UIACLIENTINTERFACES_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 <unknwn.h>
-
-#ifndef __IUIAutomationElement_INTERFACE_DEFINED__
-
-struct IUIAutomationCondition;
-struct IUIAutomationCacheRequest;
-struct IUIAutomationElementArray;
-struct IUIAutomationTreeWalker;
-struct IUIAutomationEventHandler;
-struct IUIAutomationPropertyChangedEventHandler;
-struct IUIAutomationStructureChangedEventHandler;
-struct IUIAutomationFocusChangedEventHandler;
-struct IUIAutomationProxyFactory;
-struct IUIAutomationProxyFactoryEntry;
-struct IUIAutomationProxyFactoryMapping;
-#ifndef __IAccessible_FWD_DEFINED__
-#define __IAccessible_FWD_DEFINED__
-struct IAccessible;
-#endif /* __IAccessible_FWD_DEFINED__ */
-
-#define __IUIAutomationElement_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IUIAutomationElement, 0xd22108aa, 0x8ac5, 0x49a5, 0x83,0x7b, 0x37,0xbb,0xb3,0xd7,0x59,0x1e);
-MIDL_INTERFACE("d22108aa-8ac5-49a5-837b-37bbb3d7591e")
-IUIAutomationElement : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE SetFocus() = 0;
- virtual HRESULT STDMETHODCALLTYPE GetRuntimeId(__RPC__deref_out_opt SAFEARRAY **runtimeId) = 0;
- virtual HRESULT STDMETHODCALLTYPE FindFirst(enum TreeScope scope, __RPC__in_opt IUIAutomationCondition *condition, __RPC__deref_out_opt IUIAutomationElement **found) = 0;
- virtual HRESULT STDMETHODCALLTYPE FindAll(enum TreeScope scope, __RPC__in_opt IUIAutomationCondition *condition, __RPC__deref_out_opt IUIAutomationElementArray **found) = 0;
- virtual HRESULT STDMETHODCALLTYPE FindFirstBuildCache(enum TreeScope scope, __RPC__in_opt IUIAutomationCondition *condition, __RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__deref_out_opt IUIAutomationElement **found) = 0;
- virtual HRESULT STDMETHODCALLTYPE FindAllBuildCache(enum TreeScope scope, __RPC__in_opt IUIAutomationCondition *condition, __RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__deref_out_opt IUIAutomationElementArray **found) = 0;
- virtual HRESULT STDMETHODCALLTYPE BuildUpdatedCache(__RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__deref_out_opt IUIAutomationElement **updatedElement) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetCurrentPropertyValue(PROPERTYID propertyId, __RPC__out VARIANT *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetCurrentPropertyValueEx(PROPERTYID propertyId, BOOL ignoreDefaultValue, __RPC__out VARIANT *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetCachedPropertyValue(PROPERTYID propertyId, __RPC__out VARIANT *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetCachedPropertyValueEx(PROPERTYID propertyId, BOOL ignoreDefaultValue, __RPC__out VARIANT *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetCurrentPatternAs(PATTERNID patternId, __RPC__in REFIID riid, __RPC__deref_out_opt void **patternObject) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetCachedPatternAs(PATTERNID patternId, __RPC__in REFIID riid, __RPC__deref_out_opt void **patternObject) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetCurrentPattern(PATTERNID patternId, __RPC__deref_out_opt IUnknown **patternObject) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetCachedPattern(PATTERNID patternId, __RPC__deref_out_opt IUnknown **patternObject) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetCachedParent(__RPC__deref_out_opt IUIAutomationElement **parent) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetCachedChildren(__RPC__deref_out_opt IUIAutomationElementArray **children) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentProcessId(__RPC__out int *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentControlType(__RPC__out CONTROLTYPEID *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentLocalizedControlType(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentName(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentAcceleratorKey(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentAccessKey(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentHasKeyboardFocus(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentIsKeyboardFocusable(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentIsEnabled(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentAutomationId(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentClassName(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentHelpText(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentCulture(__RPC__out int *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentIsControlElement(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentIsContentElement(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentIsPassword(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentNativeWindowHandle(__RPC__deref_out_opt UIA_HWND *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentItemType(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentIsOffscreen(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentOrientation(__RPC__out enum OrientationType *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentFrameworkId(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentIsRequiredForForm(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentItemStatus(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentBoundingRectangle(__RPC__out RECT *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentLabeledBy(__RPC__deref_out_opt IUIAutomationElement **retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentAriaRole(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentAriaProperties(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentIsDataValidForForm(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentControllerFor(__RPC__deref_out_opt IUIAutomationElementArray **retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentDescribedBy(__RPC__deref_out_opt IUIAutomationElementArray **retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentFlowsTo(__RPC__deref_out_opt IUIAutomationElementArray **retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CurrentProviderDescription(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedProcessId(__RPC__out int *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedControlType(__RPC__out CONTROLTYPEID *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedLocalizedControlType(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedName(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedAcceleratorKey(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedAccessKey(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedHasKeyboardFocus(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedIsKeyboardFocusable(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedIsEnabled(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedAutomationId(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedClassName(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedHelpText(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedCulture(__RPC__out int *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedIsControlElement(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedIsContentElement(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedIsPassword(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedNativeWindowHandle(__RPC__deref_out_opt UIA_HWND *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedItemType(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedIsOffscreen(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedOrientation(__RPC__out enum OrientationType *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedFrameworkId(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedIsRequiredForForm(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedItemStatus(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedBoundingRectangle(__RPC__out RECT *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedLabeledBy(__RPC__deref_out_opt IUIAutomationElement **retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedAriaRole(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedAriaProperties(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedIsDataValidForForm(__RPC__out BOOL *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedControllerFor(__RPC__deref_out_opt IUIAutomationElementArray **retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedDescribedBy(__RPC__deref_out_opt IUIAutomationElementArray **retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedFlowsTo(__RPC__deref_out_opt IUIAutomationElementArray **retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CachedProviderDescription(__RPC__deref_out_opt BSTR *retVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetClickablePoint(__RPC__out POINT *clickable, __RPC__out BOOL *gotClickable) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IUIAutomationElement, 0xd22108aa, 0x8ac5, 0x49a5, 0x83,0x7b, 0x37,0xbb,0xb3,0xd7,0x59,0x1e)
-#endif
-#endif
-
-
-#ifndef __IUIAutomation_INTERFACE_DEFINED__
-#define __IUIAutomation_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IUIAutomation, 0x30cbe57d, 0xd9d0, 0x452a, 0xab,0x13, 0x7a,0xc5,0xac,0x48,0x25,0xee);
-MIDL_INTERFACE("30cbe57d-d9d0-452a-ab13-7ac5ac4825ee")
-IUIAutomation : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE CompareElements(__RPC__in_opt IUIAutomationElement *el1, __RPC__in_opt IUIAutomationElement *el2, __RPC__out BOOL *areSame) = 0;
- virtual HRESULT STDMETHODCALLTYPE CompareRuntimeIds(__RPC__in SAFEARRAY * runtimeId1, __RPC__in SAFEARRAY * runtimeId2, __RPC__out BOOL *areSame) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetRootElement(__RPC__deref_out_opt IUIAutomationElement **root) = 0;
- virtual HRESULT STDMETHODCALLTYPE ElementFromHandle(__RPC__in UIA_HWND hwnd, __RPC__deref_out_opt IUIAutomationElement **element) = 0;
- virtual HRESULT STDMETHODCALLTYPE ElementFromPoint(POINT pt, __RPC__deref_out_opt IUIAutomationElement **element) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetFocusedElement(__RPC__deref_out_opt IUIAutomationElement **element) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetRootElementBuildCache(__RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__deref_out_opt IUIAutomationElement **root) = 0;
- virtual HRESULT STDMETHODCALLTYPE ElementFromHandleBuildCache(__RPC__in UIA_HWND hwnd, __RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__deref_out_opt IUIAutomationElement **element) = 0;
- virtual HRESULT STDMETHODCALLTYPE ElementFromPointBuildCache(POINT pt, __RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__deref_out_opt IUIAutomationElement **element) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetFocusedElementBuildCache(__RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__deref_out_opt IUIAutomationElement **element) = 0;
- virtual HRESULT STDMETHODCALLTYPE CreateTreeWalker(__RPC__in_opt IUIAutomationCondition *pCondition, __RPC__deref_out_opt IUIAutomationTreeWalker **walker) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_ControlViewWalker(__RPC__deref_out_opt IUIAutomationTreeWalker **walker) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_ContentViewWalker(__RPC__deref_out_opt IUIAutomationTreeWalker **walker) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_RawViewWalker(__RPC__deref_out_opt IUIAutomationTreeWalker **walker) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_RawViewCondition(__RPC__deref_out_opt IUIAutomationCondition **condition) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_ControlViewCondition(__RPC__deref_out_opt IUIAutomationCondition **condition) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_ContentViewCondition(__RPC__deref_out_opt IUIAutomationCondition **condition) = 0;
- virtual HRESULT STDMETHODCALLTYPE CreateCacheRequest(__RPC__deref_out_opt IUIAutomationCacheRequest **cacheRequest) = 0;
- virtual HRESULT STDMETHODCALLTYPE CreateTrueCondition(__RPC__deref_out_opt IUIAutomationCondition **newCondition) = 0;
- virtual HRESULT STDMETHODCALLTYPE CreateFalseCondition(__RPC__deref_out_opt IUIAutomationCondition **newCondition) = 0;
- virtual HRESULT STDMETHODCALLTYPE CreatePropertyCondition(PROPERTYID propertyId, VARIANT value, __RPC__deref_out_opt IUIAutomationCondition **newCondition) = 0;
- virtual HRESULT STDMETHODCALLTYPE CreatePropertyConditionEx(PROPERTYID propertyId, VARIANT value, enum PropertyConditionFlags flags, __RPC__deref_out_opt IUIAutomationCondition **newCondition) = 0;
- virtual HRESULT STDMETHODCALLTYPE CreateAndCondition(__RPC__in_opt IUIAutomationCondition *condition1, __RPC__in_opt IUIAutomationCondition *condition2, __RPC__deref_out_opt IUIAutomationCondition **newCondition) = 0;
- virtual HRESULT STDMETHODCALLTYPE CreateAndConditionFromArray(__RPC__in_opt SAFEARRAY * conditions, __RPC__deref_out_opt IUIAutomationCondition **newCondition) = 0;
- virtual HRESULT STDMETHODCALLTYPE CreateAndConditionFromNativeArray(__RPC__in_ecount_full(conditionCount) IUIAutomationCondition **conditions, int conditionCount, __RPC__deref_out_opt IUIAutomationCondition **newCondition) = 0;
- virtual HRESULT STDMETHODCALLTYPE CreateOrCondition(__RPC__in_opt IUIAutomationCondition *condition1, __RPC__in_opt IUIAutomationCondition *condition2, __RPC__deref_out_opt IUIAutomationCondition **newCondition) = 0;
- virtual HRESULT STDMETHODCALLTYPE CreateOrConditionFromArray(__RPC__in_opt SAFEARRAY * conditions, __RPC__deref_out_opt IUIAutomationCondition **newCondition) = 0;
- virtual HRESULT STDMETHODCALLTYPE CreateOrConditionFromNativeArray(__RPC__in_ecount_full(conditionCount) IUIAutomationCondition **conditions, int conditionCount, __RPC__deref_out_opt IUIAutomationCondition **newCondition) = 0;
- virtual HRESULT STDMETHODCALLTYPE CreateNotCondition(__RPC__in_opt IUIAutomationCondition *condition, __RPC__deref_out_opt IUIAutomationCondition **newCondition) = 0;
- virtual HRESULT STDMETHODCALLTYPE AddAutomationEventHandler(EVENTID eventId, __RPC__in_opt IUIAutomationElement *element, enum TreeScope scope, __RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__in_opt IUIAutomationEventHandler *handler) = 0;
- virtual HRESULT STDMETHODCALLTYPE RemoveAutomationEventHandler(EVENTID eventId, __RPC__in_opt IUIAutomationElement *element, __RPC__in_opt IUIAutomationEventHandler *handler) = 0;
- virtual HRESULT STDMETHODCALLTYPE AddPropertyChangedEventHandlerNativeArray(__RPC__in_opt IUIAutomationElement *element, enum TreeScope scope, __RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__in_opt IUIAutomationPropertyChangedEventHandler *handler, __RPC__in_ecount_full(propertyCount) PROPERTYID *propertyArray, int propertyCount) = 0;
- virtual HRESULT STDMETHODCALLTYPE AddPropertyChangedEventHandler(__RPC__in_opt IUIAutomationElement *element, enum TreeScope scope, __RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__in_opt IUIAutomationPropertyChangedEventHandler *handler, __RPC__in SAFEARRAY * propertyArray) = 0;
- virtual HRESULT STDMETHODCALLTYPE RemovePropertyChangedEventHandler(__RPC__in_opt IUIAutomationElement *element, __RPC__in_opt IUIAutomationPropertyChangedEventHandler *handler) = 0;
- virtual HRESULT STDMETHODCALLTYPE AddStructureChangedEventHandler(__RPC__in_opt IUIAutomationElement *element, enum TreeScope scope, __RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__in_opt IUIAutomationStructureChangedEventHandler *handler) = 0;
- virtual HRESULT STDMETHODCALLTYPE RemoveStructureChangedEventHandler(__RPC__in_opt IUIAutomationElement *element, __RPC__in_opt IUIAutomationStructureChangedEventHandler *handler) = 0;
- virtual HRESULT STDMETHODCALLTYPE AddFocusChangedEventHandler(__RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__in_opt IUIAutomationFocusChangedEventHandler *handler) = 0;
- virtual HRESULT STDMETHODCALLTYPE RemoveFocusChangedEventHandler(__RPC__in_opt IUIAutomationFocusChangedEventHandler *handler) = 0;
- virtual HRESULT STDMETHODCALLTYPE RemoveAllEventHandlers() = 0;
- virtual HRESULT STDMETHODCALLTYPE IntNativeArrayToSafeArray(__RPC__in_ecount_full(arrayCount) int *array, int arrayCount, __RPC__deref_out_opt SAFEARRAY **safeArray) = 0;
- virtual HRESULT STDMETHODCALLTYPE IntSafeArrayToNativeArray(__RPC__in SAFEARRAY * intArray, __RPC__deref_out_ecount_full_opt(*arrayCount) int **array, __RPC__out int *arrayCount) = 0;
- virtual HRESULT STDMETHODCALLTYPE RectToVariant(RECT rc, __RPC__out VARIANT *var) = 0;
- virtual HRESULT STDMETHODCALLTYPE VariantToRect(VARIANT var, __RPC__out RECT *rc) = 0;
- virtual HRESULT STDMETHODCALLTYPE SafeArrayToRectNativeArray(__RPC__in SAFEARRAY * rects, __RPC__deref_out_ecount_full_opt(*rectArrayCount) RECT **rectArray, __RPC__out int *rectArrayCount) = 0;
- virtual HRESULT STDMETHODCALLTYPE CreateProxyFactoryEntry(__RPC__in_opt IUIAutomationProxyFactory *factory, __RPC__deref_out_opt IUIAutomationProxyFactoryEntry **factoryEntry) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_ProxyFactoryMapping(__RPC__deref_out_opt IUIAutomationProxyFactoryMapping **factoryMapping) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetPropertyProgrammaticName(PROPERTYID property, __RPC__deref_out_opt BSTR *name) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetPatternProgrammaticName(PATTERNID pattern, __RPC__deref_out_opt BSTR *name) = 0;
- virtual HRESULT STDMETHODCALLTYPE PollForPotentialSupportedPatterns(__RPC__in_opt IUIAutomationElement *pElement, __RPC__deref_out_opt SAFEARRAY **patternIds, __RPC__deref_out_opt SAFEARRAY **patternNames) = 0;
- virtual HRESULT STDMETHODCALLTYPE PollForPotentialSupportedProperties(__RPC__in_opt IUIAutomationElement *pElement, __RPC__deref_out_opt SAFEARRAY **propertyIds, __RPC__deref_out_opt SAFEARRAY **propertyNames) = 0;
- virtual HRESULT STDMETHODCALLTYPE CheckNotSupported(VARIANT value, __RPC__out BOOL *isNotSupported) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_ReservedNotSupportedValue(__RPC__deref_out_opt IUnknown **notSupportedValue) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_ReservedMixedAttributeValue(__RPC__deref_out_opt IUnknown **mixedAttributeValue) = 0;
- virtual HRESULT STDMETHODCALLTYPE ElementFromIAccessible(__RPC__in_opt IAccessible *accessible, int childId, __RPC__deref_out_opt IUIAutomationElement **element) = 0;
- virtual HRESULT STDMETHODCALLTYPE ElementFromIAccessibleBuildCache(__RPC__in_opt IAccessible *accessible, int childId, __RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__deref_out_opt IUIAutomationElement **element) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IUIAutomation, 0x30cbe57d, 0xd9d0, 0x452a, 0xab,0x13, 0x7a,0xc5,0xac,0x48,0x25,0xee)
-#endif
-#endif
-
-
-#ifndef __IUIAutomationTreeWalker_INTERFACE_DEFINED__
-#define __IUIAutomationTreeWalker_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IUIAutomationTreeWalker, 0x4042c624, 0x389c, 0x4afc, 0xa6,0x30, 0x9d,0xf8,0x54,0xa5,0x41,0xfc);
-MIDL_INTERFACE("4042c624-389c-4afc-a630-9df854a541fc")
-IUIAutomationTreeWalker : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE GetParentElement(__RPC__in_opt IUIAutomationElement *element, __RPC__deref_out_opt IUIAutomationElement **parent) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetFirstChildElement(__RPC__in_opt IUIAutomationElement *element, __RPC__deref_out_opt IUIAutomationElement **first) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetLastChildElement(__RPC__in_opt IUIAutomationElement *element, __RPC__deref_out_opt IUIAutomationElement **last) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetNextSiblingElement(__RPC__in_opt IUIAutomationElement *element, __RPC__deref_out_opt IUIAutomationElement **next) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetPreviousSiblingElement(__RPC__in_opt IUIAutomationElement *element, __RPC__deref_out_opt IUIAutomationElement **previous) = 0;
- virtual HRESULT STDMETHODCALLTYPE NormalizeElement(__RPC__in_opt IUIAutomationElement *element, __RPC__deref_out_opt IUIAutomationElement **normalized) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetParentElementBuildCache(__RPC__in_opt IUIAutomationElement *element, __RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__deref_out_opt IUIAutomationElement **parent) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetFirstChildElementBuildCache(__RPC__in_opt IUIAutomationElement *element, __RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__deref_out_opt IUIAutomationElement **first) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetLastChildElementBuildCache(__RPC__in_opt IUIAutomationElement *element, __RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__deref_out_opt IUIAutomationElement **last) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetNextSiblingElementBuildCache(__RPC__in_opt IUIAutomationElement *element, __RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__deref_out_opt IUIAutomationElement **next) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetPreviousSiblingElementBuildCache(__RPC__in_opt IUIAutomationElement *element, __RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__deref_out_opt IUIAutomationElement **previous) = 0;
- virtual HRESULT STDMETHODCALLTYPE NormalizeElementBuildCache(__RPC__in_opt IUIAutomationElement *element, __RPC__in_opt IUIAutomationCacheRequest *cacheRequest, __RPC__deref_out_opt IUIAutomationElement **normalized) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_Condition(__RPC__deref_out_opt IUIAutomationCondition **condition) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IUIAutomationTreeWalker, 0x4042c624, 0x389c, 0x4afc, 0xa6,0x30, 0x9d,0xf8,0x54,0xa5,0x41,0xfc)
-#endif
-#endif
-
-DEFINE_GUID(CLSID_CUIAutomation, 0xff48dba4, 0x60ef, 0x4201, 0xaa,0x87, 0x54,0x10,0x3e,0xef,0x59,0x4e);
-
-#endif
diff --git a/src/gui/accessible/windows/apisupport/uiacontroltypeids_p.h b/src/gui/accessible/windows/apisupport/uiacontroltypeids_p.h
deleted file mode 100644
index 21d8080bc2..0000000000
--- a/src/gui/accessible/windows/apisupport/uiacontroltypeids_p.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef UIACONTROLTYPEIDS_H
-#define UIACONTROLTYPEIDS_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.
-//
-
-#define UIA_ButtonControlTypeId 50000
-#define UIA_CalendarControlTypeId 50001
-#define UIA_CheckBoxControlTypeId 50002
-#define UIA_ComboBoxControlTypeId 50003
-#define UIA_EditControlTypeId 50004
-#define UIA_HyperlinkControlTypeId 50005
-#define UIA_ImageControlTypeId 50006
-#define UIA_ListItemControlTypeId 50007
-#define UIA_ListControlTypeId 50008
-#define UIA_MenuControlTypeId 50009
-#define UIA_MenuBarControlTypeId 50010
-#define UIA_MenuItemControlTypeId 50011
-#define UIA_ProgressBarControlTypeId 50012
-#define UIA_RadioButtonControlTypeId 50013
-#define UIA_ScrollBarControlTypeId 50014
-#define UIA_SliderControlTypeId 50015
-#define UIA_SpinnerControlTypeId 50016
-#define UIA_StatusBarControlTypeId 50017
-#define UIA_TabControlTypeId 50018
-#define UIA_TabItemControlTypeId 50019
-#define UIA_TextControlTypeId 50020
-#define UIA_ToolBarControlTypeId 50021
-#define UIA_ToolTipControlTypeId 50022
-#define UIA_TreeControlTypeId 50023
-#define UIA_TreeItemControlTypeId 50024
-#define UIA_CustomControlTypeId 50025
-#define UIA_GroupControlTypeId 50026
-#define UIA_ThumbControlTypeId 50027
-#define UIA_DataGridControlTypeId 50028
-#define UIA_DataItemControlTypeId 50029
-#define UIA_DocumentControlTypeId 50030
-#define UIA_SplitButtonControlTypeId 50031
-#define UIA_WindowControlTypeId 50032
-#define UIA_PaneControlTypeId 50033
-#define UIA_HeaderControlTypeId 50034
-#define UIA_HeaderItemControlTypeId 50035
-#define UIA_TableControlTypeId 50036
-#define UIA_TitleBarControlTypeId 50037
-#define UIA_SeparatorControlTypeId 50038
-#define UIA_SemanticZoomControlTypeId 50039
-#define UIA_AppBarControlTypeId 50040
-
-#endif
diff --git a/src/gui/accessible/windows/apisupport/uiaerrorids_p.h b/src/gui/accessible/windows/apisupport/uiaerrorids_p.h
deleted file mode 100644
index b965fe5c30..0000000000
--- a/src/gui/accessible/windows/apisupport/uiaerrorids_p.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef UIAERRORIDS_H
-#define UIAERRORIDS_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.
-//
-
-#define UIA_E_ELEMENTNOTENABLED 0x80040200
-#define UIA_E_ELEMENTNOTAVAILABLE 0x80040201
-#define UIA_E_NOCLICKABLEPOINT 0x80040202
-#define UIA_E_PROXYASSEMBLYNOTLOADED 0x80040203
-#define UIA_E_NOTSUPPORTED 0x80040204
-#define UIA_E_INVALIDOPERATION 0x80131509
-#define UIA_E_TIMEOUT 0x80131505
-
-#endif
diff --git a/src/gui/accessible/windows/apisupport/uiaeventids_p.h b/src/gui/accessible/windows/apisupport/uiaeventids_p.h
deleted file mode 100644
index 7ac6d85ec5..0000000000
--- a/src/gui/accessible/windows/apisupport/uiaeventids_p.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef UIAEVENTIDS_H
-#define UIAEVENTIDS_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.
-//
-
-#define UIA_ToolTipOpenedEventId 20000
-#define UIA_ToolTipClosedEventId 20001
-#define UIA_StructureChangedEventId 20002
-#define UIA_MenuOpenedEventId 20003
-#define UIA_AutomationPropertyChangedEventId 20004
-#define UIA_AutomationFocusChangedEventId 20005
-#define UIA_AsyncContentLoadedEventId 20006
-#define UIA_MenuClosedEventId 20007
-#define UIA_LayoutInvalidatedEventId 20008
-#define UIA_Invoke_InvokedEventId 20009
-#define UIA_SelectionItem_ElementAddedToSelectionEventId 20010
-#define UIA_SelectionItem_ElementRemovedFromSelectionEventId 20011
-#define UIA_SelectionItem_ElementSelectedEventId 20012
-#define UIA_Selection_InvalidatedEventId 20013
-#define UIA_Text_TextSelectionChangedEventId 20014
-#define UIA_Text_TextChangedEventId 20015
-#define UIA_Window_WindowOpenedEventId 20016
-#define UIA_Window_WindowClosedEventId 20017
-#define UIA_MenuModeStartEventId 20018
-#define UIA_MenuModeEndEventId 20019
-#define UIA_InputReachedTargetEventId 20020
-#define UIA_InputReachedOtherElementEventId 20021
-#define UIA_InputDiscardedEventId 20022
-#define UIA_SystemAlertEventId 20023
-#define UIA_LiveRegionChangedEventId 20024
-#define UIA_HostedFragmentRootsInvalidatedEventId 20025
-#define UIA_Drag_DragStartEventId 20026
-#define UIA_Drag_DragCancelEventId 20027
-#define UIA_Drag_DragCompleteEventId 20028
-#define UIA_DropTarget_DragEnterEventId 20029
-#define UIA_DropTarget_DragLeaveEventId 20030
-#define UIA_DropTarget_DroppedEventId 20031
-#define UIA_TextEdit_TextChangedEventId 20032
-#define UIA_TextEdit_ConversionTargetChangedEventId 20033
-#define UIA_ChangesEventId 20034
-
-#endif
diff --git a/src/gui/accessible/windows/apisupport/uiageneralids_p.h b/src/gui/accessible/windows/apisupport/uiageneralids_p.h
deleted file mode 100644
index a6fdeceee3..0000000000
--- a/src/gui/accessible/windows/apisupport/uiageneralids_p.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef UIAGENERALIDS_H
-#define UIAGENERALIDS_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.
-//
-
-#define UiaAppendRuntimeId 3
-#define UiaRootObjectId -25
-
-#endif
diff --git a/src/gui/accessible/windows/apisupport/uiapatternids_p.h b/src/gui/accessible/windows/apisupport/uiapatternids_p.h
deleted file mode 100644
index 0ff463cd36..0000000000
--- a/src/gui/accessible/windows/apisupport/uiapatternids_p.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef UIAPATTERNIDS_H
-#define UIAPATTERNIDS_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.
-//
-
-#define UIA_InvokePatternId 10000
-#define UIA_SelectionPatternId 10001
-#define UIA_ValuePatternId 10002
-#define UIA_RangeValuePatternId 10003
-#define UIA_ScrollPatternId 10004
-#define UIA_ExpandCollapsePatternId 10005
-#define UIA_GridPatternId 10006
-#define UIA_GridItemPatternId 10007
-#define UIA_MultipleViewPatternId 10008
-#define UIA_WindowPatternId 10009
-#define UIA_SelectionItemPatternId 10010
-#define UIA_DockPatternId 10011
-#define UIA_TablePatternId 10012
-#define UIA_TableItemPatternId 10013
-#define UIA_TextPatternId 10014
-#define UIA_TogglePatternId 10015
-#define UIA_TransformPatternId 10016
-#define UIA_ScrollItemPatternId 10017
-#define UIA_LegacyIAccessiblePatternId 10018
-#define UIA_ItemContainerPatternId 10019
-#define UIA_VirtualizedItemPatternId 10020
-#define UIA_SynchronizedInputPatternId 10021
-#define UIA_ObjectModelPatternId 10022
-#define UIA_AnnotationPatternId 10023
-#define UIA_TextPattern2Id 10024
-#define UIA_StylesPatternId 10025
-#define UIA_SpreadsheetPatternId 10026
-#define UIA_SpreadsheetItemPatternId 10027
-#define UIA_TransformPattern2Id 10028
-#define UIA_TextChildPatternId 10029
-#define UIA_DragPatternId 10030
-#define UIA_DropTargetPatternId 10031
-#define UIA_TextEditPatternId 10032
-#define UIA_CustomNavigationPatternId 10033
-
-#endif
diff --git a/src/gui/accessible/windows/apisupport/uiapropertyids_p.h b/src/gui/accessible/windows/apisupport/uiapropertyids_p.h
deleted file mode 100644
index 77fc454e0f..0000000000
--- a/src/gui/accessible/windows/apisupport/uiapropertyids_p.h
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef UIAPROPERTYIDS_H
-#define UIAPROPERTYIDS_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.
-//
-
-#define UIA_RuntimeIdPropertyId 30000
-#define UIA_BoundingRectanglePropertyId 30001
-#define UIA_ProcessIdPropertyId 30002
-#define UIA_ControlTypePropertyId 30003
-#define UIA_LocalizedControlTypePropertyId 30004
-#define UIA_NamePropertyId 30005
-#define UIA_AcceleratorKeyPropertyId 30006
-#define UIA_AccessKeyPropertyId 30007
-#define UIA_HasKeyboardFocusPropertyId 30008
-#define UIA_IsKeyboardFocusablePropertyId 30009
-#define UIA_IsEnabledPropertyId 30010
-#define UIA_AutomationIdPropertyId 30011
-#define UIA_ClassNamePropertyId 30012
-#define UIA_HelpTextPropertyId 30013
-#define UIA_ClickablePointPropertyId 30014
-#define UIA_CulturePropertyId 30015
-#define UIA_IsControlElementPropertyId 30016
-#define UIA_IsContentElementPropertyId 30017
-#define UIA_LabeledByPropertyId 30018
-#define UIA_IsPasswordPropertyId 30019
-#define UIA_NativeWindowHandlePropertyId 30020
-#define UIA_ItemTypePropertyId 30021
-#define UIA_IsOffscreenPropertyId 30022
-#define UIA_OrientationPropertyId 30023
-#define UIA_FrameworkIdPropertyId 30024
-#define UIA_IsRequiredForFormPropertyId 30025
-#define UIA_ItemStatusPropertyId 30026
-#define UIA_IsDockPatternAvailablePropertyId 30027
-#define UIA_IsExpandCollapsePatternAvailablePropertyId 30028
-#define UIA_IsGridItemPatternAvailablePropertyId 30029
-#define UIA_IsGridPatternAvailablePropertyId 30030
-#define UIA_IsInvokePatternAvailablePropertyId 30031
-#define UIA_IsMultipleViewPatternAvailablePropertyId 30032
-#define UIA_IsRangeValuePatternAvailablePropertyId 30033
-#define UIA_IsScrollPatternAvailablePropertyId 30034
-#define UIA_IsScrollItemPatternAvailablePropertyId 30035
-#define UIA_IsSelectionItemPatternAvailablePropertyId 30036
-#define UIA_IsSelectionPatternAvailablePropertyId 30037
-#define UIA_IsTablePatternAvailablePropertyId 30038
-#define UIA_IsTableItemPatternAvailablePropertyId 30039
-#define UIA_IsTextPatternAvailablePropertyId 30040
-#define UIA_IsTogglePatternAvailablePropertyId 30041
-#define UIA_IsTransformPatternAvailablePropertyId 30042
-#define UIA_IsValuePatternAvailablePropertyId 30043
-#define UIA_IsWindowPatternAvailablePropertyId 30044
-#define UIA_ValueValuePropertyId 30045
-#define UIA_ValueIsReadOnlyPropertyId 30046
-#define UIA_RangeValueValuePropertyId 30047
-#define UIA_RangeValueIsReadOnlyPropertyId 30048
-#define UIA_RangeValueMinimumPropertyId 30049
-#define UIA_RangeValueMaximumPropertyId 30050
-#define UIA_RangeValueLargeChangePropertyId 30051
-#define UIA_RangeValueSmallChangePropertyId 30052
-#define UIA_ScrollHorizontalScrollPercentPropertyId 30053
-#define UIA_ScrollHorizontalViewSizePropertyId 30054
-#define UIA_ScrollVerticalScrollPercentPropertyId 30055
-#define UIA_ScrollVerticalViewSizePropertyId 30056
-#define UIA_ScrollHorizontallyScrollablePropertyId 30057
-#define UIA_ScrollVerticallyScrollablePropertyId 30058
-#define UIA_SelectionSelectionPropertyId 30059
-#define UIA_SelectionCanSelectMultiplePropertyId 30060
-#define UIA_SelectionIsSelectionRequiredPropertyId 30061
-#define UIA_GridRowCountPropertyId 30062
-#define UIA_GridColumnCountPropertyId 30063
-#define UIA_GridItemRowPropertyId 30064
-#define UIA_GridItemColumnPropertyId 30065
-#define UIA_GridItemRowSpanPropertyId 30066
-#define UIA_GridItemColumnSpanPropertyId 30067
-#define UIA_GridItemContainingGridPropertyId 30068
-#define UIA_DockDockPositionPropertyId 30069
-#define UIA_ExpandCollapseExpandCollapseStatePropertyId 30070
-#define UIA_MultipleViewCurrentViewPropertyId 30071
-#define UIA_MultipleViewSupportedViewsPropertyId 30072
-#define UIA_WindowCanMaximizePropertyId 30073
-#define UIA_WindowCanMinimizePropertyId 30074
-#define UIA_WindowWindowVisualStatePropertyId 30075
-#define UIA_WindowWindowInteractionStatePropertyId 30076
-#define UIA_WindowIsModalPropertyId 30077
-#define UIA_WindowIsTopmostPropertyId 30078
-#define UIA_SelectionItemIsSelectedPropertyId 30079
-#define UIA_SelectionItemSelectionContainerPropertyId 30080
-#define UIA_TableRowHeadersPropertyId 30081
-#define UIA_TableColumnHeadersPropertyId 30082
-#define UIA_TableRowOrColumnMajorPropertyId 30083
-#define UIA_TableItemRowHeaderItemsPropertyId 30084
-#define UIA_TableItemColumnHeaderItemsPropertyId 30085
-#define UIA_ToggleToggleStatePropertyId 30086
-#define UIA_TransformCanMovePropertyId 30087
-#define UIA_TransformCanResizePropertyId 30088
-#define UIA_TransformCanRotatePropertyId 30089
-#define UIA_IsLegacyIAccessiblePatternAvailablePropertyId 30090
-#define UIA_LegacyIAccessibleChildIdPropertyId 30091
-#define UIA_LegacyIAccessibleNamePropertyId 30092
-#define UIA_LegacyIAccessibleValuePropertyId 30093
-#define UIA_LegacyIAccessibleDescriptionPropertyId 30094
-#define UIA_LegacyIAccessibleRolePropertyId 30095
-#define UIA_LegacyIAccessibleStatePropertyId 30096
-#define UIA_LegacyIAccessibleHelpPropertyId 30097
-#define UIA_LegacyIAccessibleKeyboardShortcutPropertyId 30098
-#define UIA_LegacyIAccessibleSelectionPropertyId 30099
-#define UIA_LegacyIAccessibleDefaultActionPropertyId 30100
-#define UIA_AriaRolePropertyId 30101
-#define UIA_AriaPropertiesPropertyId 30102
-#define UIA_IsDataValidForFormPropertyId 30103
-#define UIA_ControllerForPropertyId 30104
-#define UIA_DescribedByPropertyId 30105
-#define UIA_FlowsToPropertyId 30106
-#define UIA_ProviderDescriptionPropertyId 30107
-#define UIA_IsItemContainerPatternAvailablePropertyId 30108
-#define UIA_IsVirtualizedItemPatternAvailablePropertyId 30109
-#define UIA_IsSynchronizedInputPatternAvailablePropertyId 30110
-#define UIA_OptimizeForVisualContentPropertyId 30111
-#define UIA_IsObjectModelPatternAvailablePropertyId 30112
-#define UIA_AnnotationAnnotationTypeIdPropertyId 30113
-#define UIA_AnnotationAnnotationTypeNamePropertyId 30114
-#define UIA_AnnotationAuthorPropertyId 30115
-#define UIA_AnnotationDateTimePropertyId 30116
-#define UIA_AnnotationTargetPropertyId 30117
-#define UIA_IsAnnotationPatternAvailablePropertyId 30118
-#define UIA_IsTextPattern2AvailablePropertyId 30119
-#define UIA_StylesStyleIdPropertyId 30120
-#define UIA_StylesStyleNamePropertyId 30121
-#define UIA_StylesFillColorPropertyId 30122
-#define UIA_StylesFillPatternStylePropertyId 30123
-#define UIA_StylesShapePropertyId 30124
-#define UIA_StylesFillPatternColorPropertyId 30125
-#define UIA_StylesExtendedPropertiesPropertyId 30126
-#define UIA_IsStylesPatternAvailablePropertyId 30127
-#define UIA_IsSpreadsheetPatternAvailablePropertyId 30128
-#define UIA_SpreadsheetItemFormulaPropertyId 30129
-#define UIA_SpreadsheetItemAnnotationObjectsPropertyId 30130
-#define UIA_SpreadsheetItemAnnotationTypesPropertyId 30131
-#define UIA_IsSpreadsheetItemPatternAvailablePropertyId 30132
-#define UIA_Transform2CanZoomPropertyId 30133
-#define UIA_IsTransformPattern2AvailablePropertyId 30134
-#define UIA_LiveSettingPropertyId 30135
-#define UIA_IsTextChildPatternAvailablePropertyId 30136
-#define UIA_IsDragPatternAvailablePropertyId 30137
-#define UIA_DragIsGrabbedPropertyId 30138
-#define UIA_DragDropEffectPropertyId 30139
-#define UIA_DragDropEffectsPropertyId 30140
-#define UIA_IsDropTargetPatternAvailablePropertyId 30141
-#define UIA_DropTargetDropTargetEffectPropertyId 30142
-#define UIA_DropTargetDropTargetEffectsPropertyId 30143
-#define UIA_DragGrabbedItemsPropertyId 30144
-#define UIA_Transform2ZoomLevelPropertyId 30145
-#define UIA_Transform2ZoomMinimumPropertyId 30146
-#define UIA_Transform2ZoomMaximumPropertyId 30147
-#define UIA_FlowsFromPropertyId 30148
-#define UIA_IsTextEditPatternAvailablePropertyId 30149
-#define UIA_IsPeripheralPropertyId 30150
-#define UIA_IsCustomNavigationPatternAvailablePropertyId 30151
-#define UIA_PositionInSetPropertyId 30152
-#define UIA_SizeOfSetPropertyId 30153
-#define UIA_LevelPropertyId 30154
-#define UIA_AnnotationTypesPropertyId 30155
-#define UIA_AnnotationObjectsPropertyId 30156
-#define UIA_LandmarkTypePropertyId 30157
-#define UIA_LocalizedLandmarkTypePropertyId 30158
-#define UIA_FullDescriptionPropertyId 30159
-#define UIA_FillColorPropertyId 30160
-#define UIA_OutlineColorPropertyId 30161
-#define UIA_FillTypePropertyId 30162
-#define UIA_VisualEffectsPropertyId 30163
-#define UIA_OutlineThicknessPropertyId 30164
-#define UIA_CenterPointPropertyId 30165
-#define UIA_RotationPropertyId 30166
-#define UIA_SizePropertyId 30167
-#define UIA_IsDialogPropertyId 30174
-
-#endif
diff --git a/src/gui/accessible/windows/apisupport/uiaserverinterfaces_p.h b/src/gui/accessible/windows/apisupport/uiaserverinterfaces_p.h
deleted file mode 100644
index 6cf15cacb0..0000000000
--- a/src/gui/accessible/windows/apisupport/uiaserverinterfaces_p.h
+++ /dev/null
@@ -1,367 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef UIASERVERINTERFACES_H
-#define UIASERVERINTERFACES_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 <unknwn.h>
-
-#ifndef __IRawElementProviderSimple_INTERFACE_DEFINED__
-#define __IRawElementProviderSimple_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IRawElementProviderSimple, 0xd6dd68d1, 0x86fd, 0x4332, 0x86,0x66, 0x9a,0xbe,0xde,0xa2,0xd2,0x4c);
-MIDL_INTERFACE("d6dd68d1-86fd-4332-8666-9abedea2d24c")
-IRawElementProviderSimple : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE get_ProviderOptions(__RPC__out enum ProviderOptions *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetPatternProvider(PATTERNID patternId, __RPC__deref_out_opt IUnknown **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(PROPERTYID propertyId, __RPC__out VARIANT *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_HostRawElementProvider(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IRawElementProviderSimple, 0xd6dd68d1, 0x86fd, 0x4332, 0x86,0x66, 0x9a,0xbe,0xde,0xa2,0xd2,0x4c)
-#endif
-#endif
-
-
-#ifndef __IRawElementProviderFragmentRoot_FWD_DEFINED__
-#define __IRawElementProviderFragmentRoot_FWD_DEFINED__
-typedef interface IRawElementProviderFragmentRoot IRawElementProviderFragmentRoot;
-#endif
-
-
-#ifndef __IRawElementProviderFragment_FWD_DEFINED__
-#define __IRawElementProviderFragment_FWD_DEFINED__
-typedef interface IRawElementProviderFragment IRawElementProviderFragment;
-#endif
-
-
-#ifndef __IRawElementProviderFragment_INTERFACE_DEFINED__
-#define __IRawElementProviderFragment_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IRawElementProviderFragment, 0xf7063da8, 0x8359, 0x439c, 0x92,0x97, 0xbb,0xc5,0x29,0x9a,0x7d,0x87);
-MIDL_INTERFACE("f7063da8-8359-439c-9297-bbc5299a7d87")
-IRawElementProviderFragment : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE Navigate(enum NavigateDirection direction, __RPC__deref_out_opt IRawElementProviderFragment **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetRuntimeId(__RPC__deref_out_opt SAFEARRAY **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_BoundingRectangle(__RPC__out struct UiaRect *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetEmbeddedFragmentRoots(__RPC__deref_out_opt SAFEARRAY **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE SetFocus() = 0;
- virtual HRESULT STDMETHODCALLTYPE get_FragmentRoot(__RPC__deref_out_opt IRawElementProviderFragmentRoot **pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IRawElementProviderFragment, 0xf7063da8, 0x8359, 0x439c, 0x92,0x97, 0xbb,0xc5,0x29,0x9a,0x7d,0x87)
-#endif
-#endif
-
-
-#ifndef __IRawElementProviderFragmentRoot_INTERFACE_DEFINED__
-#define __IRawElementProviderFragmentRoot_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IRawElementProviderFragmentRoot, 0x620ce2a5, 0xab8f, 0x40a9, 0x86,0xcb, 0xde,0x3c,0x75,0x59,0x9b,0x58);
-MIDL_INTERFACE("620ce2a5-ab8f-40a9-86cb-de3c75599b58")
-IRawElementProviderFragmentRoot : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE ElementProviderFromPoint(double x, double y, __RPC__deref_out_opt IRawElementProviderFragment **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetFocus(__RPC__deref_out_opt IRawElementProviderFragment **pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IRawElementProviderFragmentRoot, 0x620ce2a5, 0xab8f, 0x40a9, 0x86,0xcb, 0xde,0x3c,0x75,0x59,0x9b,0x58)
-#endif
-#endif
-
-
-#ifndef __IValueProvider_INTERFACE_DEFINED__
-#define __IValueProvider_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IValueProvider, 0xc7935180, 0x6fb3, 0x4201, 0xb1,0x74, 0x7d,0xf7,0x3a,0xdb,0xf6,0x4a);
-MIDL_INTERFACE("c7935180-6fb3-4201-b174-7df73adbf64a")
-IValueProvider : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE SetValue(__RPC__in LPCWSTR val) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_Value(__RPC__deref_out_opt BSTR *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_IsReadOnly(__RPC__out BOOL *pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IValueProvider, 0xc7935180, 0x6fb3, 0x4201, 0xb1,0x74, 0x7d,0xf7,0x3a,0xdb,0xf6,0x4a)
-#endif
-#endif
-
-
-#ifndef __IRangeValueProvider_INTERFACE_DEFINED__
-#define __IRangeValueProvider_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IRangeValueProvider, 0x36dc7aef, 0x33e6, 0x4691, 0xaf,0xe1, 0x2b,0xe7,0x27,0x4b,0x3d,0x33);
-MIDL_INTERFACE("36dc7aef-33e6-4691-afe1-2be7274b3d33")
-IRangeValueProvider : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE SetValue(double val) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_Value(__RPC__out double *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_IsReadOnly(__RPC__out BOOL *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_Maximum(__RPC__out double *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_Minimum(__RPC__out double *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_LargeChange(__RPC__out double *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_SmallChange(__RPC__out double *pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IRangeValueProvider, 0x36dc7aef, 0x33e6, 0x4691, 0xaf,0xe1, 0x2b,0xe7,0x27,0x4b,0x3d,0x33)
-#endif
-#endif
-
-
-#ifndef __ITextRangeProvider_INTERFACE_DEFINED__
-#define __ITextRangeProvider_INTERFACE_DEFINED__
-DEFINE_GUID(IID_ITextRangeProvider, 0x5347ad7b, 0xc355, 0x46f8, 0xaf,0xf5, 0x90,0x90,0x33,0x58,0x2f,0x63);
-MIDL_INTERFACE("5347ad7b-c355-46f8-aff5-909033582f63")
-ITextRangeProvider : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE Clone(__RPC__deref_out_opt ITextRangeProvider **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE Compare(__RPC__in_opt ITextRangeProvider *range, __RPC__out BOOL *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE CompareEndpoints(enum TextPatternRangeEndpoint endpoint, __RPC__in_opt ITextRangeProvider *targetRange, enum TextPatternRangeEndpoint targetEndpoint, __RPC__out int *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE ExpandToEnclosingUnit(enum TextUnit unit) = 0;
- virtual HRESULT STDMETHODCALLTYPE FindAttribute(TEXTATTRIBUTEID attributeId, VARIANT val, BOOL backward, __RPC__deref_out_opt ITextRangeProvider **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE FindText(__RPC__in BSTR text, BOOL backward, BOOL ignoreCase, __RPC__deref_out_opt ITextRangeProvider **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetAttributeValue(TEXTATTRIBUTEID attributeId, __RPC__out VARIANT *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetBoundingRectangles(__RPC__deref_out_opt SAFEARRAY **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetEnclosingElement(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetText(int maxLength, __RPC__deref_out_opt BSTR *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE Move(enum TextUnit unit, int count, __RPC__out int *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE MoveEndpointByUnit(enum TextPatternRangeEndpoint endpoint, enum TextUnit unit, int count, __RPC__out int *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE MoveEndpointByRange(enum TextPatternRangeEndpoint endpoint, __RPC__in_opt ITextRangeProvider *targetRange, enum TextPatternRangeEndpoint targetEndpoint) = 0;
- virtual HRESULT STDMETHODCALLTYPE Select() = 0;
- virtual HRESULT STDMETHODCALLTYPE AddToSelection() = 0;
- virtual HRESULT STDMETHODCALLTYPE RemoveFromSelection() = 0;
- virtual HRESULT STDMETHODCALLTYPE ScrollIntoView(BOOL alignToTop) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetChildren(__RPC__deref_out_opt SAFEARRAY **pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(ITextRangeProvider, 0x5347ad7b, 0xc355, 0x46f8, 0xaf,0xf5, 0x90,0x90,0x33,0x58,0x2f,0x63)
-#endif
-#endif
-
-
-#ifndef __ITextProvider_INTERFACE_DEFINED__
-#define __ITextProvider_INTERFACE_DEFINED__
-DEFINE_GUID(IID_ITextProvider, 0x3589c92c, 0x63f3, 0x4367, 0x99,0xbb, 0xad,0xa6,0x53,0xb7,0x7c,0xf2);
-MIDL_INTERFACE("3589c92c-63f3-4367-99bb-ada653b77cf2")
-ITextProvider : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE GetSelection(__RPC__deref_out_opt SAFEARRAY **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetVisibleRanges(__RPC__deref_out_opt SAFEARRAY **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE RangeFromChild(__RPC__in_opt IRawElementProviderSimple *childElement, __RPC__deref_out_opt ITextRangeProvider **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE RangeFromPoint(struct UiaPoint point, __RPC__deref_out_opt ITextRangeProvider **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_DocumentRange(__RPC__deref_out_opt ITextRangeProvider **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_SupportedTextSelection(__RPC__out enum SupportedTextSelection *pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(ITextProvider, 0x3589c92c, 0x63f3, 0x4367, 0x99,0xbb, 0xad,0xa6,0x53,0xb7,0x7c,0xf2)
-#endif
-#endif
-
-
-#ifndef __ITextProvider2_INTERFACE_DEFINED__
-#define __ITextProvider2_INTERFACE_DEFINED__
-DEFINE_GUID(IID_ITextProvider2, 0x0dc5e6ed, 0x3e16, 0x4bf1, 0x8f,0x9a, 0xa9,0x79,0x87,0x8b,0xc1,0x95);
-MIDL_INTERFACE("0dc5e6ed-3e16-4bf1-8f9a-a979878bc195")
-ITextProvider2 : public ITextProvider
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE RangeFromAnnotation(__RPC__in_opt IRawElementProviderSimple *annotationElement, __RPC__deref_out_opt ITextRangeProvider **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetCaretRange(__RPC__out BOOL *isActive, __RPC__deref_out_opt ITextRangeProvider **pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(ITextProvider2, 0x0dc5e6ed, 0x3e16, 0x4bf1, 0x8f,0x9a, 0xa9,0x79,0x87,0x8b,0xc1,0x95)
-#endif
-#endif
-
-
-#ifndef __IToggleProvider_INTERFACE_DEFINED__
-#define __IToggleProvider_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IToggleProvider, 0x56d00bd0, 0xc4f4, 0x433c, 0xa8,0x36, 0x1a,0x52,0xa5,0x7e,0x08,0x92);
-MIDL_INTERFACE("56d00bd0-c4f4-433c-a836-1a52a57e0892")
-IToggleProvider : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE Toggle() = 0;
- virtual HRESULT STDMETHODCALLTYPE get_ToggleState(__RPC__out enum ToggleState *pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IToggleProvider, 0x56d00bd0, 0xc4f4, 0x433c, 0xa8,0x36, 0x1a,0x52,0xa5,0x7e,0x08,0x92)
-#endif
-#endif
-
-
-#ifndef __IInvokeProvider_INTERFACE_DEFINED__
-#define __IInvokeProvider_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IInvokeProvider, 0x54fcb24b, 0xe18e, 0x47a2, 0xb4,0xd3, 0xec,0xcb,0xe7,0x75,0x99,0xa2);
-MIDL_INTERFACE("54fcb24b-e18e-47a2-b4d3-eccbe77599a2")
-IInvokeProvider : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE Invoke() = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IInvokeProvider, 0x54fcb24b, 0xe18e, 0x47a2, 0xb4,0xd3, 0xec,0xcb,0xe7,0x75,0x99,0xa2)
-#endif
-#endif
-
-
-#ifndef __ISelectionProvider_INTERFACE_DEFINED__
-#define __ISelectionProvider_INTERFACE_DEFINED__
-DEFINE_GUID(IID_ISelectionProvider, 0xfb8b03af, 0x3bdf, 0x48d4, 0xbd,0x36, 0x1a,0x65,0x79,0x3b,0xe1,0x68);
-MIDL_INTERFACE("fb8b03af-3bdf-48d4-bd36-1a65793be168")
-ISelectionProvider : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE GetSelection(__RPC__deref_out_opt SAFEARRAY **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CanSelectMultiple(__RPC__out BOOL *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_IsSelectionRequired(__RPC__out BOOL *pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(ISelectionProvider, 0xfb8b03af, 0x3bdf, 0x48d4, 0xbd,0x36, 0x1a,0x65,0x79,0x3b,0xe1,0x68)
-#endif
-#endif
-
-
-#ifndef __ISelectionItemProvider_INTERFACE_DEFINED__
-#define __ISelectionItemProvider_INTERFACE_DEFINED__
-DEFINE_GUID(IID_ISelectionItemProvider, 0x2acad808, 0xb2d4, 0x452d, 0xa4,0x07, 0x91,0xff,0x1a,0xd1,0x67,0xb2);
-MIDL_INTERFACE("2acad808-b2d4-452d-a407-91ff1ad167b2")
-ISelectionItemProvider : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE Select() = 0;
- virtual HRESULT STDMETHODCALLTYPE AddToSelection() = 0;
- virtual HRESULT STDMETHODCALLTYPE RemoveFromSelection() = 0;
- virtual HRESULT STDMETHODCALLTYPE get_IsSelected(__RPC__out BOOL *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_SelectionContainer(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(ISelectionItemProvider, 0x2acad808, 0xb2d4, 0x452d, 0xa4,0x07, 0x91,0xff,0x1a,0xd1,0x67,0xb2)
-#endif
-#endif
-
-
-#ifndef __ITableProvider_INTERFACE_DEFINED__
-#define __ITableProvider_INTERFACE_DEFINED__
-DEFINE_GUID(IID_ITableProvider, 0x9c860395, 0x97b3, 0x490a, 0xb5,0x2a, 0x85,0x8c,0xc2,0x2a,0xf1,0x66);
-MIDL_INTERFACE("9c860395-97b3-490a-b52a-858cc22af166")
-ITableProvider : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE GetRowHeaders(__RPC__deref_out_opt SAFEARRAY **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetColumnHeaders(__RPC__deref_out_opt SAFEARRAY **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_RowOrColumnMajor(__RPC__out enum RowOrColumnMajor *pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(ITableProvider, 0x9c860395, 0x97b3, 0x490a, 0xb5,0x2a, 0x85,0x8c,0xc2,0x2a,0xf1,0x66)
-#endif
-#endif
-
-
-#ifndef __ITableItemProvider_INTERFACE_DEFINED__
-#define __ITableItemProvider_INTERFACE_DEFINED__
-DEFINE_GUID(IID_ITableItemProvider, 0xb9734fa6, 0x771f, 0x4d78, 0x9c,0x90, 0x25,0x17,0x99,0x93,0x49,0xcd);
-MIDL_INTERFACE("b9734fa6-771f-4d78-9c90-2517999349cd")
-ITableItemProvider : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE GetRowHeaderItems(__RPC__deref_out_opt SAFEARRAY **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetColumnHeaderItems(__RPC__deref_out_opt SAFEARRAY **pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(ITableItemProvider, 0xb9734fa6, 0x771f, 0x4d78, 0x9c,0x90, 0x25,0x17,0x99,0x93,0x49,0xcd)
-#endif
-#endif
-
-
-#ifndef __IGridProvider_INTERFACE_DEFINED__
-#define __IGridProvider_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IGridProvider, 0xb17d6187, 0x0907, 0x464b, 0xa1,0x68, 0x0e,0xf1,0x7a,0x15,0x72,0xb1);
-MIDL_INTERFACE("b17d6187-0907-464b-a168-0ef17a1572b1")
-IGridProvider : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE GetItem(int row, int column, __RPC__deref_out_opt IRawElementProviderSimple **pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_RowCount(__RPC__out int *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_ColumnCount(__RPC__out int *pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IGridProvider, 0xb17d6187, 0x0907, 0x464b, 0xa1,0x68, 0x0e,0xf1,0x7a,0x15,0x72,0xb1)
-#endif
-#endif
-
-
-#ifndef __IGridItemProvider_INTERFACE_DEFINED__
-#define __IGridItemProvider_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IGridItemProvider, 0xd02541f1, 0xfb81, 0x4d64, 0xae,0x32, 0xf5,0x20,0xf8,0xa6,0xdb,0xd1);
-MIDL_INTERFACE("d02541f1-fb81-4d64-ae32-f520f8a6dbd1")
-IGridItemProvider : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE get_Row(__RPC__out int *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_Column(__RPC__out int *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_RowSpan(__RPC__out int *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_ColumnSpan(__RPC__out int *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_ContainingGrid(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IGridItemProvider, 0xd02541f1, 0xfb81, 0x4d64, 0xae,0x32, 0xf5,0x20,0xf8,0xa6,0xdb,0xd1)
-#endif
-#endif
-
-
-#ifndef __IWindowProvider_INTERFACE_DEFINED__
-#define __IWindowProvider_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IWindowProvider, 0x987df77b, 0xdb06, 0x4d77, 0x8f,0x8a, 0x86,0xa9,0xc3,0xbb,0x90,0xb9);
-MIDL_INTERFACE("987df77b-db06-4d77-8f8a-86a9c3bb90b9")
-IWindowProvider : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE SetVisualState(enum WindowVisualState state) = 0;
- virtual HRESULT STDMETHODCALLTYPE Close( void) = 0;
- virtual HRESULT STDMETHODCALLTYPE WaitForInputIdle(int milliseconds, __RPC__out BOOL *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CanMaximize(__RPC__out BOOL *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_CanMinimize(__RPC__out BOOL *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_IsModal(__RPC__out BOOL *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_WindowVisualState(__RPC__out enum WindowVisualState *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_WindowInteractionState(__RPC__out enum WindowInteractionState *pRetVal) = 0;
- virtual HRESULT STDMETHODCALLTYPE get_IsTopmost(__RPC__out BOOL *pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IWindowProvider, 0x987df77b, 0xdb06, 0x4d77, 0x8f,0x8a, 0x86,0xa9,0xc3,0xbb,0x90,0xb9)
-#endif
-#endif
-
-
-#ifndef __IExpandCollapseProvider_INTERFACE_DEFINED__
-#define __IExpandCollapseProvider_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IExpandCollapseProvider, 0xd847d3a5, 0xcab0, 0x4a98, 0x8c,0x32, 0xec,0xb4,0x5c,0x59,0xad,0x24);
-MIDL_INTERFACE("d847d3a5-cab0-4a98-8c32-ecb45c59ad24")
-IExpandCollapseProvider : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE Expand() = 0;
- virtual HRESULT STDMETHODCALLTYPE Collapse() = 0;
- virtual HRESULT STDMETHODCALLTYPE get_ExpandCollapseState(__RPC__out enum ExpandCollapseState *pRetVal) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IExpandCollapseProvider, 0xd847d3a5, 0xcab0, 0x4a98, 0x8c,0x32, 0xec,0xb4,0x5c,0x59,0xad,0x24)
-#endif
-#endif
-
-#endif
diff --git a/src/gui/accessible/windows/apisupport/uiatypes_p.h b/src/gui/accessible/windows/apisupport/uiatypes_p.h
deleted file mode 100644
index 465bd07a07..0000000000
--- a/src/gui/accessible/windows/apisupport/uiatypes_p.h
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef UIATYPES_H
-#define UIATYPES_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.
-//
-
-typedef int PROPERTYID;
-typedef int PATTERNID;
-typedef int EVENTID;
-typedef int TEXTATTRIBUTEID;
-typedef int CONTROLTYPEID;
-typedef int LANDMARKTYPEID;
-typedef int METADATAID;
-
-typedef void *UIA_HWND;
-
-enum NavigateDirection {
- NavigateDirection_Parent = 0,
- NavigateDirection_NextSibling = 1,
- NavigateDirection_PreviousSibling = 2,
- NavigateDirection_FirstChild = 3,
- NavigateDirection_LastChild = 4
-};
-
-enum ProviderOptions {
- ProviderOptions_ClientSideProvider = 0x1,
- ProviderOptions_ServerSideProvider = 0x2,
- ProviderOptions_NonClientAreaProvider = 0x4,
- ProviderOptions_OverrideProvider = 0x8,
- ProviderOptions_ProviderOwnsSetFocus = 0x10,
- ProviderOptions_UseComThreading = 0x20,
- ProviderOptions_RefuseNonClientSupport = 0x40,
- ProviderOptions_HasNativeIAccessible = 0x80,
- ProviderOptions_UseClientCoordinates = 0x100
-};
-
-enum SupportedTextSelection {
- SupportedTextSelection_None = 0,
- SupportedTextSelection_Single = 1,
- SupportedTextSelection_Multiple = 2
-};
-
-enum TextUnit {
- TextUnit_Character = 0,
- TextUnit_Format = 1,
- TextUnit_Word = 2,
- TextUnit_Line = 3,
- TextUnit_Paragraph = 4,
- TextUnit_Page = 5,
- TextUnit_Document = 6
-};
-
-enum TextPatternRangeEndpoint {
- TextPatternRangeEndpoint_Start = 0,
- TextPatternRangeEndpoint_End = 1
-};
-
-enum CaretPosition {
- CaretPosition_Unknown = 0,
- CaretPosition_EndOfLine = 1,
- CaretPosition_BeginningOfLine = 2
-};
-
-enum ToggleState {
- ToggleState_Off = 0,
- ToggleState_On = 1,
- ToggleState_Indeterminate = 2
-};
-
-enum RowOrColumnMajor {
- RowOrColumnMajor_RowMajor = 0,
- RowOrColumnMajor_ColumnMajor = 1,
- RowOrColumnMajor_Indeterminate = 2
-};
-
-enum TreeScope {
- TreeScope_None = 0,
- TreeScope_Element = 0x1,
- TreeScope_Children = 0x2,
- TreeScope_Descendants = 0x4,
- TreeScope_Parent = 0x8,
- TreeScope_Ancestors = 0x10,
- TreeScope_Subtree = TreeScope_Element | TreeScope_Children | TreeScope_Descendants
-};
-
-enum OrientationType {
- OrientationType_None = 0,
- OrientationType_Horizontal = 1,
- OrientationType_Vertical = 2
-};
-
-enum PropertyConditionFlags {
- PropertyConditionFlags_None = 0,
- PropertyConditionFlags_IgnoreCase = 1
-};
-
-enum WindowVisualState {
- WindowVisualState_Normal = 0,
- WindowVisualState_Maximized = 1,
- WindowVisualState_Minimized = 2
-};
-
-enum WindowInteractionState {
- WindowInteractionState_Running = 0,
- WindowInteractionState_Closing = 1,
- WindowInteractionState_ReadyForUserInteraction = 2,
- WindowInteractionState_BlockedByModalWindow = 3,
- WindowInteractionState_NotResponding = 4
-};
-
-enum ExpandCollapseState {
- ExpandCollapseState_Collapsed = 0,
- ExpandCollapseState_Expanded = 1,
- ExpandCollapseState_PartiallyExpanded = 2,
- ExpandCollapseState_LeafNode = 3
-};
-
-enum NotificationKind {
- NotificationKind_ItemAdded = 0,
- NotificationKind_ItemRemoved = 1,
- NotificationKind_ActionCompleted = 2,
- NotificationKind_ActionAborted = 3,
- NotificationKind_Other = 4
-};
-
-enum NotificationProcessing {
- NotificationProcessing_ImportantAll = 0,
- NotificationProcessing_ImportantMostRecent = 1,
- NotificationProcessing_All = 2,
- NotificationProcessing_MostRecent = 3,
- NotificationProcessing_CurrentThenMostRecent = 4
-};
-
-struct UiaRect {
- double left;
- double top;
- double width;
- double height;
-};
-
-struct UiaPoint {
- double x;
- double y;
-};
-
-#endif
diff --git a/src/gui/compat/removed_api.cpp b/src/gui/compat/removed_api.cpp
new file mode 100644
index 0000000000..659a27ecb6
--- /dev/null
+++ b/src/gui/compat/removed_api.cpp
@@ -0,0 +1,59 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#define QT_GUI_BUILD_REMOVED_API
+
+#include "qtguiglobal.h"
+
+QT_USE_NAMESPACE
+
+#if QT_GUI_REMOVED_SINCE(6, 6)
+
+#include "qpixmapcache.h" // inlined API
+
+// #include "qotherheader.h"
+// // implement removed functions from qotherheader.h
+// order sections alphabetically
+
+#endif // QT_GUI_REMOVED_SINCE(6, 6)
+
+#if QT_GUI_REMOVED_SINCE(6, 7)
+
+#include "qtextdocument.h"
+
+bool Qt::mightBeRichText(const QString& text)
+{
+ return Qt::mightBeRichText(qToStringViewIgnoringNull(text));
+}
+
+#endif // QT_GUI_REMOVED_SINCE(6, 7)
+
+#if QT_GUI_REMOVED_SINCE(6, 8)
+
+#include "qpagelayout.h"
+
+bool QPageLayout::setMargins(const QMarginsF &margins)
+{
+ return setMargins(margins, OutOfBoundsPolicy::Reject);
+}
+
+bool QPageLayout::setLeftMargin(qreal leftMargin)
+{
+ return setLeftMargin(leftMargin, OutOfBoundsPolicy::Reject);
+}
+
+bool QPageLayout::setRightMargin(qreal rightMargin)
+{
+ return setRightMargin(rightMargin, OutOfBoundsPolicy::Reject);
+}
+
+bool QPageLayout::setTopMargin(qreal topMargin)
+{
+ return setTopMargin(topMargin, OutOfBoundsPolicy::Reject);
+}
+
+// #include "qotherheader.h"
+// // implement removed functions from qotherheader.h
+// order sections alphabetically
+
+#endif // QT_GUI_REMOVED_SINCE(6, 8)
diff --git a/src/gui/configure.cmake b/src/gui/configure.cmake
index 2c009cbc41..e1d8efb292 100644
--- a/src/gui/configure.cmake
+++ b/src/gui/configure.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
#### Inputs
@@ -25,7 +28,8 @@ set_property(CACHE INPUT_libpng PROPERTY STRINGS undefined no qt system)
#### Libraries
-qt_set01(X11_SUPPORTED LINUX OR HPUX OR FREEBSD OR NETBSD OR OPENBSD OR SOLARIS OR HURD) # special case
+qt_set01(X11_SUPPORTED LINUX OR HPUX OR FREEBSD OR NETBSD OR OPENBSD OR SOLARIS OR
+ HURD)
qt_find_package(ATSPI2 PROVIDED_TARGETS PkgConfig::ATSPI2 MODULE_NAME gui QMAKE_LIB atspi)
qt_find_package(DirectFB PROVIDED_TARGETS PkgConfig::DirectFB MODULE_NAME gui QMAKE_LIB directfb)
qt_find_package(Libdrm PROVIDED_TARGETS Libdrm::Libdrm MODULE_NAME gui QMAKE_LIB drm)
@@ -54,20 +58,25 @@ qt_find_package(Mtdev PROVIDED_TARGETS PkgConfig::Mtdev MODULE_NAME gui QMAKE_LI
qt_find_package(WrapOpenGL PROVIDED_TARGETS WrapOpenGL::WrapOpenGL MODULE_NAME gui QMAKE_LIB opengl)
qt_find_package(GLESv2 PROVIDED_TARGETS GLESv2::GLESv2 MODULE_NAME gui QMAKE_LIB opengl_es2)
qt_find_package(Tslib PROVIDED_TARGETS PkgConfig::Tslib MODULE_NAME gui QMAKE_LIB tslib)
-qt_find_package(WrapVulkanHeaders PROVIDED_TARGETS WrapVulkanHeaders::WrapVulkanHeaders MODULE_NAME gui QMAKE_LIB vulkan MARK_OPTIONAL) # special case
+qt_find_package(WrapVulkanHeaders PROVIDED_TARGETS WrapVulkanHeaders::WrapVulkanHeaders
+ MODULE_NAME gui QMAKE_LIB vulkan MARK_OPTIONAL)
if((LINUX) OR QT_FIND_ALL_PACKAGES_ALWAYS)
qt_find_package(Wayland PROVIDED_TARGETS Wayland::Server MODULE_NAME gui QMAKE_LIB wayland_server)
+ qt_find_package(Wayland PROVIDED_TARGETS Wayland::Client MODULE_NAME gui QMAKE_LIB wayland_client)
endif()
if((X11_SUPPORTED) OR QT_FIND_ALL_PACKAGES_ALWAYS)
qt_find_package(X11 PROVIDED_TARGETS X11::X11 MODULE_NAME gui QMAKE_LIB xlib)
endif()
if((X11_SUPPORTED) OR QT_FIND_ALL_PACKAGES_ALWAYS)
- qt_find_package(X11 PROVIDED_TARGETS ${X11_SM_LIB} ${X11_ICE_LIB} MODULE_NAME gui QMAKE_LIB x11sm)
+ qt_find_package(X11 PROVIDED_TARGETS X11::SM X11::ICE MODULE_NAME gui QMAKE_LIB x11sm)
endif()
if((X11_SUPPORTED) OR QT_FIND_ALL_PACKAGES_ALWAYS)
qt_find_package(XCB 1.11 PROVIDED_TARGETS XCB::XCB MODULE_NAME gui QMAKE_LIB xcb)
endif()
if((X11_SUPPORTED) OR QT_FIND_ALL_PACKAGES_ALWAYS)
+ qt_find_package(XCB 0.1.1 COMPONENTS CURSOR PROVIDED_TARGETS XCB::CURSOR MODULE_NAME gui QMAKE_LIB xcb_cursor)
+endif()
+if((X11_SUPPORTED) OR QT_FIND_ALL_PACKAGES_ALWAYS)
qt_find_package(XCB 0.3.9 COMPONENTS ICCCM PROVIDED_TARGETS XCB::ICCCM MODULE_NAME gui QMAKE_LIB xcb_icccm)
endif()
qt_add_qmake_lib_dependency(xcb_icccm xcb)
@@ -134,6 +143,7 @@ if((X11_SUPPORTED) OR QT_FIND_ALL_PACKAGES_ALWAYS)
endif()
qt_add_qmake_lib_dependency(xrender xlib)
+qt_find_package(RenderDoc PROVIDED_TARGETS RenderDoc::RenderDoc)
#### Tests
@@ -224,6 +234,7 @@ EGLDeviceEXT device = 0;
EGLStreamKHR stream = 0;
EGLOutputLayerEXT layer = 0;
(void) EGL_DRM_CRTC_EXT;
+(void) EGL_DRM_MASTER_FD_EXT;
/* END TEST: */
return 0;
}
@@ -271,8 +282,8 @@ qt_config_compile_test(egl_viv
LABEL "i.Mx6 EGL"
LIBRARIES
EGL::EGL
- COMPILE_OPTIONS # special case
- "-DEGL_API_FB=1" # special case
+ COMPILE_OPTIONS
+ "-DEGL_API_FB=1"
CODE
"#include <EGL/egl.h>
#include <EGL/eglvivante.h>
@@ -400,11 +411,9 @@ ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
")
# opengles3
-# special case begin
if(WASM)
set(extra_compiler_options "-s FULL_ES3=1")
endif()
-# special case end
set(test_libs GLESv2::GLESv2)
if(INTEGRITY AND _qt_igy_gui_libs)
@@ -415,9 +424,7 @@ qt_config_compile_test(opengles3
LABEL "OpenGL ES 3.0"
LIBRARIES
${test_libs}
-# special case begin
COMPILE_OPTIONS ${extra_compiler_options}
-# special case end
CODE
"#ifdef __APPLE__
# include <OpenGLES/ES3/gl.h>
@@ -479,6 +486,7 @@ glFramebufferTexture(GL_TEXTURE_2D, GL_DEPTH_STENCIL_ATTACHMENT, 1, 0);
qt_config_compile_test(xcb_syslibs
LABEL "XCB (extensions)"
LIBRARIES
+ XCB::CURSOR
XCB::ICCCM
XCB::IMAGE
XCB::KEYSYMS
@@ -497,6 +505,7 @@ qt_config_compile_test(xcb_syslibs
#include <xcb/xcb.h>
#include <xcb/xcb_image.h>
#include <xcb/xcb_keysyms.h>
+#include <xcb/xcb_cursor.h>
#include <xcb/randr.h>
#include <xcb/render.h>
#include <xcb/shape.h>
@@ -545,7 +554,6 @@ libinput_event_pointer_get_scroll_value_v120(nullptr, LIBINPUT_POINTER_AXIS_SCRO
}
")
-# special case begin
# directwrite (assumes DirectWrite2)
qt_config_compile_test(directwrite
LABEL "WINDOWS directwrite"
@@ -572,7 +580,7 @@ qt_config_compile_test(directwrite3
int main(int, char **)
{
IUnknown *factory = nullptr;
- DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3),
+ DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory6),
&factory);
return 0;
}
@@ -607,14 +615,27 @@ int main(int, char **)
return 0;
}
")
-# special case end
+
+qt_config_compile_test(renderdoc
+ LIBRARIES
+ RenderDoc::RenderDoc
+ LABEL "RenderDoc header check"
+ CODE
+"#include <renderdoc_app.h>
+int main(int, char **)
+{
+ if (RENDERDOC_Version::eRENDERDOC_API_Version_1_6_0)
+ return 0;
+ return 0;
+}
+")
#### Features
qt_feature("accessibility-atspi-bridge" PUBLIC PRIVATE
LABEL "ATSPI Bridge"
- CONDITION QT_FEATURE_accessibility AND QT_FEATURE_xcb AND QT_FEATURE_dbus AND ATSPI2_FOUND
+ CONDITION QT_FEATURE_accessibility AND QT_FEATURE_dbus AND ATSPI2_FOUND
)
qt_feature_definition("accessibility-atspi-bridge" "QT_NO_ACCESSIBILITY_ATSPI_BRIDGE" NEGATE VALUE "1")
qt_feature("directfb" PRIVATE
@@ -625,21 +646,21 @@ qt_feature("directfb" PRIVATE
)
qt_feature("directwrite" PRIVATE
LABEL "DirectWrite"
- CONDITION TEST_directwrite # special case
+ CONDITION TEST_directwrite
EMIT_IF WIN32
)
qt_feature("directwrite3" PRIVATE
LABEL "DirectWrite 3"
- CONDITION QT_FEATURE_directwrite AND TEST_directwrite3 # special case
+ CONDITION QT_FEATURE_directwrite AND TEST_directwrite3
EMIT_IF WIN32
)
qt_feature("direct2d" PRIVATE
LABEL "Direct 2D"
- CONDITION WIN32 AND NOT WINRT AND TEST_d2d1 # special case
+ CONDITION WIN32 AND NOT WINRT AND TEST_d2d1
)
qt_feature("direct2d1_1" PRIVATE
LABEL "Direct 2D 1.1"
- CONDITION QT_FEATURE_direct2d AND TEST_d2d1_1 # special case
+ CONDITION QT_FEATURE_direct2d AND TEST_d2d1_1
)
qt_feature("evdev" PRIVATE
LABEL "evdev"
@@ -661,7 +682,7 @@ qt_feature("system-freetype" PRIVATE
qt_feature("fontconfig" PUBLIC PRIVATE
LABEL "Fontconfig"
AUTODETECT NOT APPLE
- CONDITION NOT WIN32 AND QT_FEATURE_system_freetype AND FONTCONFIG_FOUND
+ CONDITION NOT WIN32 AND QT_FEATURE_system_freetype AND Fontconfig_FOUND
)
qt_feature_definition("fontconfig" "QT_NO_FONTCONFIG" NEGATE VALUE "1")
qt_feature("gbm"
@@ -790,6 +811,10 @@ qt_feature("vulkan" PUBLIC
LABEL "Vulkan"
CONDITION QT_FEATURE_library AND QT_FEATURE_vkgen AND WrapVulkanHeaders_FOUND
)
+qt_feature("metal" PUBLIC
+ LABEL "Metal"
+ CONDITION MACOS OR IOS OR VISIONOS
+)
qt_feature("vkkhrdisplay" PRIVATE
SECTION "Platform plugins"
LABEL "VK_KHR_display"
@@ -843,7 +868,7 @@ qt_feature("eglfs_rcar" PRIVATE
)
qt_feature("eglfs_viv_wl" PRIVATE
LABEL "EGLFS i.Mx6 Wayland"
- CONDITION QT_FEATURE_eglfs_viv AND Wayland_FOUND
+ CONDITION QT_FEATURE_eglfs_viv AND TARGET Wayland::Server
)
qt_feature("eglfs_openwfd" PRIVATE
LABEL "EGLFS OpenWFD"
@@ -924,7 +949,7 @@ qt_feature("xcb-glx" PRIVATE
)
qt_feature("xcb-egl-plugin" PRIVATE
LABEL "EGL-X11 Plugin"
- CONDITION QT_FEATURE_egl_x11 AND QT_FEATURE_opengl
+ CONDITION QT_FEATURE_egl AND QT_FEATURE_opengl
EMIT_IF QT_FEATURE_xcb
)
qt_feature("xcb-native-painting" PRIVATE
@@ -991,7 +1016,8 @@ qt_feature("system-textmarkdownreader" PUBLIC
qt_feature("textmarkdownwriter" PUBLIC
SECTION "Kernel"
LABEL "MarkdownWriter"
- PURPOSE "Provides a Markdown (CommonMark) writer"
+ CONDITION QT_FEATURE_regularexpression
+ PURPOSE "Provides a Markdown (CommonMark and GitHub) writer"
)
qt_feature("textodfwriter" PUBLIC
SECTION "Kernel"
@@ -1196,6 +1222,7 @@ qt_feature("raster-fp" PRIVATE
SECTION "Painting"
LABEL "QPainter - floating point raster"
PURPOSE "Internal painting support for floating point rasterization."
+ CONDITION NOT VXWORKS # QTBUG-115777
)
qt_feature("undocommand" PUBLIC
SECTION "Utilities"
@@ -1216,7 +1243,19 @@ qt_feature("undogroup" PUBLIC
PURPOSE "Provides the ability to cluster QUndoCommands."
CONDITION QT_FEATURE_undostack
)
+qt_feature("graphicsframecapture" PRIVATE
+ SECTION "Utilities"
+ LABEL "QGraphicsFrameCapture"
+ PURPOSE "Provides a way to capture 3D graphics API calls for a rendered frame."
+ CONDITION TEST_renderdoc OR (MACOS OR IOS)
+)
qt_feature_definition("undogroup" "QT_NO_UNDOGROUP" NEGATE VALUE "1")
+qt_feature("wayland" PUBLIC
+ SECTION "Platform plugins"
+ LABEL "Wayland"
+ CONDITION TARGET Wayland::Client
+)
+
qt_configure_add_summary_section(NAME "Qt Gui")
qt_configure_add_summary_entry(ARGS "accessibility")
qt_configure_add_summary_entry(ARGS "freetype")
@@ -1254,6 +1293,8 @@ qt_configure_add_summary_entry(ARGS "opengles31")
qt_configure_add_summary_entry(ARGS "opengles32")
qt_configure_end_summary_section() # end of "OpenGL" section
qt_configure_add_summary_entry(ARGS "vulkan")
+qt_configure_add_summary_entry(ARGS "metal")
+qt_configure_add_summary_entry(ARGS "graphicsframecapture")
qt_configure_add_summary_entry(ARGS "sessionmanager")
qt_configure_end_summary_section() # end of "Qt Gui" section
qt_configure_add_summary_section(NAME "Features used by QPA backends")
@@ -1308,7 +1349,7 @@ qt_configure_end_summary_section() # end of "GL integrations" section
qt_configure_end_summary_section() # end of "XCB" section
qt_configure_add_summary_section(NAME "Windows")
qt_configure_add_summary_entry(ARGS "direct2d")
-qt_configure_add_summary_entry(ARGS "direct2d1_1") ### special case
+qt_configure_add_summary_entry(ARGS "direct2d1_1")
qt_configure_add_summary_entry(ARGS "directwrite")
qt_configure_add_summary_entry(ARGS "directwrite3")
qt_configure_end_summary_section() # end of "Windows" section
@@ -1329,14 +1370,9 @@ qt_configure_add_report_entry(
CONDITION QT_FEATURE_gui AND LINUX AND NOT ANDROID AND NOT QT_FEATURE_xcb AND NOT QT_FEATURE_eglfs AND NOT QT_FEATURE_directfb AND NOT QT_FEATURE_linuxfb
)
qt_configure_add_report_entry(
- TYPE WARNING
- MESSAGE "On OS X, AAT is supported only with -qt-harfbuzz."
- CONDITION APPLE AND QT_FEATURE_system_harfbuzz
-)
-qt_configure_add_report_entry(
TYPE ERROR
MESSAGE "The OpenGL functionality tests failed! You might need to modify the OpenGL package search path by setting the OpenGL_DIR CMake variable to the OpenGL library's installation directory."
- CONDITION QT_FEATURE_gui AND NOT WATCHOS AND ( NOT INPUT_opengl STREQUAL 'no' ) AND NOT QT_FEATURE_opengl_desktop AND NOT QT_FEATURE_opengles2 AND NOT QT_FEATURE_opengl_dynamic
+ CONDITION QT_FEATURE_gui AND NOT WATCHOS AND NOT VISIONOS AND ( NOT INPUT_opengl STREQUAL 'no' ) AND NOT QT_FEATURE_opengl_desktop AND NOT QT_FEATURE_opengles2 AND NOT QT_FEATURE_opengl_dynamic
)
qt_configure_add_report_entry(
TYPE WARNING
diff --git a/src/gui/doc/images/qpainter-concentriccircles.png b/src/gui/doc/images/qpainter-concentriccircles.png
index 4889dcd76d..d9489a4162 100644
--- a/src/gui/doc/images/qpainter-concentriccircles.png
+++ b/src/gui/doc/images/qpainter-concentriccircles.png
Binary files differ
diff --git a/src/gui/doc/includes/QtGuiDoc b/src/gui/doc/includes/QtGuiDoc
index 00d01afa9c..e8fa73786e 100644
--- a/src/gui/doc/includes/QtGuiDoc
+++ b/src/gui/doc/includes/QtGuiDoc
@@ -16,4 +16,8 @@
#include <QtGui/qpa/qplatformscreen_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qkeymapper_p.h>
-#include <QtGui/private/qwindowsmime_p.h>
+
+// rhi
+#include <QtGui/rhi/qrhi.h>
+#include <QtGui/rhi/qshader.h>
+#include <QtGui/rhi/qshaderdescription.h>
diff --git a/src/gui/doc/includes/windows.h b/src/gui/doc/includes/windows.h
index e900698255..b4824c2e93 100644
--- a/src/gui/doc/includes/windows.h
+++ b/src/gui/doc/includes/windows.h
@@ -15,7 +15,7 @@
//
// Dummy declarations for generating docs on non-Windows platforms
-#if !defined(Q_OS_WIN) && defined(Q_CLANG_QDOC)
+#if !defined(Q_OS_WIN) && defined(Q_QDOC)
typedef struct _FORMATETC {} FORMATETC;
typedef struct _STGMEDIUM {} STGMEDIUM;
typedef void *IDataObject;
diff --git a/src/gui/doc/qtgui.qdocconf b/src/gui/doc/qtgui.qdocconf
index 15e98ad385..b94f11849c 100644
--- a/src/gui/doc/qtgui.qdocconf
+++ b/src/gui/doc/qtgui.qdocconf
@@ -39,6 +39,7 @@ depends += \
qtdoc \
qmake \
qtcmake \
+ qtshadertools \
qttestlib \
qtplatformintegration \
qthelp
@@ -62,7 +63,7 @@ imagedirs += images \
excludefiles += ../kernel/qtestsupport_gui.cpp \
../painting/qdrawhelper_ssse3.cpp
-# manifestmeta.highlighted.names = "QtGui/Analog Clock Window Example"
+manifestmeta.highlighted.names = "QtGui/Hello Vulkan Cubes Example"
navigation.landingpage = "Qt GUI"
navigation.cppclassespage = "Qt GUI C++ Classes"
@@ -72,6 +73,7 @@ spurious += "Undocumented enum item '.*' in QGradient::Preset"
# \svgcolor {#ffdead}
macro.svgcolor.HTML = "<div style=\"padding:10px;color:#fff;background:\1;\"></div>"
+macro.svgcolor.DocBook = "<db:phrase role=\"color:\1\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</db:phrase>"
-# Fail the documentation build if there are more warnings than the limit
+# Enforce zero documentation warnings
warninglimit = 0
diff --git a/src/gui/doc/snippets/code/doc_src_richtext.qdoc b/src/gui/doc/snippets/code/doc_src_richtext.qdoc
index 39e29caa8d..0c69514210 100644
--- a/src/gui/doc/snippets/code/doc_src_richtext.qdoc
+++ b/src/gui/doc/snippets/code/doc_src_richtext.qdoc
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
//! [7]
<meta http-equiv="Content-Type" content="text/html; charset=EUC-JP" />
diff --git a/src/gui/doc/snippets/code/src_gui_image_qicon.cpp b/src/gui/doc/snippets/code/src_gui_image_qicon.cpp
index d446f16c3c..a7f27a7fdd 100644
--- a/src/gui/doc/snippets/code/src_gui_image_qicon.cpp
+++ b/src/gui/doc/snippets/code/src_gui_image_qicon.cpp
@@ -8,7 +8,7 @@ namespace src_gui_image_qicon {
struct MyWidget : public QWidget
{
- void drawIcon(QPainter *painter, QPoint pos);
+ void drawIcon(QPainter *painter, const QRect &rect);
bool isChecked() { return true; }
QIcon icon;
};
@@ -17,9 +17,13 @@ void wrapper0() {
//! [0]
QToolButton *button = new QToolButton;
-button->setIcon(QIcon("open.xpm"));
+button->setIcon(QIcon("open.png"));
//! [0]
+//! [addFile]
+QIcon openIcon("open.png");
+openIcon.addFile("open-disabled.png", QIcon::Disabled);
+//! [addFile]
//! [1]
button->setIcon(QIcon());
@@ -29,29 +33,27 @@ button->setIcon(QIcon());
//! [2]
-void MyWidget::drawIcon(QPainter *painter, QPoint pos)
+void MyWidget::drawIcon(QPainter *painter, const QRect &rect)
{
- QPixmap pixmap = icon.pixmap(QSize(22, 22),
- isEnabled() ? QIcon::Normal
- : QIcon::Disabled,
- isChecked() ? QIcon::On
- : QIcon::Off);
- painter->drawPixmap(pos, pixmap);
+ icon.paint(painter, rect, Qt::AlignCenter, isEnabled() ? QIcon::Normal
+ : QIcon::Disabled,
+ isChecked() ? QIcon::On
+ : QIcon::Off);
}
//! [2]
void wrapper1() {
-//! [3]
-QIcon undoicon = QIcon::fromTheme("edit-undo");
-//! [3]
+//! [fromTheme]
+QIcon undoicon = QIcon::fromTheme(QIcon::ThemeIcon::EditUndo);
+//! [fromTheme]
} // wrapper1
//! [4]
-QIcon undoicon = QIcon::fromTheme("edit-undo", QIcon(":/undo.png"));
+QIcon undoicon = QIcon::fromTheme(QIcon::ThemeIcon::EditUndo, QIcon(":/undo.png"));
//! [4]
diff --git a/src/gui/doc/snippets/code/src_gui_kernel_qapplication.cpp b/src/gui/doc/snippets/code/src_gui_kernel_qapplication.cpp
index ab8521b465..9455cacf1c 100644
--- a/src/gui/doc/snippets/code/src_gui_kernel_qapplication.cpp
+++ b/src/gui/doc/snippets/code/src_gui_kernel_qapplication.cpp
@@ -19,70 +19,6 @@ struct MyWidget
int manhattanLength() { return 0; }
};
-
-//! [0]
-QCoreApplication *createApplication(int &argc, char *argv[])
-{
- for (int i = 1; i < argc; ++i)
- if (!qstrcmp(argv[i], "-no-gui"))
- return new QCoreApplication(argc, argv);
- return new QApplication(argc, argv);
-}
-
-int main(int argc, char *argv[])
-{
- QScopedPointer<QCoreApplication> app(createApplication(argc, argv));
-
- if (qobject_cast<QApplication *>(app.data())) {
- // start GUI version...
- } else {
- // start non-GUI version...
- }
-
- return app->exec();
-}
-//! [0]
-
-
-void wrapper0() {
-
-//! [1]
-QApplication::setStyle(QStyleFactory::create("fusion"));
-//! [1]
-
-} // wrapper0
-
-
-//! [3]
-QSize MyWidget::sizeHint() const
-{
- return QSize(80, 25);
-}
-//! [3]
-
-
-//! [4]
-void showAllHiddenTopLevelWidgets()
-{
- const auto topLevelWidgets = QApplication::topLevelWidgets();
- for (QWidget *widget : topLevelWidgets) {
- if (widget->isHidden())
- widget->show();
- }
-}
-//! [4]
-
-
-//! [5]
-void updateAllWidgets()
-{
- const auto topLevelWidgets = QApplication::topLevelWidgets();
- for (QWidget *widget : topLevelWidgets)
- widget->update();
-}
-//! [5]
-
-
void startTheDrag() {};
void wrapper1() {
MyWidget startPos;
@@ -96,25 +32,6 @@ if ((startPos - currentPos).manhattanLength() >=
startTheDrag();
//! [6]
-
-//! [7]
-QWidget *widget = qApp->widgetAt(x, y);
-if (widget)
- widget = widget->window();
-//! [7]
-
} // wrapper1
-
-void wrapper2() {
-QPoint point;
-
-//! [8]
-QWidget *widget = qApp->widgetAt(point);
-if (widget)
- widget = widget->window();
-//! [8]
-
-
-} // wrapper2
} // src_gui_kernel_qapplication
diff --git a/src/gui/doc/snippets/code/src_gui_kernel_qguiapplication.cpp b/src/gui/doc/snippets/code/src_gui_kernel_qguiapplication.cpp
index e9a2446b91..347b47403e 100644
--- a/src/gui/doc/snippets/code/src_gui_kernel_qguiapplication.cpp
+++ b/src/gui/doc/snippets/code/src_gui_kernel_qguiapplication.cpp
@@ -74,26 +74,4 @@ appname -session id
*/ // wrap snippet 2
-
-void wrapper0() {
-
-
-//! [3]
-const QStringList commands = mySession.restartCommand();
-for (const QString &command : commands)
- do_something(command);
-//! [3]
-
-} // wrapper0
-
-
-void wrapper1() {
-//! [4]
-const QStringList commands = mySession.discardCommand();
-for (const QString &command : mySession.discardCommand())
- do_something(command);
-//! [4]
-
-
-} // wrapper1
} // src_gui_kernel_qguiapplication
diff --git a/src/gui/doc/snippets/code/src_gui_kernel_qkeysequence.cpp b/src/gui/doc/snippets/code/src_gui_kernel_qkeysequence.cpp
index 0e4c56d8a4..0f335dd0a0 100644
--- a/src/gui/doc/snippets/code/src_gui_kernel_qkeysequence.cpp
+++ b/src/gui/doc/snippets/code/src_gui_kernel_qkeysequence.cpp
@@ -32,7 +32,7 @@ void Wrapper::wrapper() {
//! [2]
QMenu *file = new QMenu(this);
file->addAction(tr("&Open..."), QKeySequence(tr("Ctrl+O", "File|Open")),
- this, SLOT(open()));
+ this, &MainWindow::open);
//! [2]
} // Wrapper::wrapper
diff --git a/src/gui/doc/snippets/code/src_gui_painting_qpainter.cpp b/src/gui/doc/snippets/code/src_gui_painting_qpainter.cpp
index 17436c9de5..cfbf3e44a2 100644
--- a/src/gui/doc/snippets/code/src_gui_painting_qpainter.cpp
+++ b/src/gui/doc/snippets/code/src_gui_painting_qpainter.cpp
@@ -63,6 +63,7 @@ struct MyWidget : public QWidget
void wrapper13();
void wrapper14();
void wrapper15();
+ void concentricCircles();
};
QLine drawingCode;
@@ -97,15 +98,6 @@ struct QPainter {
void setWorldTransform(QTransform matrix, bool);
};
-//! [4]
-void QPainter::rotate(qreal angle)
-{
- QTransform matrix;
- matrix.rotate(angle);
- setWorldTransform(matrix, true);
-}
-//! [4]
-
} // QPainterWrapper
void MyWidget::wrapper1() {
@@ -123,7 +115,7 @@ painter.drawPath(path);
//! [6]
QLineF line(10.0, 80.0, 90.0, 20.0);
-QPainter(this);
+QPainter painter(this);
painter.drawLine(line);
//! [6]
} // MyWidget::wrapper1()
@@ -260,7 +252,7 @@ QRectF target(10.0, 20.0, 80.0, 60.0);
QRectF source(0.0, 0.0, 70.0, 40.0);
QPixmap pixmap(":myPixmap.png");
-QPainter(this);
+QPainter painter(this);
painter.drawPixmap(target, pixmap, source);
//! [16]
@@ -364,4 +356,21 @@ painter.drawRect(rectangle.adjusted(0, 0, -pen.width(), -pen.width()));
} // MyWidget::wrapper15
-} // src_gui_painting_qpainter2
+
+void MyWidget::concentricCircles()
+{
+//! [renderHint]
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing, true);
+//! [renderHint]
+ int diameter = 50;
+//! [floatBased]
+ painter.drawEllipse(QRectF(-diameter / 2.0, -diameter / 2.0, diameter, diameter));
+//! [floatBased]
+//! [intBased]
+ painter.drawEllipse(QRect(-diameter / 2, -diameter / 2, diameter, diameter));
+//! [intBased]
+
+} // MyWidget::concentricCircles
+
+} // src_gui_painting_qpainter2 \ No newline at end of file
diff --git a/src/gui/doc/snippets/code/src_gui_text_qfontmetrics.cpp b/src/gui/doc/snippets/code/src_gui_text_qfontmetrics.cpp
index 33a82f8dd1..4650ad8d8f 100644
--- a/src/gui/doc/snippets/code/src_gui_text_qfontmetrics.cpp
+++ b/src/gui/doc/snippets/code/src_gui_text_qfontmetrics.cpp
@@ -9,7 +9,7 @@ void wrapper0() {
//! [0]
QFont font("times", 24);
QFontMetrics fm(font);
-int pixelsWide = fm.horizontalAdvance("What's the width of this text?");
+int pixelsWide = fm.horizontalAdvance("What's the advance width of this text?");
int pixelsHigh = fm.height();
//! [0]
@@ -22,7 +22,7 @@ void wrapper1() {
//! [1]
QFont font("times", 24);
QFontMetricsF fm(font);
-qreal pixelsWide = fm.horizontalAdvance("What's the width of this text?");
+qreal pixelsWide = fm.horizontalAdvance("What's the advance width of this text?");
qreal pixelsHigh = fm.height();
//! [1]
diff --git a/src/gui/doc/snippets/code/src_gui_text_qtextdocument.cpp b/src/gui/doc/snippets/code/src_gui_text_qtextdocument.cpp
deleted file mode 100644
index 570728f41d..0000000000
--- a/src/gui/doc/snippets/code/src_gui_text_qtextdocument.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-namespace src_gui_text_qtextdocument {
-
-/* wrap non-code snippet
-
-//! [0]
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body>...
-//! [0]
-
-*/ // wrap non-code snippet
-} // src_gui_text_qtextdocument
diff --git a/src/gui/doc/snippets/code/src_gui_text_qtextlayout.cpp b/src/gui/doc/snippets/code/src_gui_text_qtextlayout.cpp
index 2602c2ced0..70ec6b01ea 100644
--- a/src/gui/doc/snippets/code/src_gui_text_qtextlayout.cpp
+++ b/src/gui/doc/snippets/code/src_gui_text_qtextlayout.cpp
@@ -10,6 +10,7 @@ namespace src_gui_text_qtextlayout {
struct Wrapper : public QPaintDevice
{
void wrapper1();
+ void elided();
};
QTextLayout textLayout;
@@ -24,7 +25,7 @@ int leading = fontMetrics.leading();
qreal height = 0;
textLayout.setCacheEnabled(true);
textLayout.beginLayout();
-while (1) {
+while (true) {
QTextLine line = textLayout.createLine();
if (!line.isValid())
break;
@@ -49,4 +50,41 @@ textLayout.draw(&painter, QPoint(0, 0));
} // Wrapper::wrapper1
+void Wrapper::elided() {
+
+QString content;
+
+//! [elided]
+QPainter painter(this);
+QFontMetrics fontMetrics = painter.fontMetrics();
+
+int lineSpacing = fontMetrics.lineSpacing();
+int y = 0;
+
+QTextLayout textLayout(content, painter.font());
+textLayout.beginLayout();
+while (true) {
+ QTextLine line = textLayout.createLine();
+
+ if (!line.isValid())
+ break;
+
+ line.setLineWidth(width());
+ const int nextLineY = y + lineSpacing;
+
+ if (height() >= nextLineY + lineSpacing) {
+ line.draw(&painter, QPoint(0, y));
+ y = nextLineY;
+ } else {
+ const QString lastLine = content.mid(line.textStart());
+ const QString elidedLastLine = fontMetrics.elidedText(lastLine, Qt::ElideRight, width());
+ painter.drawText(QPoint(0, y + fontMetrics.ascent()), elidedLastLine);
+ line = textLayout.createLine();
+ break;
+ }
+}
+textLayout.endLayout();
+//! [elided]
+}
+
} // src_gui_text_qtextlayout
diff --git a/src/gui/doc/snippets/code/src_gui_util_qdesktopservices.cpp b/src/gui/doc/snippets/code/src_gui_util_qdesktopservices.cpp
index 96c8040eb2..13deb88bc8 100644
--- a/src/gui/doc/snippets/code/src_gui_util_qdesktopservices.cpp
+++ b/src/gui/doc/snippets/code/src_gui_util_qdesktopservices.cpp
@@ -65,15 +65,6 @@ QDesktopServices::openUrl(QUrl("file:///C:/Program Files", QUrl::TolerantMode));
*/ // comment wrapper 2
-
-void wrapper3() {
-//! [6]
-QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +
- "/data/organization/application";
-//! [6]
-} // wrapper3
-
-
/* comment wrapper 3
//! [7]
<key>com.apple.developer.associated-domains</key>
diff --git a/src/gui/doc/snippets/code/src_gui_vulkan_qvulkanfunctions.cpp b/src/gui/doc/snippets/code/src_gui_vulkan_qvulkanfunctions.cpp
index a83c6eb7f9..700d933f43 100644
--- a/src/gui/doc/snippets/code/src_gui_vulkan_qvulkanfunctions.cpp
+++ b/src/gui/doc/snippets/code/src_gui_vulkan_qvulkanfunctions.cpp
@@ -8,20 +8,18 @@
namespace src_gui_vulkan_qvulkanfunctions {
struct Window {
- void render();
+ void init();
QVulkanInstance *vulkanInstance() { return nullptr; }
};
-VkDevice_T *device = nullptr;
-VkCommandBufferAllocateInfo cmdBufInfo;
-VkCommandBuffer cmdBuf;
//! [0]
-void Window::render()
+void Window::init()
{
QVulkanInstance *inst = vulkanInstance();
QVulkanFunctions *f = inst->functions();
// ...
- VkResult err = f->vkAllocateCommandBuffers(device, &cmdBufInfo, &cmdBuf);
+ uint32_t count = 0;
+ VkResult err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &count, nullptr);
// ...
}
//! [0]
diff --git a/src/gui/doc/snippets/image/image.cpp b/src/gui/doc/snippets/image/image.cpp
index b1c42d62da..82703c5c0f 100644
--- a/src/gui/doc/snippets/image/image.cpp
+++ b/src/gui/doc/snippets/image/image.cpp
@@ -25,13 +25,6 @@ buffer.open(QIODevice::WriteOnly);
pixmap.save(&buffer, "PNG"); // writes pixmap into bytes in PNG format
//! [1]
-
-//! [2]
-QPixmap alpha("image-with-alpha.png");
-QPixmap alphacopy = alpha;
-alphacopy.setMask(alphacopy.mask());
-//! [2]
-
} // wrapper1
} // image
diff --git a/src/gui/doc/snippets/qfileopenevent/main.cpp b/src/gui/doc/snippets/qfileopenevent/main.cpp
index b733bfc320..a94ff58137 100644
--- a/src/gui/doc/snippets/qfileopenevent/main.cpp
+++ b/src/gui/doc/snippets/qfileopenevent/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2016 Samuel Gaist <samuel.gaist@edeltech.ch>
+// Copyright (C) 2023 Samuel Gaist <samuel.gaist@edeltech.ch>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [QApplication subclass]
@@ -19,7 +19,15 @@ public:
{
if (event->type() == QEvent::FileOpen) {
QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event);
- qDebug() << "Open file" << openEvent->file();
+ const QUrl url = openEvent->url();
+ if (url.isLocalFile()) {
+ QFile localFile(url.toLocalFile());
+ // read from local file
+ } else if (url.isValid()) {
+ // process according to the URL's schema
+ } else {
+ // parse openEvent->file()
+ }
}
return QApplication::event(event);
diff --git a/src/gui/doc/snippets/rhioffscreen/color.frag b/src/gui/doc/snippets/rhioffscreen/color.frag
new file mode 100644
index 0000000000..ad9d953d02
--- /dev/null
+++ b/src/gui/doc/snippets/rhioffscreen/color.frag
@@ -0,0 +1,16 @@
+//! [0]
+#version 440
+
+layout(location = 0) in vec3 v_color;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ float opacity;
+};
+
+void main()
+{
+ fragColor = vec4(v_color * opacity, opacity);
+}
+//! [0]
diff --git a/src/gui/doc/snippets/rhioffscreen/color.vert b/src/gui/doc/snippets/rhioffscreen/color.vert
new file mode 100644
index 0000000000..0010e55561
--- /dev/null
+++ b/src/gui/doc/snippets/rhioffscreen/color.vert
@@ -0,0 +1,18 @@
+//! [0]
+#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;
+};
+
+void main()
+{
+ v_color = color;
+ gl_Position = mvp * position;
+}
+//! [0]
diff --git a/src/gui/doc/snippets/rhioffscreen/main.cpp b/src/gui/doc/snippets/rhioffscreen/main.cpp
new file mode 100644
index 0000000000..c2c6f74dc1
--- /dev/null
+++ b/src/gui/doc/snippets/rhioffscreen/main.cpp
@@ -0,0 +1,151 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [0]
+#include <QGuiApplication>
+#include <QImage>
+#include <QFile>
+#include <rhi/qrhi.h>
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+#if QT_CONFIG(vulkan)
+ QVulkanInstance inst;
+#endif
+ std::unique_ptr<QRhi> rhi;
+#if defined(Q_OS_WIN)
+ QRhiD3D12InitParams params;
+ rhi.reset(QRhi::create(QRhi::D3D12, &params));
+#elif QT_CONFIG(metal)
+ QRhiMetalInitParams params;
+ rhi.reset(QRhi::create(QRhi::Metal, &params));
+#elif QT_CONFIG(vulkan)
+ inst.setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
+ if (inst.create()) {
+ QRhiVulkanInitParams params;
+ params.inst = &inst;
+ rhi.reset(QRhi::create(QRhi::Vulkan, &params));
+ } else {
+ qFatal("Failed to create Vulkan instance");
+ }
+#endif
+ if (rhi)
+ qDebug() << rhi->backendName() << rhi->driverInfo();
+ else
+ qFatal("Failed to initialize RHI");
+
+ float rotation = 0.0f;
+ float opacity = 1.0f;
+ int opacityDir = 1;
+
+ std::unique_ptr<QRhiTexture> tex(rhi->newTexture(QRhiTexture::RGBA8,
+ QSize(1280, 720),
+ 1,
+ QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
+ tex->create();
+ std::unique_ptr<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ tex.get() }));
+ std::unique_ptr<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
+ rt->setRenderPassDescriptor(rp.get());
+ rt->create();
+
+ QMatrix4x4 viewProjection = rhi->clipSpaceCorrMatrix();
+ viewProjection.perspective(45.0f, 1280 / 720.f, 0.01f, 1000.0f);
+ viewProjection.translate(0, 0, -4);
+
+ static float vertexData[] = { // Y up, CCW
+ 0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
+ -0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
+ 0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
+ };
+
+ std::unique_ptr<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable,
+ QRhiBuffer::VertexBuffer,
+ sizeof(vertexData)));
+ vbuf->create();
+
+ std::unique_ptr<QRhiBuffer> ubuf(rhi->newBuffer(QRhiBuffer::Dynamic,
+ QRhiBuffer::UniformBuffer,
+ 64 + 4));
+ ubuf->create();
+
+ std::unique_ptr<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
+ srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0,
+ QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
+ ubuf.get())
+ });
+ srb->create();
+
+ std::unique_ptr<QRhiGraphicsPipeline> ps(rhi->newGraphicsPipeline());
+ QRhiGraphicsPipeline::TargetBlend premulAlphaBlend;
+ premulAlphaBlend.enable = true;
+ ps->setTargetBlends({ premulAlphaBlend });
+ static auto getShader = [](const QString &name) {
+ QFile f(name);
+ return f.open(QIODevice::ReadOnly) ? QShader::fromSerialized(f.readAll()) : QShader();
+ };
+ ps->setShaderStages({
+ { QRhiShaderStage::Vertex, getShader(QLatin1String("color.vert.qsb")) },
+ { QRhiShaderStage::Fragment, getShader(QLatin1String("color.frag.qsb")) }
+ });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 5 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
+ });
+ ps->setVertexInputLayout(inputLayout);
+ ps->setShaderResourceBindings(srb.get());
+ ps->setRenderPassDescriptor(rp.get());
+ ps->create();
+
+ QRhiCommandBuffer *cb;
+ for (int frame = 0; frame < 20; ++frame) {
+ rhi->beginOffscreenFrame(&cb);
+
+ QRhiResourceUpdateBatch *u = rhi->nextResourceUpdateBatch();
+ if (frame == 0)
+ u->uploadStaticBuffer(vbuf.get(), vertexData);
+
+ QMatrix4x4 mvp = viewProjection;
+ mvp.rotate(rotation, 0, 1, 0);
+ u->updateDynamicBuffer(ubuf.get(), 0, 64, mvp.constData());
+ rotation += 5.0f;
+
+ u->updateDynamicBuffer(ubuf.get(), 64, 4, &opacity);
+ opacity += opacityDir * 0.2f;
+ if (opacity < 0.0f || opacity > 1.0f) {
+ opacityDir *= -1;
+ opacity = qBound(0.0f, opacity, 1.0f);
+ }
+
+ cb->beginPass(rt.get(), Qt::green, { 1.0f, 0 }, u);
+ cb->setGraphicsPipeline(ps.get());
+ cb->setViewport({ 0, 0, 1280, 720 });
+ cb->setShaderResources();
+ const QRhiCommandBuffer::VertexInput vbufBinding(vbuf.get(), 0);
+ cb->setVertexInput(0, 1, &vbufBinding);
+ cb->draw(3);
+ QRhiReadbackResult readbackResult;
+ u = rhi->nextResourceUpdateBatch();
+ u->readBackTexture({ tex.get() }, &readbackResult);
+ cb->endPass(u);
+
+ rhi->endOffscreenFrame();
+
+ QImage image(reinterpret_cast<const uchar *>(readbackResult.data.constData()),
+ readbackResult.pixelSize.width(),
+ readbackResult.pixelSize.height(),
+ QImage::Format_RGBA8888_Premultiplied);
+ if (rhi->isYUpInFramebuffer())
+ image = image.mirrored();
+ image.save(QString::asprintf("frame%d.png", frame));
+ }
+
+ return 0;
+}
+//! [0]
diff --git a/src/gui/doc/snippets/separations/finalwidget.cpp b/src/gui/doc/snippets/separations/finalwidget.cpp
index a3d60cfc83..3c308d9df8 100644
--- a/src/gui/doc/snippets/separations/finalwidget.cpp
+++ b/src/gui/doc/snippets/separations/finalwidget.cpp
@@ -28,10 +28,9 @@ FinalWidget::FinalWidget(QWidget *parent, const QString &name,
imageLabel->setMinimumSize(labelSize);
nameLabel = new QLabel(name);
- QVBoxLayout *layout = new QVBoxLayout;
+ QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(imageLabel, 1);
layout->addWidget(nameLabel, 0);
- setLayout(layout);
}
/*!
diff --git a/src/gui/doc/snippets/separations/screenwidget.cpp b/src/gui/doc/snippets/separations/screenwidget.cpp
index 87a6c9e605..223de95962 100644
--- a/src/gui/doc/snippets/separations/screenwidget.cpp
+++ b/src/gui/doc/snippets/separations/screenwidget.cpp
@@ -61,12 +61,11 @@ ScreenWidget::ScreenWidget(QWidget *parent, QColor initialColor,
connect(colorButton, &QPushButton::clicked, this, &ScreenWidget::setColor);
connect(invertButton, &QPushButton::clicked, this, &ScreenWidget::invertImage);
- QGridLayout *gridLayout = new QGridLayout;
+ QGridLayout *gridLayout = new QGridLayout(this);
gridLayout->addWidget(imageLabel, 0, 0, 1, 2);
gridLayout->addWidget(nameLabel, 1, 0);
gridLayout->addWidget(colorButton, 1, 1);
gridLayout->addWidget(invertButton, 2, 1, 1, 1);
- setLayout(gridLayout);
}
/*!
diff --git a/src/gui/doc/snippets/separations/separations.qdoc b/src/gui/doc/snippets/separations/separations.qdoc
index ad670f305a..ee567030bb 100644
--- a/src/gui/doc/snippets/separations/separations.qdoc
+++ b/src/gui/doc/snippets/separations/separations.qdoc
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*
\example painting/separations
diff --git a/src/gui/doc/snippets/textdocument-listitems/mainwindow.cpp b/src/gui/doc/snippets/textdocument-listitems/mainwindow.cpp
index 2bf6e4a018..324f1937d9 100644
--- a/src/gui/doc/snippets/textdocument-listitems/mainwindow.cpp
+++ b/src/gui/doc/snippets/textdocument-listitems/mainwindow.cpp
@@ -13,17 +13,17 @@ MainWindow::MainWindow()
QMenu *fileMenu = new QMenu(tr("&File"));
fileMenu->addAction(tr("E&xit"), QKeySequence(tr("Ctrl+Q", "File|Exit")),
- this, SLOT(close()));
+ this, &QWidget::close);
QMenu *actionsMenu = new QMenu(tr("&Actions"));
actionsMenu->addAction(tr("&Highlight List Items"),
- this, SLOT(highlightListItems()));
- actionsMenu->addAction(tr("&Show Current List"), this, SLOT(showList()));
+ this, &MainWindow::highlightListItems);
+ actionsMenu->addAction(tr("&Show Current List"), this, &MainWindow::showList);
QMenu *insertMenu = new QMenu(tr("&Insert"));
insertMenu->addAction(tr("&List"), QKeySequence(tr("Ctrl+L", "Insert|List")),
- this, SLOT(insertList()));
+ this, &MainWindow::insertList);
menuBar()->addMenu(fileMenu);
menuBar()->addMenu(insertMenu);
diff --git a/src/gui/doc/snippets/textdocument-selections/mainwindow.cpp b/src/gui/doc/snippets/textdocument-selections/mainwindow.cpp
index eefa0aa841..6e1d051d4b 100644
--- a/src/gui/doc/snippets/textdocument-selections/mainwindow.cpp
+++ b/src/gui/doc/snippets/textdocument-selections/mainwindow.cpp
@@ -9,30 +9,30 @@ MainWindow::MainWindow()
QMenu *fileMenu = new QMenu(tr("&File"));
fileMenu->addAction(tr("&Open..."), QKeySequence(tr("Ctrl+O", "File|Open")),
- this, SLOT(openFile()));
+ this, &MainWindow::openFile);
- QAction *quitAction = fileMenu->addAction(tr("E&xit"), this, SLOT(close()));
+ QAction *quitAction = fileMenu->addAction(tr("E&xit"), this, &MainWindow::close);
quitAction->setShortcut(tr("Ctrl+Q"));
QMenu *editMenu = new QMenu(tr("&Edit"));
- cutAction = editMenu->addAction(tr("Cu&t"), this, SLOT(cutSelection()));
+ cutAction = editMenu->addAction(tr("Cu&t"), this, &MainWindow::cutSelection);
cutAction->setShortcut(tr("Ctrl+X"));
cutAction->setEnabled(false);
- copyAction = editMenu->addAction(tr("&Copy"), this, SLOT(copySelection()));
+ copyAction = editMenu->addAction(tr("&Copy"), this, &MainWindow::copySelection);
copyAction->setShortcut(tr("Ctrl+C"));
copyAction->setEnabled(false);
- pasteAction = editMenu->addAction(tr("&Paste"), this, SLOT(pasteSelection()));
+ pasteAction = editMenu->addAction(tr("&Paste"), this, &MainWindow::pasteSelection);
pasteAction->setShortcut(tr("Ctrl+V"));
pasteAction->setEnabled(false);
QMenu *selectMenu = new QMenu(tr("&Select"));
- selectMenu->addAction(tr("&Word"), this, SLOT(selectWord()));
- selectMenu->addAction(tr("&Line"), this, SLOT(selectLine()));
- selectMenu->addAction(tr("&Block"), this, SLOT(selectBlock()));
- selectMenu->addAction(tr("&Frame"), this, SLOT(selectFrame()));
+ selectMenu->addAction(tr("&Word"), this, &MainWindow::selectWord);
+ selectMenu->addAction(tr("&Line"), this, &MainWindow::selectLine);
+ selectMenu->addAction(tr("&Block"), this, &MainWindow::selectBlock);
+ selectMenu->addAction(tr("&Frame"), this, &MainWindow::selectFrame);
menuBar()->addMenu(fileMenu);
menuBar()->addMenu(editMenu);
diff --git a/src/gui/doc/snippets/transform/main.cpp b/src/gui/doc/snippets/transform/main.cpp
index 56d6780269..cedffe218c 100644
--- a/src/gui/doc/snippets/transform/main.cpp
+++ b/src/gui/doc/snippets/transform/main.cpp
@@ -91,11 +91,10 @@ int main(int argc, char **argv)
CombinedTransformation *combinedWidget = new CombinedTransformation;
BasicOperations *basicWidget = new BasicOperations;
- QVBoxLayout *layout = new QVBoxLayout;
+ QVBoxLayout *layout = new QVBoxLayout(&widget);
layout->addWidget(simpleWidget);
layout->addWidget(combinedWidget);
layout->addWidget(basicWidget);
- widget.setLayout(layout);
widget.show();
widget.resize(130, 350);
diff --git a/src/gui/doc/src/coordsys.qdoc b/src/gui/doc/src/coordsys.qdoc
index eb39239e37..087916635b 100644
--- a/src/gui/doc/src/coordsys.qdoc
+++ b/src/gui/doc/src/coordsys.qdoc
@@ -213,11 +213,11 @@
\row
\li {2,1}
- \snippet analogclock/main.cpp 1
+ \snippet ../widgets/widgets/analogclock/analogclock.cpp 9
We translate the coordinate system so that point (0, 0) is in the
widget's center, instead of being at the top-left corner. We also
- scale the system by \c side / 100, where \c side is either the
+ scale the system by \c side / 200, where \c side is either the
widget's width or the height, whichever is shortest. We want the
clock to be square, even if the device isn't.
@@ -227,7 +227,7 @@
See also the \l {Window-Viewport Conversion} section.
- \snippet analogclock/main.cpp 2
+ \snippet ../widgets/widgets/analogclock/analogclock.cpp 18
We draw the clock's hour hand by rotating the coordinate system
and calling QPainter::drawConvexPolygon(). Thank's to the
@@ -235,26 +235,35 @@
The polygon is specified as an array of alternating \e x, \e y
values, stored in the \c hourHand static variable (defined at the
- beginning of the function), which corresponds to the four points
- (2, 0), (0, 2), (-2, 0), and (0, -25).
+ beginning of the function), which corresponds to the three points
+ (7, 8), (-7, 8), (0, -40).
The calls to QPainter::save() and QPainter::restore() surrounding
the code guarantees that the code that follows won't be disturbed
by the transformations we've used.
- \snippet analogclock/main.cpp 3
+ \snippet ../widgets/widgets/analogclock/analogclock.cpp 21
+
+ After that, we draw the hour markers for the clock face, which
+ consists of twelve short lines at 30-degree intervals. When that
+ loop is done, the painter has been rotated a full circle back to
+ its original state, so we don't need to save and restore the state.
+
+ \snippet ../widgets/widgets/analogclock/analogclock.cpp 24
We do the same for the clock's minute hand, which is defined by
- the four points (1, 0), (0, 1), (-1, 0), and (0, -40). These
+ the three points (7, 8), (-7, 8), (0, -70). These
coordinates specify a hand that is thinner and longer than the
minute hand.
- \snippet analogclock/main.cpp 4
+ \snippet ../widgets/widgets/analogclock/analogclock.cpp 27
- Finally, we draw the clock face, which consists of twelve short
- lines at 30-degree intervals. At the end of that, the painter is
- rotated in a way which isn't very useful, but we're done with
- painting so that doesn't matter.
+ Finally, we draw the minute markers for the clock face, which
+ consists of sixty short lines at 6-degree intervals. We skip every
+ fifth minute marker because we don't want to draw over the hour
+ markers. At the end of that, the painter is rotated in a way which
+ isn't very useful, but we're done with painting so that doesn't
+ matter.
\endtable
For more information about the transformation matrix, see the
@@ -422,5 +431,5 @@
\endtable
\endomit
- \sa {Analog Clock Window Example}
+ \sa {Analog Clock}
*/
diff --git a/src/gui/doc/src/dnd.qdoc b/src/gui/doc/src/dnd.qdoc
index a8c5b8437c..7a756b304e 100644
--- a/src/gui/doc/src/dnd.qdoc
+++ b/src/gui/doc/src/dnd.qdoc
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
+ \keyword Drag and Drop in Qt
\page dnd.html
\title Drag and Drop
\brief An overview of the drag and drop system provided by Qt.
@@ -334,7 +335,9 @@
For example, we can copy the contents of a QLineEdit to the clipboard
with the following code:
- \snippet ../widgets/widgets/charactermap/mainwindow.cpp 11
+ \code
+ QGuiApplication::clipboard()->setText(lineEdit->text(), QClipboard::Clipboard);
+ \endcode
Data with different MIME types can also be put on the clipboard.
Construct a QMimeData object and set data with setData() function in
@@ -363,8 +366,6 @@
\li \l{draganddrop/draggableicons}{Draggable Icons}
\li \l{draganddrop/draggabletext}{Draggable Text}
\li \l{draganddrop/dropsite}{Drop Site}
- \li \l{draganddrop/fridgemagnets}{Fridge Magnets}
- \li \l{draganddrop/puzzle}{Drag and Drop Puzzle}
\endlist
\section1 Interoperating with Other Applications
@@ -375,11 +376,10 @@
Cocoa Drag Manager. On X11, XDND uses MIME, so no translation is necessary.
The Qt API is the same regardless of the platform. On Windows, MIME-aware
applications can communicate by using clipboard format names that are MIME
- types. Already some Windows applications use MIME naming conventions for
+ types. Some Windows applications already use MIME naming conventions for
their clipboard formats.
Custom classes for translating proprietary clipboard formats can be
- registered by reimplementing QWinMime on Windows or
- QMacPasteboardMime on \macos.
-
+ registered by reimplementing QWindowsMimeConverter on Windows or
+ QUtiMimeConverter on \macos.
*/
diff --git a/src/gui/doc/src/external-resources.qdoc b/src/gui/doc/src/external-resources.qdoc
index a02a9ddbf9..16bca2475d 100644
--- a/src/gui/doc/src/external-resources.qdoc
+++ b/src/gui/doc/src/external-resources.qdoc
@@ -8,7 +8,7 @@
*/
/*!
- \externalpage http://www.linux-foundation.org/en/Accessibility/IAccessible2
+ \externalpage https://accessibility.linuxfoundation.org/a11yspecs/ia2/docs/html/index.html
\title IAccessible2 Specification
*/
@@ -29,15 +29,24 @@
/*!
\externalpage https://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
- \title Icon Theme Specification
+ \title Freedesktop Icon Theme Specification
*/
/*!
+ \externalpage https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
+ \title Freedesktop Icon Naming Specification
+*/
+/*!
\externalpage https://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#directory_layout
\title Icon Theme Specification - Directory Layout
*/
/*!
+ \externalpage https://freedesktop.org/
+ \title Freedesktop
+*/
+
+/*!
\externalpage https://www.khronos.org/vulkan/
\title Vulkan
*/
@@ -46,3 +55,13 @@
\externalpage https://www.lunarg.com/vulkan-sdk/
\title LunarG Vulkan SDK
*/
+
+/*!
+ \externalpage https://developer.android.com/reference/androidx/core/content/FileProvider
+ \title Android: FileProvider
+*/
+
+/*!
+ \externalpage https://developer.android.com/training/secure-file-sharing/setup-sharing.html
+ \title Android: Setting up file sharing
+*/
diff --git a/src/gui/doc/src/includes/qt-colors.qdocinc b/src/gui/doc/src/includes/qt-colors.qdocinc
index 4c082323b6..d98c0a908a 100644
--- a/src/gui/doc/src/includes/qt-colors.qdocinc
+++ b/src/gui/doc/src/includes/qt-colors.qdocinc
@@ -3,84 +3,105 @@
\li Name
\li Hex
\li Color
+
\row
\li Color0
\li #000000
\li \svgcolor {#000000}
+
\row
\li Color1
\li #ffffff
\li \svgcolor {#ffffff}
+
\row
\li Black
\li #000000
\li \svgcolor {#000000}
+
\row
\li White
\li #ffffff
\li \svgcolor {#ffffff}
+
\row
\li DarkGray
\li #808080
\li \svgcolor {#808080}
+
\row
\li Gray
\li #a0a0a4
\li \svgcolor {#a0a0a4}
+
\row
\li LightGray
\li #c0c0c0
\li \svgcolor {#c0c0c0}
+
\row
\li Red
\li #ff0000
\li \svgcolor {#ff0000}
+
\row
\li Green
\li #00ff00
\li \svgcolor {#00ff00}
+
\row
\li Blue
\li #0000ff
\li \svgcolor {#0000ff}
+
\row
\li Cyan
\li #00ffff
\li \svgcolor {#00ffff}
+
\row
\li Magenta
\li #ff00ff
\li \svgcolor {#ff00ff}
+
\row
\li Yellow
\li #ffff00
\li \svgcolor {#ffff00}
+
\row
\li DarkRed
\li #800000
\li \svgcolor {#800000}
+
\row
\li DarkGreen
\li #008000
\li \svgcolor {#008000}
+
\row
\li DarkBlue
\li #000080
\li \svgcolor {#000080}
+
\row
\li DarkCyan
\li #008080
\li \svgcolor {#008080}
+
\row
\li DarkMagenta
\li #800080
\li \svgcolor {#800080}
+
\row
\li DarkYellow
\li #808000
\li \svgcolor {#808000}
+
\row
\li Transparent
\li #00000000
\li (transparent)
+
\endtable
diff --git a/src/gui/doc/src/includes/svg-colors.qdocinc b/src/gui/doc/src/includes/svg-colors.qdocinc
index 4e5fb56d5e..c28cbabbf7 100644
--- a/src/gui/doc/src/includes/svg-colors.qdocinc
+++ b/src/gui/doc/src/includes/svg-colors.qdocinc
@@ -3,592 +3,740 @@
\li Name
\li Hex
\li Color
+
\row
\li aliceblue
\li #f0f8ff
\li \svgcolor {#f0f8ff}
+
\row
\li antiquewhite
\li #faebd7
\li \svgcolor {#faebd7}
+
\row
\li aqua
\li #00ffff
\li \svgcolor {#00ffff}
+
\row
\li aquamarine
\li #7fffd4
\li \svgcolor {#7fffd4}
+
\row
\li azure
\li #f0ffff
\li \svgcolor {#f0ffff}
+
\row
\li beige
\li #f5f5dc
\li \svgcolor {#f5f5dc}
+
\row
\li bisque
\li #ffe4c4
\li \svgcolor {#ffe4c4}
+
\row
\li black
\li #000000
\li \svgcolor {#000000}
+
\row
\li blanchedalmond
\li #ffebcd
\li \svgcolor {#ffebcd}
+
\row
\li blue
\li #0000ff
\li \svgcolor {#0000ff}
+
\row
\li blueviolet
\li #8a2be2
\li \svgcolor {#8a2be2}
+
\row
\li brown
\li #a52a2a
\li \svgcolor {#a52a2a}
+
\row
\li burlywood
\li #deb887
\li \svgcolor {#deb887}
+
\row
\li cadetblue
\li #5f9ea0
\li \svgcolor {#5f9ea0}
+
\row
\li chartreuse
\li #7fff00
\li \svgcolor {#7fff00}
+
\row
\li chocolate
\li #d2691e
\li \svgcolor {#d2691e}
+
\row
\li coral
\li #ff7f50
\li \svgcolor {#ff7f50}
+
\row
\li cornflowerblue
\li #6495ed
\li \svgcolor {#6495ed}
+
\row
\li cornsilk
\li #fff8dc
\li \svgcolor {#fff8dc}
+
\row
\li crimson
\li #dc143c
\li \svgcolor {#dc143c}
+
\row
\li cyan
\li #00ffff
\li \svgcolor {#00ffff}
+
\row
\li darkblue
\li #00008b
\li \svgcolor {#00008b}
+
\row
\li darkcyan
\li #008b8b
\li \svgcolor {#008b8b}
+
\row
\li darkgoldenrod
\li #b8860b
\li \svgcolor {#b8860b}
+
\row
\li darkgray
\li #a9a9a9
\li \svgcolor {#a9a9a9}
+
\row
\li darkgreen
\li #006400
\li \svgcolor {#006400}
+
\row
\li darkgrey
\li #a9a9a9
\li \svgcolor {#a9a9a9}
+
\row
\li darkkhaki
\li #bdb76b
\li \svgcolor {#bdb76b}
+
\row
\li darkmagenta
\li #8b008b
\li \svgcolor {#8b008b}
+
\row
\li darkolivegreen
\li #556b2f
\li \svgcolor {#556b2f}
+
\row
\li darkorange
\li #ff8c00
\li \svgcolor {#ff8c00}
+
\row
\li darkorchid
\li #9932cc
\li \svgcolor {#9932cc}
+
\row
\li darkred
\li #8b0000
\li \svgcolor {#8b0000}
+
\row
\li darksalmon
\li #e9967a
\li \svgcolor {#e9967a}
+
\row
\li darkseagreen
\li #8fbc8f
\li \svgcolor {#8fbc8f}
+
\row
\li darkslateblue
\li #483d8b
\li \svgcolor {#483d8b}
+
\row
\li darkslategray
\li #2f4f4f
\li \svgcolor {#2f4f4f}
+
\row
\li darkslategrey
\li #2f4f4f
\li \svgcolor {#2f4f4f}
+
\row
\li darkturquoise
\li #00ced1
\li \svgcolor {#00ced1}
+
\row
\li darkviolet
\li #9400d3
\li \svgcolor {#9400d3}
+
\row
\li deeppink
\li #ff1493
\li \svgcolor {#ff1493}
+
\row
\li deepskyblue
\li #00bfff
\li \svgcolor {#00bfff}
+
\row
\li dimgray
\li #696969
\li \svgcolor {#696969}
+
\row
\li dimgrey
\li #696969
\li \svgcolor {#696969}
+
\row
\li dodgerblue
\li #1e90ff
\li \svgcolor {#1e90ff}
+
\row
\li firebrick
\li #b22222
\li \svgcolor {#b22222}
+
\row
\li floralwhite
\li #fffaf0
\li \svgcolor {#fffaf0}
+
\row
\li forestgreen
\li #228b22
\li \svgcolor {#228b22}
+
\row
\li fuchsia
\li #ff00ff
\li \svgcolor {#ff00ff}
+
\row
\li gainsboro
\li #dcdcdc
\li \svgcolor {#dcdcdc}
+
\row
\li ghostwhite
\li #f8f8ff
\li \svgcolor {#f8f8ff}
+
\row
\li gold
\li #ffd700
\li \svgcolor {#ffd700}
+
\row
\li goldenrod
\li #daa520
\li \svgcolor {#daa520}
+
\row
\li gray
\li #808080
\li \svgcolor {#808080}
+
\row
\li grey
\li #808080
\li \svgcolor {#808080}
+
\row
\li green
\li #008000
\li \svgcolor {#008000}
+
\row
\li greenyellow
\li #adff2f
\li \svgcolor {#adff2f}
+
\row
\li honeydew
\li #f0fff0
\li \svgcolor {#f0fff0}
+
\row
\li hotpink
\li #ff69b4
\li \svgcolor {#ff69b4}
+
\row
\li indianred
\li #cd5c5c
\li \svgcolor {#cd5c5c}
+
\row
\li indigo
\li #4b0082
\li \svgcolor {#4b0082}
+
\row
\li ivory
\li #fffff0
\li \svgcolor {#fffff0}
+
\row
\li khaki
\li #f0e68c
\li \svgcolor {#f0e68c}
+
\row
\li lavender
\li #e6e6fa
\li \svgcolor {#e6e6fa}
+
\row
\li lavenderblush
\li #fff0f5
\li \svgcolor {#fff0f5}
+
\row
\li lawngreen
\li #7cfc00
\li \svgcolor {#7cfc00}
+
\row
\li lemonchiffon
\li #fffacd
\li \svgcolor {#fffacd}
+
\row
\li lightblue
\li #add8e6
\li \svgcolor {#add8e6}
+
\row
\li lightcoral
\li #f08080
\li \svgcolor {#f08080}
+
\row
\li lightcyan
\li #e0ffff
\li \svgcolor {#e0ffff}
+
\row
\li lightgoldenrodyellow
\li #fafad2
\li \svgcolor {#fafad2}
+
\row
\li lightgray
\li #d3d3d3
\li \svgcolor {#d3d3d3}
+
\row
\li lightgreen
\li #90ee90
\li \svgcolor {#90ee90}
+
\row
\li lightgrey
\li #d3d3d3
\li \svgcolor {#d3d3d3}
+
\row
\li lightpink
\li #ffb6c1
\li \svgcolor {#ffb6c1}
+
\row
\li lightsalmon
\li #ffa07a
\li \svgcolor {#ffa07a}
+
\row
\li lightseagreen
\li #20b2aa
\li \svgcolor {#20b2aa}
+
\row
\li lightskyblue
\li #87cefa
\li \svgcolor {#87cefa}
+
\row
\li lightslategray
\li #778899
\li \svgcolor {#778899}
+
\row
\li lightslategrey
\li #778899
\li \svgcolor {#778899}
+
\row
\li lightsteelblue
\li #b0c4de
\li \svgcolor {#b0c4de}
+
\row
\li lightyellow
\li #ffffe0
\li \svgcolor {#ffffe0}
+
\row
\li lime
\li #00ff00
\li \svgcolor {#00ff00}
+
\row
\li limegreen
\li #32cd32
\li \svgcolor {#32cd32}
+
\row
\li linen
\li #faf0e6
\li \svgcolor {#faf0e6}
+
\row
\li magenta
\li #ff00ff
\li \svgcolor {#ff00ff}
+
\row
\li maroon
\li #800000
\li \svgcolor {#800000}
+
\row
\li mediumaquamarine
\li #66cdaa
\li \svgcolor {#66cdaa}
+
\row
\li mediumblue
\li #0000cd
\li \svgcolor {#0000cd}
+
\row
\li mediumorchid
\li #ba55d3
\li \svgcolor {#ba55d3}
+
\row
\li mediumpurple
\li #9370db
\li \svgcolor {#9370db}
+
\row
\li mediumseagreen
\li #3cb371
\li \svgcolor {#3cb371}
+
\row
\li mediumslateblue
\li #7b68ee
\li \svgcolor {#7b68ee}
+
\row
\li mediumspringgreen
\li #00fa9a
\li \svgcolor {#00fa9a}
+
\row
\li mediumturquoise
\li #48d1cc
\li \svgcolor {#48d1cc}
+
\row
\li mediumvioletred
\li #c71585
\li \svgcolor {#c71585}
+
\row
\li midnightblue
\li #191970
\li \svgcolor {#191970}
+
\row
\li mintcream
\li #f5fffa
\li \svgcolor {#f5fffa}
+
\row
\li mistyrose
\li #ffe4e1
\li \svgcolor {#ffe4e1}
+
\row
\li moccasin
\li #ffe4b5
\li \svgcolor {#ffe4b5}
+
\row
\li navajowhite
\li #ffdead
\li \svgcolor {#ffdead}
+
\row
\li navy
\li #000080
\li \svgcolor {#000080}
+
\row
\li oldlace
\li #fdf5e6
\li \svgcolor {#fdf5e6}
+
\row
\li olive
\li #808000
\li \svgcolor {#808000}
+
\row
\li olivedrab
\li #6b8e23
\li \svgcolor {#6b8e23}
+
\row
\li orange
\li #ffa500
\li \svgcolor {#ffa500}
+
\row
\li orangered
\li #ff4500
\li \svgcolor {#ff4500}
+
\row
\li orchid
\li #da70d6
\li \svgcolor {#da70d6}
+
\row
\li palegoldenrod
\li #eee8aa
\li \svgcolor {#eee8aa}
+
\row
\li palegreen
\li #98fb98
\li \svgcolor {#98fb98}
+
\row
\li paleturquoise
\li #afeeee
\li \svgcolor {#afeeee}
+
\row
\li palevioletred
\li #db7093
\li \svgcolor {#db7093}
+
\row
\li papayawhip
\li #ffefd5
\li \svgcolor {#ffefd5}
+
\row
\li peachpuff
\li #ffdab9
\li \svgcolor {#ffdab9}
+
\row
\li peru
\li #cd853f
\li \svgcolor {#cd853f}
+
\row
\li pink
\li #ffc0cb
\li \svgcolor {#ffc0cb}
+
\row
\li plum
\li #dda0dd
\li \svgcolor {#dda0dd}
+
\row
\li powderblue
\li #b0e0e6
\li \svgcolor {#b0e0e6}
+
\row
\li purple
\li #800080
\li \svgcolor {#800080}
+
\row
\li red
\li #ff0000
\li \svgcolor {#ff0000}
+
\row
\li rosybrown
\li #bc8f8f
\li \svgcolor {#bc8f8f}
+
\row
\li royalblue
\li #4169e1
\li \svgcolor {#4169e1}
+
\row
\li saddlebrown
\li #8b4513
\li \svgcolor {#8b4513}
+
\row
\li salmon
\li #fa8072
\li \svgcolor {#fa8072}
+
\row
\li sandybrown
\li #f4a460
\li \svgcolor {#f4a460}
+
\row
\li seagreen
\li #2e8b57
\li \svgcolor {#2e8b57}
+
\row
\li seashell
\li #fff5ee
\li \svgcolor {#fff5ee}
+
\row
\li sienna
\li #a0522d
\li \svgcolor {#a0522d}
+
\row
\li silver
\li #c0c0c0
\li \svgcolor {#c0c0c0}
+
\row
\li skyblue
\li #87ceeb
\li \svgcolor {#87ceeb}
+
\row
\li slateblue
\li #6a5acd
\li \svgcolor {#6a5acd}
+
\row
\li slategray
\li #708090
\li \svgcolor {#708090}
+
\row
\li slategrey
\li #708090
\li \svgcolor {#708090}
+
\row
\li snow
\li #fffafa
\li \svgcolor {#fffafa}
+
\row
\li springgreen
\li #00ff7f
\li \svgcolor {#00ff7f}
+
\row
\li steelblue
\li #4682b4
\li \svgcolor {#4682b4}
+
\row
\li tan
\li #d2b48c
\li \svgcolor {#d2b48c}
+
\row
\li teal
\li #008080
\li \svgcolor {#008080}
+
\row
\li thistle
\li #d8bfd8
\li \svgcolor {#d8bfd8}
+
\row
\li tomato
\li #ff6347
\li \svgcolor {#ff6347}
+
\row
\li turquoise
\li #40e0d0
\li \svgcolor {#40e0d0}
+
\row
\li violet
\li #ee82ee
\li \svgcolor {#ee82ee}
+
\row
\li wheat
\li #f5deb3
\li \svgcolor {#f5deb3}
+
\row
\li white
\li #ffffff
\li \svgcolor {#ffffff}
+
\row
\li whitesmoke
\li #f5f5f5
\li \svgcolor {#f5f5f5}
+
\row
\li yellow
\li #ffff00
\li \svgcolor {#ffff00}
+
\row
\li yellowgreen
\li #9acd32
\li \svgcolor {#9acd32}
+
\endtable
diff --git a/src/gui/doc/src/qt6-changes.qdoc b/src/gui/doc/src/qt6-changes.qdoc
index 109a7f1750..60e1bffba8 100644
--- a/src/gui/doc/src/qt6-changes.qdoc
+++ b/src/gui/doc/src/qt6-changes.qdoc
@@ -5,7 +5,7 @@
\page gui-changes-qt6.html
\title Changes to Qt GUI
\ingroup changes-qt-5-to-6
- \brief Migrate Qt GUI to Qt 6.
+ \brief Kernel, Text, Painting, and Utility classes are modified.
Qt 6 is a result of the conscious effort to make the framework more
efficient and easy to use.
@@ -114,4 +114,38 @@
Metal, in addition to OpenGL. On Windows the default choice is Direct 3D,
therefore the removal of ANGLE is alleviated by having support for graphics
APIs other than OpenGL as well.
+
+ \section2 Native clipboard integration
+
+ Qt 5 provided interfaces for integrating platform specific or custom
+ clipboard formats into Qt through \c QMacPasteboardMime in \c QtMacExtras,
+ and \c QWindowsMime from the Windows QPA API. Since Qt 6.6, the
+ equivalent functionality is provided by the classes QUtiMimeConverter for
+ \macos, and the QWindowsMimeConverter for Windows.
+
+ Porting from QWindowsMime to QWindowsMimeConverter requires practically no
+ changes, as the virtual interface is identical. However, in Qt 6 it is no
+ longer needed to register a QWindowsMimeConverter implementation;
+ instantiating the type implicitly registers the converter.
+
+ Porting a QMacPasteboardMime to QUtiMimeConverter requires renaming some of
+ the virtual functions. Note that the \c{QMacPasteboardMime} API used the
+ outdated term \c{flavor} for the native clipboard format on \macos, whereas
+ the platform now uses \c{Uniform Type Identifiers}, i.e. \c{UTI}s, which Qt
+ has adapted for function and parameter names.
+
+ The \c{mimeFor} and \c{flavorFor} functions are replaced by the
+ \l{QUtiMimeConverter::}{mimeForUti} and \l{QUtiMimeConverter::}{utiForMime}
+ implementations, respectively. Those should return the name of the mime
+ type or \c{UTI} that the converter can convert the input format to, so a
+ port usually just involves renaming existing overrides. The
+ \c{convertToMime}, \c{convertFromMime}, and \c{count} functions in
+ QUtiMimeConverter are identical to their QMacPasteboardMime versions.
+
+ The \c{canConvert}, \c{converterName} functions are no longer needed, they
+ are implied by implementation of the above functions, so overrides of those
+ functions can be removed.
+
+ As with the the QWindowsMimeConverter, registration is done by instantiating
+ the type.
*/
diff --git a/src/gui/doc/src/qtgui-overview.qdoc b/src/gui/doc/src/qtgui-overview.qdoc
index ecd34f0e9e..446479c9be 100644
--- a/src/gui/doc/src/qtgui-overview.qdoc
+++ b/src/gui/doc/src/qtgui-overview.qdoc
@@ -51,6 +51,68 @@
that prefer more low-level APIs to text and font handling can use classes
like QRawFont and QGlyphRun.
+ \section1 RHI Graphics
+
+ 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}.
+
+ As an alternative to using OpenGL or Vulkan directly to render to a
+ QWindow, \l QRhi and the related classes provide a portable, cross-platform
+ 3D graphics and compute API complemented by a shader conditioning and
+ transpiling pipeline. This way applications can avoid directly depending on
+ a single, and, in some cases, vendor or platform-specific 3D API.
+
+ Below is a list of the main RHI-related classes. These are complemented by
+ a number of additional classes and structs.
+
+ \list
+ \li QRhi
+ \li QShader
+ \li QShaderDescription
+ \li QRhiCommandBuffer
+ \li QRhiResourceUpdateBatch
+ \li QRhiBuffer
+ \li QRhiRenderBuffer
+ \li QRhiTexture
+ \li QRhiSampler
+ \li QRhiTextureRenderTarget
+ \li QRhiShaderResourceBindings
+ \li QRhiGraphicsPipeline
+ \li QRhiComputePipeline
+ \li QRhiSwapChain
+ \endlist
+
+ See the \l{RHI Window Example} for an introductory example of creating a
+ portable, cross-platform application that performs accelerated 3D rendering
+ onto a QWindow using QRhi.
+
+ Working directly with QWindow is the most advanced and often the most
+ flexible way of rendering with the QRhi API. It is the most low-level
+ approach, however, and limited in the sense that Qt's UI technologies,
+ widgets and Qt Quick, are not utilized at all. In many cases applications
+ will rather want to integrate QRhi-based rendering into a widget or Qt
+ Quick-based user interface. QWidget-based applications may choose to embed
+ the window as a native child into the widget hierarchy via
+ QWidget::createWindowContainer(), but in many cases \l QRhiWidget will
+ offer a more convenient enabler to integrate QRhi-based rendering into a
+ widget UI. Qt Quick provides its own set of enablers for extending the
+ 2D/3D scene with QRhi-based custom rendering.
+
+ \note The RHI family of APIs are currently offered with a limited
+ compatibility guarantee, as opposed to regular Qt public APIs. See \l QRhi
+ for details.
+
+ \section1 3D Matrix and Vector Math
+
+ The Qt GUI module also contains a few math classes to aid with the most
+ common mathematical operations related to 3D graphics. These classes
+ include \l {QMatrix4x4}, \l {QVector2D}, \l {QVector3D}, \l {QVector4D},
+ and \l {QQuaternion}.
+
\section1 OpenGL and OpenGL ES Integration
QWindow supports rendering using OpenGL and OpenGL ES, depending on what the
@@ -86,10 +148,6 @@
For more information, see the \l {OpenGL Window Example}.
- The Qt GUI module also contains a few math classes to aid with the most
- common mathematical operations related to 3D graphics. These classes include
- \l {QMatrix4x4}, \l {QVector4D}, and \l {QQuaternion}.
-
A \l {QWindow} created with the \l {QSurface::OpenGLSurface} can be used in
combination with \l QPainter and \l QOpenGLPaintDevice to have OpenGL
hardware-accelerated 2D graphics by sacrificing some of the visual quality.
@@ -104,20 +162,23 @@
On Android, Vulkan headers were added in API level 24 of the NDK.
- Relevant classes:
+ The main relevant classes for low-level Vulkan support are:
\list
- \li QVulkanDeviceFunctions
- \li QVulkanExtension
- \li QVulkanFunctions
- \li QVulkanInfoVector
\li QVulkanInstance
- \li QVulkanWindow
- \li QVulkanWindowRenderer
+ \li QVulkanFunctions
+ \li QVulkanDeviceFunctions
\endlist
+ In addition, \l QVulkanWindow provides a convenience subclass of QWindow
+ that makes it easier to get started with implementing Vulkan-based
+ rendering targeting a QWindow. Using this helper class is completely
+ optional; applications with more advanced Vulkan-based renderers may
+ instead want to use a QWindow with the \l {QSurface::VulkanSurface} type
+ directly.
+
For more information, see the \l{Hello Vulkan Widget Example}
- and the \l {Hello Vulkan Window Example}.
+ and the \l {Hello Vulkan Triangle Example}.
\section1 Drag and Drop
diff --git a/src/gui/doc/src/qtgui.qdoc b/src/gui/doc/src/qtgui.qdoc
index d39fa72221..2a1d69d4d3 100644
--- a/src/gui/doc/src/qtgui.qdoc
+++ b/src/gui/doc/src/qtgui.qdoc
@@ -15,6 +15,23 @@
*/
/*!
+ \module QtGuiPrivate
+ \title Qt GUI Private C++ Classes
+ \qtcmakepackage Gui
+ \qtvariable gui-private
+
+ \brief Provides access to private GUI functionality.
+
+ Use the following CMake commands in your \c {CMakeLists.txt} to access
+ private Qt GUI APIs:
+
+ \badcode
+ find_package(Qt6 REQUIRED COMPONENTS Gui)
+ target_link_libraries(mytarget PRIVATE Qt6::GuiPrivate)
+ \endcode
+*/
+
+/*!
\page qtgui-index.html
\title Qt GUI
@@ -54,6 +71,8 @@
\list
\li \l {Application Windows} {Qt GUI Application Windows}
\li \l {2D Graphics} {Qt GUI 2D Graphics}
+ \li \l {RHI Graphics} {Qt GUI Accelerated 2D and 3D Graphics using the Qt RHI}
+ \li \l {3D Matrix and Vector Math} {Qt GUI Matrix and Vector Math}
\li \l {OpenGL and OpenGL ES Integration}
{Qt GUI OpenGL and OpenGL ES Integration}
\li \l {Vulkan Integration} {Qt GUI Vulkan Integration}
diff --git a/src/gui/doc/src/richtext.qdoc b/src/gui/doc/src/richtext.qdoc
index 5ae7739481..10d9962850 100644
--- a/src/gui/doc/src/richtext.qdoc
+++ b/src/gui/doc/src/richtext.qdoc
@@ -11,10 +11,9 @@
\page richtext.html
\title Rich Text Processing
\brief An overview of Qt's rich text processing, editing and display features.
-
+ \ingroup explanations-ui
\ingroup frameworks-technologies
\ingroup qt-basic-concepts
- \ingroup best-practices
\nextpage Rich Text Document Structure
@@ -839,7 +838,11 @@
\section1 Supported Tags
The following table lists the HTML tags supported by Qt's
- \l{Rich Text Processing}{rich text} engine:
+ \l{Rich Text Processing}{rich text} engine.
+
+ \note The functionality implemented for tags listed below is a subset of
+ the full HTML 4 specification. Not all attributes are supported,
+ see comments for each tag.
\table 70%
\header \li Tag
diff --git a/src/gui/image/qabstractfileiconengine.cpp b/src/gui/image/qabstractfileiconengine.cpp
index e94451e4bb..ffbe088d20 100644
--- a/src/gui/image/qabstractfileiconengine.cpp
+++ b/src/gui/image/qabstractfileiconengine.cpp
@@ -61,7 +61,7 @@ QSize QAbstractFileIconEngine::actualSize(const QSize &size, QIcon::Mode mode,
QIcon::State state)
{
const QList<QSize> &sizes = availableSizes(mode, state);
- const int numberSizes = sizes.length();
+ const int numberSizes = sizes.size();
if (numberSizes == 0)
return QSize();
diff --git a/src/gui/image/qabstractfileiconengine_p.h b/src/gui/image/qabstractfileiconengine_p.h
index cdabdc7b77..99d16d3224 100644
--- a/src/gui/image/qabstractfileiconengine_p.h
+++ b/src/gui/image/qabstractfileiconengine_p.h
@@ -30,6 +30,7 @@ public:
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State) override;
QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State, qreal scale) override;
QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
+ bool isNull() override { return false; }
QFileInfo fileInfo() const { return m_fileInfo; }
QPlatformTheme::IconOptions options() const { return m_options; }
diff --git a/src/gui/image/qabstractfileiconprovider.cpp b/src/gui/image/qabstractfileiconprovider.cpp
index c5a51001af..71b6a0b03d 100644
--- a/src/gui/image/qabstractfileiconprovider.cpp
+++ b/src/gui/image/qabstractfileiconprovider.cpp
@@ -229,21 +229,16 @@ QIcon QAbstractFileIconProvider::icon(const QFileInfo &info) const
return result.isNull() ? d->getPlatformThemeIcon(info) : result;
}
-/*!
- Returns the type of the file described by \a info.
-*/
-QString QAbstractFileIconProvider::type(const QFileInfo &info) const
+QString QAbstractFileIconProviderPrivate::getFileType(const QFileInfo &info)
{
- Q_D(const QAbstractFileIconProvider);
if (QFileSystemEntry::isRootPath(info.absoluteFilePath()))
return QGuiApplication::translate("QAbstractFileIconProvider", "Drive");
if (info.isFile()) {
#if QT_CONFIG(mimetype)
- const QMimeType mimeType = d->mimeDatabase.mimeTypeForFile(info);
+ const QMimeType mimeType = QMimeDatabase().mimeTypeForFile(info);
return mimeType.comment().isEmpty() ? mimeType.name() : mimeType.comment();
#else
- Q_UNUSED(d);
return QGuiApplication::translate("QAbstractFileIconProvider", "File");
#endif
}
@@ -273,4 +268,13 @@ QString QAbstractFileIconProvider::type(const QFileInfo &info) const
return QGuiApplication::translate("QAbstractFileIconProvider", "Unknown");
}
+/*!
+ Returns the type of the file described by \a info.
+*/
+
+QString QAbstractFileIconProvider::type(const QFileInfo &info) const
+{
+ return QAbstractFileIconProviderPrivate::getFileType(info);
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/image/qabstractfileiconprovider_p.h b/src/gui/image/qabstractfileiconprovider_p.h
index d2caf05440..f53be0f06c 100644
--- a/src/gui/image/qabstractfileiconprovider_p.h
+++ b/src/gui/image/qabstractfileiconprovider_p.h
@@ -37,6 +37,7 @@ public:
QIcon getIconThemeIcon(const QFileInfo &info) const;
static void clearIconTypeCache();
+ static QString getFileType(const QFileInfo &info);
QAbstractFileIconProvider *q_ptr = nullptr;
QAbstractFileIconProvider::Options options = {};
diff --git a/src/gui/image/qbitmap.cpp b/src/gui/image/qbitmap.cpp
index 96ff03ce39..2208cca1be 100644
--- a/src/gui/image/qbitmap.cpp
+++ b/src/gui/image/qbitmap.cpp
@@ -196,7 +196,7 @@ QBitmap QBitmap::fromImage(QImage &&image, Qt::ImageConversionFlags flags)
Constructs a bitmap with the given \a size, and sets the contents to
the \a bits supplied.
- The bitmap data has to be byte aligned and provided in in the bit
+ The bitmap data has to be byte aligned and provided in the bit
order specified by \a monoFormat. The mono format must be either
QImage::Format_Mono or QImage::Format_MonoLSB. Use
QImage::Format_Mono to specify data on the XBM format.
diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp
index 35e9875f20..798ba7f4b3 100644
--- a/src/gui/image/qbmphandler.cpp
+++ b/src/gui/image/qbmphandler.cpp
@@ -135,16 +135,42 @@ static QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi)
return s;
}
-static int calc_shift(uint mask)
+static uint calc_shift(uint mask)
{
- int result = 0;
- while (mask && !(mask & 1)) {
+ uint result = 0;
+ while ((mask >= 0x100) || (!(mask & 1) && mask)) {
result++;
mask >>= 1;
}
return result;
}
+static uint calc_scale(uint low_mask)
+{
+ uint result = 8;
+ while (low_mask && result) {
+ result--;
+ low_mask >>= 1;
+ }
+ return result;
+}
+
+static inline uint apply_scale(uint value, uint scale)
+{
+ if (!(scale & 0x07)) // return immediately if scale == 8 or 0
+ return value;
+
+ uint filled = 8 - scale;
+ uint result = value << scale;
+
+ do {
+ result |= result >> filled;
+ filled <<= 1;
+ } while (filled < 8);
+
+ return result;
+}
+
static bool read_dib_fileheader(QDataStream &s, BMP_FILEHDR &bf)
{
// read BMP file header
@@ -207,14 +233,14 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 datapos,
uint green_mask = 0;
uint blue_mask = 0;
uint alpha_mask = 0;
- int red_shift = 0;
- int green_shift = 0;
- int blue_shift = 0;
- int alpha_shift = 0;
- int red_scale = 0;
- int green_scale = 0;
- int blue_scale = 0;
- int alpha_scale = 0;
+ uint red_shift = 0;
+ uint green_shift = 0;
+ uint blue_shift = 0;
+ uint alpha_shift = 0;
+ uint red_scale = 0;
+ uint green_scale = 0;
+ uint blue_scale = 0;
+ uint alpha_scale = 0;
bool bitfields = comp == BMP_BITFIELDS || comp == BMP_ALPHABITFIELDS;
if (!d->isSequential())
@@ -289,19 +315,19 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 datapos,
red_shift = calc_shift(red_mask);
if (((red_mask >> red_shift) + 1) == 0)
return false;
- red_scale = 256 / ((red_mask >> red_shift) + 1);
+ red_scale = calc_scale(red_mask >> red_shift);
green_shift = calc_shift(green_mask);
if (((green_mask >> green_shift) + 1) == 0)
return false;
- green_scale = 256 / ((green_mask >> green_shift) + 1);
+ green_scale = calc_scale(green_mask >> green_shift);
blue_shift = calc_shift(blue_mask);
if (((blue_mask >> blue_shift) + 1) == 0)
return false;
- blue_scale = 256 / ((blue_mask >> blue_shift) + 1);
+ blue_scale = calc_scale(blue_mask >> blue_shift);
alpha_shift = calc_shift(alpha_mask);
if (((alpha_mask >> alpha_shift) + 1) == 0)
return false;
- alpha_scale = 256 / ((alpha_mask >> alpha_shift) + 1);
+ alpha_scale = calc_scale(alpha_mask >> alpha_shift);
} else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) {
blue_mask = 0x000000ff;
green_mask = 0x0000ff00;
@@ -309,23 +335,21 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 datapos,
blue_shift = 0;
green_shift = 8;
red_shift = 16;
- blue_scale = green_scale = red_scale = 1;
+ blue_scale = green_scale = red_scale = 0;
if (transp) {
alpha_shift = calc_shift(alpha_mask);
if (((alpha_mask >> alpha_shift) + 1) == 0)
return false;
- alpha_scale = 256 / ((alpha_mask >> alpha_shift) + 1);
+ alpha_scale = calc_scale(alpha_mask >> alpha_shift);
}
} else if (comp == BMP_RGB && nbits == 16) {
blue_mask = 0x001f;
green_mask = 0x03e0;
red_mask = 0x7c00;
blue_shift = 0;
- green_shift = 2;
- red_shift = 7;
- red_scale = 1;
- green_scale = 1;
- blue_scale = 8;
+ green_shift = 5;
+ red_shift = 10;
+ blue_scale = green_scale = red_scale = 3;
}
image.setDotsPerMeterX(bi.biXPelsPerMeter);
@@ -533,10 +557,10 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 datapos,
c |= *(uchar*)(b+2)<<16;
if (nbits > 24)
c |= *(uchar*)(b+3)<<24;
- *p++ = qRgba(((c & red_mask) >> red_shift) * red_scale,
- ((c & green_mask) >> green_shift) * green_scale,
- ((c & blue_mask) >> blue_shift) * blue_scale,
- transp ? ((c & alpha_mask) >> alpha_shift) * alpha_scale : 0xff);
+ *p++ = qRgba(apply_scale((c & red_mask) >> red_shift, red_scale),
+ apply_scale((c & green_mask) >> green_shift, green_scale),
+ apply_scale((c & blue_mask) >> blue_shift, blue_scale),
+ transp ? apply_scale((c & alpha_mask) >> alpha_shift, alpha_scale) : 0xff);
b += nbits/8;
}
}
@@ -584,7 +608,7 @@ bool qt_write_dib(QDataStream &s, const QImage &image, int bpl, int bpl_bmp, int
if (image.depth() != 32) { // write color table
uchar *color_table = new uchar[4*image.colorCount()];
uchar *rgb = color_table;
- QList<QRgb> c = image.colorTable();
+ const QList<QRgb> c = image.colorTable();
for (int i = 0; i < image.colorCount(); i++) {
*rgb++ = qBlue (c[i]);
*rgb++ = qGreen(c[i]);
diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp
index 38e66b1dc0..086ac37a07 100644
--- a/src/gui/image/qicon.cpp
+++ b/src/gui/image/qicon.cpp
@@ -25,6 +25,7 @@
#include "private/qhexstring_p.h"
#include "private/qguiapplication_p.h"
+#include "private/qoffsetstringarray_p.h"
#include "qpa/qplatformtheme.h"
#ifndef QT_NO_ICON
@@ -67,7 +68,7 @@ using namespace Qt::StringLiterals;
static int nextSerialNumCounter()
{
- static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0);
+ Q_CONSTINIT static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0);
return 1 + serial.fetchAndAddRelaxed(1);
}
@@ -98,6 +99,11 @@ QIconPrivate::QIconPrivate(QIconEngine *e)
{
}
+void QIconPrivate::clearIconCache()
+{
+ qt_cleanup_icon_cache();
+}
+
/*! \internal
Computes the displayDevicePixelRatio for a pixmap.
@@ -164,6 +170,9 @@ static QPixmapIconEngineEntry *bestSizeScaleMatch(const QSize &size, qreal scale
qreal ascore = pa->scale - scale;
qreal bscore = pb->scale - scale;
+ // always prefer positive scores to prevent upscaling
+ if ((ascore < 0) != (bscore < 0))
+ return bscore < 0 ? pa : pb;
// Take the one closest to 0
return (qAbs(ascore) < qAbs(bscore)) ? pa : pb;
}
@@ -192,7 +201,7 @@ static QPixmapIconEngineEntry *bestSizeScaleMatch(const QSize &size, qreal scale
QPixmapIconEngineEntry *QPixmapIconEngine::tryMatch(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state)
{
QPixmapIconEngineEntry *pe = nullptr;
- for (int i = 0; i < pixmaps.count(); ++i)
+ for (int i = 0; i < pixmaps.size(); ++i)
if (pixmaps.at(i).mode == mode && pixmaps.at(i).state == state) {
if (pe)
pe = bestSizeScaleMatch(size, scale, &pixmaps[i], pe);
@@ -269,7 +278,7 @@ QPixmap QPixmapIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIc
pm = pe->pixmap;
if (pm.isNull()) {
- int idx = pixmaps.count();
+ int idx = pixmaps.size();
while (--idx >= 0) {
if (pe == &pixmaps.at(idx)) {
pixmaps.remove(idx);
@@ -343,15 +352,16 @@ QSize QPixmapIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::
QList<QSize> QPixmapIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state)
{
QList<QSize> sizes;
- for (int i = 0; i < pixmaps.size(); ++i) {
- QPixmapIconEngineEntry &pe = pixmaps[i];
- if (pe.size == QSize() && pe.pixmap.isNull()) {
+ for (QPixmapIconEngineEntry &pe : pixmaps) {
+ if (pe.mode != mode || pe.state != state)
+ continue;
+ if (pe.size.isEmpty() && pe.pixmap.isNull()) {
pe.pixmap = QPixmap(pe.fileName);
pe.size = pe.pixmap.size();
}
- if (pe.mode == mode && pe.state == state && !pe.size.isEmpty())
+ if (!pe.size.isEmpty() && !sizes.contains(pe.size))
sizes.push_back(pe.size);
- }
+ }
return sizes;
}
@@ -451,12 +461,17 @@ void QPixmapIconEngine::addFile(const QString &fileName, const QSize &size, QIco
}
}
}
- for (const QImage &i : qAsConst(icoImages))
+ for (const QImage &i : std::as_const(icoImages))
pixmaps += QPixmapIconEngineEntry(abs, i, mode, state);
if (icoImages.isEmpty() && !ignoreSize) // Add placeholder with the filename and empty pixmap for the size.
pixmaps += QPixmapIconEngineEntry(abs, size, mode, state);
}
+bool QPixmapIconEngine::isNull()
+{
+ return pixmaps.isEmpty();
+}
+
QString QPixmapIconEngine::key() const
{
return "QPixmapIconEngine"_L1;
@@ -515,12 +530,12 @@ bool QPixmapIconEngine::write(QDataStream &out) const
return true;
}
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, iceLoader,
(QIconEngineFactoryInterface_iid, "/iconengines"_L1, Qt::CaseInsensitive))
QFactoryLoader *qt_iconEngineFactoryLoader()
{
- return loader();
+ return iceLoader();
}
@@ -536,15 +551,26 @@ QFactoryLoader *qt_iconEngineFactoryLoader()
A QIcon can generate smaller, larger, active, and disabled pixmaps
from the set of pixmaps it is given. Such pixmaps are used by Qt
- widgets to show an icon representing a particular action.
+ UI components to show an icon representing a particular action.
- The simplest use of QIcon is to create one from a QPixmap file or
- resource, and then use it, allowing Qt to work out all the required
- icon styles and sizes. For example:
+ \section1 Creating an icon from image files
+
+ The simplest way to construct a QIcon is to create one from one or
+ several image files or resources. For example:
\snippet code/src_gui_image_qicon.cpp 0
- To undo a QIcon, simply set a null icon in its place:
+ QIcon can store several images for different states, and Qt will
+ select the image that is the closest match for the action's current
+ state.
+
+ \snippet code/src_gui_image_qicon.cpp addFile
+
+ Qt will generate the required icon styles and sizes when needed,
+ e.g. the pixmap for the QIcon::Disabled state might be generated by
+ graying out one of the provided pixmaps.
+
+ To clear the icon, simply set a null icon in its place:
\snippet code/src_gui_image_qicon.cpp 1
@@ -552,31 +578,54 @@ QFactoryLoader *qt_iconEngineFactoryLoader()
QImageWriter::supportedImageFormats() functions to retrieve a
complete list of the supported file formats.
- When you retrieve a pixmap using pixmap(QSize, Mode, State), and no
- pixmap for this given size, mode and state has been added with
- addFile() or addPixmap(), then QIcon will generate one on the
- fly. This pixmap generation happens in a QIconEngine. The default
- engine scales pixmaps down if required, but never up, and it uses
- the current style to calculate a disabled appearance. By using
- custom icon engines, you can customize every aspect of generated
- icons. With QIconEnginePlugin it is possible to register different
- icon engines for different file suffixes, making it possible for
- third parties to provide additional icon engines to those included
+ \section1 Creating an icon from a theme or icon library
+
+ The most convenient way to construct an icon is by using the
+ \l{QIcon::}{fromTheme()} factory function. Qt implements access to
+ the native icon library on platforms that support the
+ \l {Freedesktop Icon Theme Specification}. Since Qt 6.7, Qt also
+ provides access to the native icon library on macOS, iOS, and
+ Windows 10 and 11. On Android, Qt can access icons from the Material
+ design system as long as the
+ \l{https://github.com/google/material-design-icons/tree/master/font}
+ {MaterialIcons-Regular} font is available on the system, or bundled
+ as a resource at \c{:/qt-project.org/icons/MaterialIcons-Regular.ttf}
+ with the application.
+
+ \snippet code/src_gui_image_qicon.cpp fromTheme
+
+ Applications can use the same theming specification to provide
+ their own icon library. See below for an example theme description
+ and the corresponding directory structure for the image files.
+ Icons from an application-provided theme take precedence over the
+ native icon library.
+
+ In addition, it is possible to provide custom \l {QIconEngine}
+ {icon engines}. This allows applications to customize every aspect
+ of generated icons. With QIconEnginePlugin it is possible to register
+ different icon engines for different file suffixes, making it possible
+ for third parties to provide additional icon engines to those included
with Qt.
- \note Since Qt 4.2, an icon engine that supports SVG is included.
-
\section1 Making Classes that Use QIcon
If you write your own widgets that have an option to set a small
pixmap, consider allowing a QIcon to be set for that pixmap. The
Qt class QToolButton is an example of such a widget.
- Provide a method to set a QIcon, and when you draw the icon, choose
- whichever pixmap is appropriate for the current state of your widget.
- For example:
+ Provide a method to set a QIcon, and paint the QIcon with
+ \l{QIcon::}{paint}, choosing the appropriate parameters based
+ on the current state of your widget. For example:
+
\snippet code/src_gui_image_qicon.cpp 2
+ When you retrieve a pixmap using pixmap(QSize, Mode, State), and no
+ pixmap for this given size, mode and state has been added with
+ addFile() or addPixmap(), then QIcon will generate one on the
+ fly. This pixmap generation happens in a QIconEngine. The default
+ engine scales pixmaps down if required, but never up, and it uses
+ the current style to calculate a disabled appearance.
+
You might also make use of the \c Active mode, perhaps making your
widget \c Active when the mouse is over the widget (see \l
QWidget::enterEvent()), while the mouse is pressed pending the
@@ -590,17 +639,19 @@ QFactoryLoader *qt_iconEngineFactoryLoader()
\section1 High DPI Icons
- There are two ways that QIcon supports \l {High DPI}{high DPI}
- icons: via \l addFile() and \l fromTheme().
+ Icons that are provided by the native icon library are usually based
+ on vector graphics, and will automatically be rendered in the appropriate
+ resolution.
- \l addFile() is useful if you have your own custom directory structure and do
- not need to use the \l {Icon Theme Specification}{freedesktop.org Icon Theme
- Specification}. Icons created via this approach use Qt's \l {High Resolution
- Versions of Images}{"@nx" high DPI syntax}.
+ When providing your own image files via \l addFile(), then QIcon will
+ use Qt's \l {High Resolution Versions of Images}{"@nx" high DPI syntax}.
+ This is useful if you have your own custom directory structure and do not
+ use follow \l {Freedesktop Icon Theme Specification}.
- Using \l fromTheme() is necessary if you plan on following the Icon Theme
- Specification. To make QIcon use the high DPI version of an image, add an
- additional entry to the appropriate \c index.theme file:
+ When providing an application theme, then you need to follow the Icon Theme
+ Specification to specify which files to use for different resolutions.
+ To make QIcon use the high DPI version of an image, add an additional entry
+ to the appropriate \c index.theme file:
\badcode
[Icon Theme]
@@ -632,8 +683,6 @@ QFactoryLoader *qt_iconEngineFactoryLoader()
│ └── appointment-new.png
└── index.theme
\endcode
-
- \sa {Icons Example}
*/
@@ -1019,9 +1068,9 @@ void QIcon::addPixmap(const QPixmap &pixmap, Mode mode, State state)
static QIconEngine *iconEngineFromSuffix(const QString &fileName, const QString &suffix)
{
if (!suffix.isEmpty()) {
- const int index = loader()->indexOf(suffix);
+ const int index = iceLoader()->indexOf(suffix);
if (index != -1) {
- if (QIconEnginePlugin *factory = qobject_cast<QIconEnginePlugin*>(loader()->instance(index))) {
+ if (QIconEnginePlugin *factory = qobject_cast<QIconEnginePlugin*>(iceLoader()->instance(index))) {
return factory->create(fileName);
}
}
@@ -1118,6 +1167,10 @@ QString QIcon::name() const
\since 4.6
Sets the search paths for icon themes to \a paths.
+
+ The content of \a paths should follow the theme format
+ documented by setThemeName().
+
\sa themeSearchPaths(), fromTheme(), setThemeName()
*/
void QIcon::setThemeSearchPaths(const QStringList &paths)
@@ -1126,20 +1179,14 @@ void QIcon::setThemeSearchPaths(const QStringList &paths)
}
/*!
- \since 4.6
-
- Returns the search paths for icon themes.
-
- The default value will depend on the platform:
+ \since 4.6
- On X11, the search path will use the XDG_DATA_DIRS environment
- variable if available.
+ Returns the search paths for icon themes.
- By default all platforms will have the resource directory
- \c{:\icons} as a fallback. You can use "rcc -project" to generate a
- resource file from your icon theme.
+ The default search paths will be defined by the platform.
+ All platforms will also have the resource directory \c{:\icons} as a fallback.
- \sa setThemeSearchPaths(), fromTheme(), setThemeName()
+ \sa setThemeSearchPaths(), fromTheme(), setThemeName()
*/
QStringList QIcon::themeSearchPaths()
{
@@ -1151,7 +1198,13 @@ QStringList QIcon::themeSearchPaths()
Returns the fallback search paths for icons.
- The default value will depend on the platform.
+ The fallback search paths are consulted for standalone
+ icon files if the \l{themeName()}{current icon theme}
+ or \l{fallbackThemeName()}{fallback icon theme} do
+ not provide results for an icon lookup.
+
+ If not set, the fallback search paths will be defined
+ by the platform.
\sa setFallbackSearchPaths(), themeSearchPaths()
*/
@@ -1165,7 +1218,12 @@ QStringList QIcon::fallbackSearchPaths()
Sets the fallback search paths for icons to \a paths.
- \note To add some path without replacing existing ones:
+ The fallback search paths are consulted for standalone
+ icon files if the \l{themeName()}{current icon theme}
+ or \l{fallbackThemeName()}{fallback icon theme} do
+ not provide results for an icon lookup.
+
+ For example:
\snippet code/src_gui_image_qicon.cpp 5
@@ -1181,11 +1239,15 @@ void QIcon::setFallbackSearchPaths(const QStringList &paths)
Sets the current icon theme to \a name.
- The \a name should correspond to a directory name in the
- themeSearchPath() containing an index.theme
- file describing its contents.
+ The theme will be will be looked up in themeSearchPaths().
- \sa themeSearchPaths(), themeName()
+ At the moment the only supported icon theme format is the
+ \l{Freedesktop Icon Theme Specification}. The \a name should
+ correspond to a directory name in the themeSearchPath()
+ containing an \c index.theme file describing its contents.
+
+ \sa themeSearchPaths(), themeName(),
+ {Freedesktop Icon Theme Specification}
*/
void QIcon::setThemeName(const QString &name)
{
@@ -1197,8 +1259,12 @@ void QIcon::setThemeName(const QString &name)
Returns the name of the current icon theme.
- On X11, the current icon theme depends on your desktop
- settings. On other platforms it is not set by default.
+ If not set, the current icon theme will be defined by the
+ platform.
+
+ \note Platform icon themes are only implemented on
+ \l{Freedesktop} based systems at the moment, and the
+ icon theme depends on your desktop settings.
\sa setThemeName(), themeSearchPaths(), fromTheme(),
hasThemeIcon()
@@ -1213,8 +1279,12 @@ QString QIcon::themeName()
Returns the name of the fallback icon theme.
- On X11, if not set, the fallback icon theme depends on your desktop
- settings. On other platforms it is not set by default.
+ If not set, the fallback icon theme will be defined by the
+ platform.
+
+ \note Platform fallback icon themes are only implemented on
+ \l{Freedesktop} based systems at the moment, and the
+ icon theme depends on your desktop settings.
\sa setFallbackThemeName(), themeName()
*/
@@ -1228,12 +1298,16 @@ QString QIcon::fallbackThemeName()
Sets the fallback icon theme to \a name.
- The \a name should correspond to a directory name in the
- themeSearchPath() containing an index.theme
- file describing its contents.
+ The fallback icon theme is consulted for icons not provided by
+ the \l{themeName()}{current icon theme}, or if the \l{themeName()}
+ {current icon theme} does not exist.
+
+ The \a name should correspond to theme in the same format
+ as documented by setThemeName(), and will be looked up
+ in themeSearchPaths().
- \note This should be done before creating \l QGuiApplication, to ensure
- correct initialization.
+ \note Fallback icon themes should be set before creating
+ QGuiApplication, to ensure correct initialization.
\sa fallbackThemeName(), themeSearchPaths(), themeName()
*/
@@ -1245,67 +1319,63 @@ void QIcon::setFallbackThemeName(const QString &name)
/*!
\since 4.6
- Returns the QIcon corresponding to \a name in the current
- icon theme.
+ Returns the QIcon corresponding to \a name in the
+ \l{themeName()}{current icon theme}.
- The latest version of the freedesktop icon specification and naming
- specification can be obtained here:
-
- \list
- \li \l{http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html}
- \li \l{http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html}
- \endlist
+ If the current theme does not provide an icon for \a name,
+ the \l{fallbackThemeName()}{fallback icon theme} is consulted,
+ before falling back to looking up standalone icon files in the
+ \l{QIcon::fallbackSearchPaths()}{fallback icon search path}.
+ Finally, the platform's native icon library is consulted.
To fetch an icon from the current icon theme:
- \snippet code/src_gui_image_qicon.cpp 3
-
- \note By default, only X11 will support themed icons. In order to
- use themed icons on Mac and Windows, you will have to bundle a
- compliant theme in one of your themeSearchPaths() and set the
- appropriate themeName().
-
- \note Qt will make use of GTK's icon-theme.cache if present to speed up
- the lookup. These caches can be generated using gtk-update-icon-cache:
- \l{https://developer.gnome.org/gtk3/stable/gtk-update-icon-cache.html}.
+ \snippet code/src_gui_image_qicon.cpp fromTheme
- \note If an icon can't be found in the current theme, then it will be
- searched in fallbackSearchPaths() as an unthemed icon.
+ If an \l{themeName()}{icon theme} has not been explicitly
+ set via setThemeName() a platform defined icon theme will
+ be used.
- \sa themeName(), setThemeName(), themeSearchPaths(), fallbackSearchPaths()
+ \sa themeName(), fallbackThemeName(), setThemeName(), themeSearchPaths(), fallbackSearchPaths(),
+ {Freedesktop Icon Naming Specification}
*/
QIcon QIcon::fromTheme(const QString &name)
{
- QIcon icon;
- if (qtIconCache()->contains(name)) {
- icon = *qtIconCache()->object(name);
- } else if (QDir::isAbsolutePath(name)) {
+ if (QIcon *cachedIcon = qtIconCache()->object(name))
+ return *cachedIcon;
+
+ if (QDir::isAbsolutePath(name))
return QIcon(name);
- } else {
- QPlatformTheme * const platformTheme = QGuiApplicationPrivate::platformTheme();
- bool hasUserTheme = QIconLoader::instance()->hasUserTheme();
- QIconEngine * const engine = (platformTheme && !hasUserTheme) ? platformTheme->createIconEngine(name)
- : new QIconLoaderEngine(name);
- icon = QIcon(engine);
- if (!icon.isNull())
- qtIconCache()->insert(name, new QIcon(icon));
- }
+ QIcon icon(new QThemeIconEngine(name));
+ qtIconCache()->insert(name, new QIcon(icon));
return icon;
}
/*!
\overload
- Returns the QIcon corresponding to \a name in the current
- icon theme. If no such icon is found in the current theme
- \a fallback is returned instead.
+ Returns the QIcon corresponding to \a name in the
+ \l{themeName()}{current icon theme}.
+
+ If the current theme does not provide an icon for \a name,
+ the \l{fallbackThemeName()}{fallback icon theme} is consulted,
+ before falling back to looking up standalone icon files in the
+ \l{QIcon::fallbackSearchPaths()}{fallback icon search path}.
+ Finally, the platform's native icon library is consulted.
+
+ If no icon is found \a fallback is returned.
+
+ This is useful to provide a guaranteed fallback, regardless of
+ whether the current set of icon themes and fallbacks paths
+ support the requested icon.
- If you want to provide a guaranteed fallback for platforms that
- do not support theme icons, you can use the second argument:
+ For example:
\snippet code/src_gui_image_qicon.cpp 4
+
+ \sa fallbackThemeName(), fallbackSearchPaths()
*/
QIcon QIcon::fromTheme(const QString &name, const QIcon &fallback)
{
@@ -1321,7 +1391,8 @@ QIcon QIcon::fromTheme(const QString &name, const QIcon &fallback)
\since 4.6
Returns \c true if there is an icon available for \a name in the
- current icon theme, otherwise returns \c false.
+ current icon theme or any of the fallbacks, as described by
+ fromTheme(), otherwise returns \c false.
\sa themeSearchPaths(), fromTheme(), setThemeName()
*/
@@ -1332,6 +1403,401 @@ bool QIcon::hasThemeIcon(const QString &name)
return icon.name() == name;
}
+static constexpr auto themeIconMapping = qOffsetStringArray(
+ "address-book-new",
+ "application-exit",
+ "appointment-new",
+ "call-start",
+ "call-stop",
+ "contact-new",
+ "document-new",
+ "document-open",
+ "document-open-recent",
+ "document-page-setup",
+ "document-print",
+ "document-print-preview",
+ "document-properties",
+ "document-revert",
+ "document-save",
+ "document-save-as",
+ "document-send",
+ "edit-clear",
+ "edit-copy",
+ "edit-cut",
+ "edit-delete",
+ "edit-find",
+ "edit-paste",
+ "edit-redo",
+ "edit-select-all",
+ "edit-undo",
+ "folder-new",
+ "format-indent-less",
+ "format-indent-more",
+ "format-justify-center",
+ "format-justify-fill",
+ "format-justify-left",
+ "format-justify-right",
+ "format-text-direction-ltr",
+ "format-text-direction-rtl",
+ "format-text-bold",
+ "format-text-italic",
+ "format-text-underline",
+ "format-text-strikethrough",
+ "go-down",
+ "go-home",
+ "go-next",
+ "go-previous",
+ "go-up",
+ "help-about",
+ "help-faq",
+ "insert-image",
+ "insert-link",
+ "insert-text",
+ "list-add",
+ "list-remove",
+ "mail-forward",
+ "mail-mark-important",
+ "mail-mark-read",
+ "mail-mark-unread",
+ "mail-message-new",
+ "mail-reply-all",
+ "mail-reply-sender",
+ "mail-send",
+ "media-eject",
+ "media-playback-pause",
+ "media-playback-start",
+ "media-playback-stop",
+ "media-record",
+ "media-seek-backward",
+ "media-seek-forward",
+ "media-skip-backward",
+ "media-skip-forward",
+ "object-rotate-left",
+ "object-rotate-right",
+ "process-stop",
+ "system-lock-screen",
+ "system-log-out",
+ "system-search",
+ "system-reboot",
+ "system-shutdown",
+ "tools-check-spelling",
+ "view-fullscreen",
+ "view-refresh",
+ "view-restore",
+ "window-close",
+ "window-new",
+ "zoom-fit-best",
+ "zoom-in",
+ "zoom-out",
+
+ "audio-card",
+ "audio-input-microphone",
+ "battery",
+ "camera-photo",
+ "camera-video",
+ "camera-web",
+ "computer",
+ "drive-harddisk",
+ "drive-optical",
+ "input-gaming",
+ "input-keyboard",
+ "input-mouse",
+ "input-tablet",
+ "media-flash",
+ "media-optical",
+ "media-tape",
+ "multimedia-player",
+ "network-wired",
+ "network-wireless",
+ "phone",
+ "printer",
+ "scanner",
+ "video-display",
+
+ "appointment-missed",
+ "appointment-soon",
+ "audio-volume-high",
+ "audio-volume-low",
+ "audio-volume-medium",
+ "audio-volume-muted",
+ "battery-caution",
+ "battery-low",
+ "dialog-error",
+ "dialog-information",
+ "dialog-password",
+ "dialog-question",
+ "dialog-warning",
+ "folder-drag-accept",
+ "folder-open",
+ "folder-visiting",
+ "image-loading",
+ "image-missing",
+ "mail-attachment",
+ "mail-unread",
+ "mail-read",
+ "mail-replied",
+ "media-playlist-repeat",
+ "media-playlist-shuffle",
+ "network-offline",
+ "printer-printing",
+ "security-high",
+ "security-low",
+ "software-update-available",
+ "software-update-urgent",
+ "sync-error",
+ "sync-synchronizing",
+ "user-available",
+ "user-offline",
+ "weather-clear",
+ "weather-clear-night",
+ "weather-few-clouds",
+ "weather-few-clouds-night",
+ "weather-fog",
+ "weather-showers",
+ "weather-snow",
+ "weather-storm"
+);
+static_assert(QIcon::ThemeIcon::NThemeIcons == QIcon::ThemeIcon(themeIconMapping.count()));
+
+static constexpr QLatin1StringView themeIconName(QIcon::ThemeIcon icon)
+{
+ using ThemeIconIndex = std::underlying_type_t<QIcon::ThemeIcon>;
+ const auto index = static_cast<ThemeIconIndex>(icon);
+ Q_ASSERT(index < themeIconMapping.count());
+ return QLatin1StringView(themeIconMapping.viewAt(index));
+}
+
+/*!
+ \enum QIcon::ThemeIcon
+ \since 6.7
+
+ This enum provides access to icons that are provided by most
+ icon theme implementations.
+
+ \value AddressBookNew The icon for the action to create a new address book.
+ \value ApplicationExit The icon for exiting an application.
+ \value AppointmentNew The icon for the action to create a new appointment.
+ \value CallStart The icon for initiating or accepting a call.
+ \value CallStop The icon for stopping a current call.
+ \value ContactNew The icon for the action to create a new contact.
+ \value DocumentNew The icon for the action to create a new document.
+ \value DocumentOpen The icon for the action to open a document.
+ \value DocumentOpenRecent The icon for the action to open a document that was recently opened.
+ \value DocumentPageSetup The icon for the \e{page setup} action.
+ \value DocumentPrint The icon for the \e{print} action.
+ \value DocumentPrintPreview The icon for the \e{print preview} action.
+ \value DocumentProperties The icon for the action to view the properties of a document.
+ \value DocumentRevert The icon for the action of reverting to a previous version of a document.
+ \value DocumentSave The icon for the \e{save} action.
+ \value DocumentSaveAs The icon for the \e{save as} action.
+ \value DocumentSend The icon for the \e{send} action.
+ \value EditClear The icon for the \e{clear} action.
+ \value EditCopy The icon for the \e{copy} action.
+ \value EditCut The icon for the \e{cut} action.
+ \value EditDelete The icon for the \e{delete} action.
+ \value EditFind The icon for the \e{find} action.
+ \value EditPaste The icon for the \e{paste} action.
+ \value EditRedo The icon for the \e{redo} action.
+ \value EditSelectAll The icon for the \e{select all} action.
+ \value EditUndo The icon for the \e{undo} action.
+ \value FolderNew The icon for creating a new folder.
+ \value FormatIndentLess The icon for the \e{decrease indent formatting} action.
+ \value FormatIndentMore The icon for the \e{increase indent formatting} action.
+ \value FormatJustifyCenter The icon for the \e{center justification formatting} action.
+ \value FormatJustifyFill The icon for the \e{fill justification formatting} action.
+ \value FormatJustifyLeft The icon for the \e{left justification formatting} action.
+ \value FormatJustifyRight The icon for the \e{right justification} action.
+ \value FormatTextDirectionLtr The icon for the \e{left-to-right text formatting} action.
+ \value FormatTextDirectionRtl The icon for the \e{right-to-left formatting} action.
+ \value FormatTextBold The icon for the \e{bold text formatting} action.
+ \value FormatTextItalic The icon for the \e{italic text formatting} action.
+ \value FormatTextUnderline The icon for the \e{underlined text formatting} action.
+ \value FormatTextStrikethrough The icon for the \e{strikethrough text formatting} action.
+ \value GoDown The icon for the \e{go down in a list} action.
+ \value GoHome The icon for the \e{go to home location} action.
+ \value GoNext The icon for the \e{go to the next item in a list} action.
+ \value GoPrevious The icon for the \e{go to the previous item in a list} action.
+ \value GoUp The icon for the \e{go up in a list} action.
+ \value HelpAbout The icon for the \e{About} item in the Help menu.
+ \value HelpFaq The icon for the \e{FAQ} item in the Help menu.
+ \value InsertImage The icon for the \e{insert image} action of an application.
+ \value InsertLink The icon for the \e{insert link} action of an application.
+ \value InsertText The icon for the \e{insert text} action of an application.
+ \value ListAdd The icon for the \e{add to list} action.
+ \value ListRemove The icon for the \e{remove from list} action.
+ \value MailForward The icon for the \e{forward} action.
+ \value MailMarkImportant The icon for the \e{mark as important} action.
+ \value MailMarkRead The icon for the \e{mark as read} action.
+ \value MailMarkUnread The icon for the \e{mark as unread} action.
+ \value MailMessageNew The icon for the \e{compose new mail} action.
+ \value MailReplyAll The icon for the \e{reply to all} action.
+ \value MailReplySender The icon for the \e{reply to sender} action.
+ \value MailSend The icon for the \e{send} action.
+ \value MediaEject The icon for the \e{eject} action of a media player or file manager.
+ \value MediaPlaybackPause The icon for the \e{pause} action of a media player.
+ \value MediaPlaybackStart The icon for the \e{start playback} action of a media player.
+ \value MediaPlaybackStop The icon for the \e{stop} action of a media player.
+ \value MediaRecord The icon for the \e{record} action of a media application.
+ \value MediaSeekBackward The icon for the \e{seek backward} action of a media player.
+ \value MediaSeekForward The icon for the \e{seek forward} action of a media player.
+ \value MediaSkipBackward The icon for the \e{skip backward} action of a media player.
+ \value MediaSkipForward The icon for the \e{skip forward} action of a media player.
+ \value ObjectRotateLeft The icon for the \e{rotate left} action performed on an object.
+ \value ObjectRotateRight The icon for the \e{rotate right} action performed on an object.
+ \value ProcessStop The icon for the \e{stop action in applications with} actions that
+ may take a while to process, such as web page loading in a browser.
+ \value SystemLockScreen The icon for the \e{lock screen} action.
+ \value SystemLogOut The icon for the \e{log out} action.
+ \value SystemSearch The icon for the \e{search} action.
+ \value SystemReboot The icon for the \e{reboot} action.
+ \value SystemShutdown The icon for the \e{shutdown} action.
+ \value ToolsCheckSpelling The icon for the \e{check spelling} action.
+ \value ViewFullscreen The icon for the \e{fullscreen} action.
+ \value ViewRefresh The icon for the \e{refresh} action.
+ \value ViewRestore The icon for leaving the fullscreen view.
+ \value WindowClose The icon for the \e{close window} action.
+ \value WindowNew The icon for the \e{new window} action.
+ \value ZoomFitBest The icon for the \e{best fit} action.
+ \value ZoomIn The icon for the \e{zoom in} action.
+ \value ZoomOut The icon for the \e{zoom out} action.
+
+ \value AudioCard The icon for the audio rendering device.
+ \value AudioInputMicrophone The icon for the microphone audio input device.
+ \value Battery The icon for the system battery device.
+ \value CameraPhoto The icon for a digital still camera devices.
+ \value CameraVideo The icon for a video camera device.
+ \value CameraWeb The icon for a web camera device.
+ \value Computer The icon for the computing device as a whole.
+ \value DriveHarddisk The icon for hard disk drives.
+ \value DriveOptical The icon for optical media drives such as CD and DVD.
+ \value InputGaming The icon for the gaming input device.
+ \value InputKeyboard The icon for the keyboard input device.
+ \value InputMouse The icon for the mousing input device.
+ \value InputTablet The icon for graphics tablet input devices.
+ \value MediaFlash The icon for flash media, such as a memory stick.
+ \value MediaOptical The icon for physical optical media such as CD and DVD.
+ \value MediaTape The icon for generic physical tape media.
+ \value MultimediaPlayer The icon for generic multimedia playing devices.
+ \value NetworkWired The icon for wired network connections.
+ \value NetworkWireless The icon for wireless network connections.
+ \value Phone The icon for phone devices.
+ \value Printer The icon for a printer device.
+ \value Scanner The icon for a scanner device.
+ \value VideoDisplay The icon for the monitor that video gets displayed on.
+
+ \value AppointmentMissed The icon for when an appointment was missed.
+ \value AppointmentSoon The icon for when an appointment will occur soon.
+ \value AudioVolumeHigh The icon used to indicate high audio volume.
+ \value AudioVolumeLow The icon used to indicate low audio volume.
+ \value AudioVolumeMedium The icon used to indicate medium audio volume.
+ \value AudioVolumeMuted The icon used to indicate the muted state for audio playback.
+ \value BatteryCaution The icon used when the battery is below 40%.
+ \value BatteryLow The icon used when the battery is below 20%.
+ \value DialogError The icon used when a dialog is opened to explain an error
+ condition to the user.
+ \value DialogInformation The icon used when a dialog is opened to give information to the
+ user that may be pertinent to the requested action.
+ \value DialogPassword The icon used when a dialog requesting the authentication
+ credentials for a user is opened.
+ \value DialogQuestion The icon used when a dialog is opened to ask a simple question
+ to the user.
+ \value DialogWarning The icon used when a dialog is opened to warn the user of
+ impending issues with the requested action.
+ \value FolderDragAccept The icon used for a folder while an acceptable object is being
+ dragged onto it.
+ \value FolderOpen The icon used for folders, while their contents are being displayed
+ within the same window.
+ \value FolderVisiting The icon used for folders, while their contents are being displayed
+ in another window.
+ \value ImageLoading The icon used while another image is being loaded.
+ \value ImageMissing The icon used when another image could not be loaded.
+ \value MailAttachment The icon for a message that contains attachments.
+ \value MailUnread The icon for an unread message.
+ \value MailRead The icon for a read message.
+ \value MailReplied The icon for a message that has been replied to.
+ \value MediaPlaylistRepeat The icon for the repeat mode of a media player.
+ \value MediaPlaylistShuffle The icon for the shuffle mode of a media player.
+ \value NetworkOffline The icon used to indicate that the device is not connected to the
+ network.
+ \value PrinterPrinting The icon used while a print job is successfully being spooled to a
+ printing device.
+ \value SecurityHigh The icon used to indicate that the security level of an item is
+ known to be high.
+ \value SecurityLow The icon used to indicate that the security level of an item is
+ known to be low.
+ \value SoftwareUpdateAvailable The icon used to indicate that an update is available.
+ \value SoftwareUpdateUrgent The icon used to indicate that an urgent update is available.
+ \value SyncError The icon used when an error occurs while attempting to synchronize
+ data across devices.
+ \value SyncSynchronizing The icon used while data is successfully synchronizing across
+ devices.
+ \value UserAvailable The icon used to indicate that a user is available.
+ \value UserOffline The icon used to indicate that a user is not available.
+ \value WeatherClear The icon used to indicate that the sky is clear.
+ \value WeatherClearNight The icon used to indicate that the sky is clear
+ during the night.
+ \value WeatherFewClouds The icon used to indicate that the sky is partly cloudy.
+ \value WeatherFewCloudsNight The icon used to indicate that the sky is partly cloudy
+ during the night.
+ \value WeatherFog The icon used to indicate that the weather is foggy.
+ \value WeatherShowers The icon used to indicate that rain showers are occurring.
+ \value WeatherSnow The icon used to indicate that snow is falling.
+ \value WeatherStorm The icon used to indicate that the weather is stormy.
+
+ \omitvalue NThemeIcons
+
+ \sa {QIcon#Creating an icon from a theme or icon library},
+ fromTheme()
+*/
+
+/*!
+ \since 6.7
+ \overload
+
+ Returns \c true if there is an icon available for \a icon in the
+ current icon theme or any of the fallbacks, as described by
+ fromTheme(), otherwise returns \c false.
+
+ \sa fromTheme()
+*/
+bool QIcon::hasThemeIcon(QIcon::ThemeIcon icon)
+{
+ return hasThemeIcon(themeIconName(icon));
+}
+
+/*!
+ \fn QIcon QIcon::fromTheme(QIcon::ThemeIcon icon)
+ \fn QIcon QIcon::fromTheme(QIcon::ThemeIcon icon, const QIcon &fallback)
+ \since 6.7
+ \overload
+
+ Returns the QIcon corresponding to \a icon in the
+ \l{themeName()}{current icon theme}.
+
+ If the current theme does not provide an icon for \a icon,
+ the \l{fallbackThemeName()}{fallback icon theme} is consulted,
+ before falling back to looking up standalone icon files in the
+ \l{QIcon::fallbackSearchPaths()}{fallback icon search path}.
+ Finally, the platform's native icon library is consulted.
+
+ If no icon is found and a \a fallback is provided, \a fallback is
+ returned. This is useful to provide a guaranteed fallback, regardless
+ of whether the current set of icon themes and fallbacks paths
+ support the requested icon.
+
+ If no icon is found and no \a fallback is provided, a default
+ constructed, empty QIcon is returned.
+*/
+QIcon QIcon::fromTheme(QIcon::ThemeIcon icon)
+{
+ return fromTheme(themeIconName(icon));
+}
+
+QIcon QIcon::fromTheme(QIcon::ThemeIcon icon, const QIcon &fallback)
+{
+ return fromTheme(themeIconName(icon), fallback);
+}
+
/*!
\since 5.6
@@ -1426,13 +1892,13 @@ QDataStream &operator>>(QDataStream &s, QIcon &icon)
if (key == "QPixmapIconEngine"_L1) {
icon.d = new QIconPrivate(new QPixmapIconEngine);
icon.d->engine->read(s);
- } else if (key == "QIconLoaderEngine"_L1) {
- icon.d = new QIconPrivate(new QIconLoaderEngine());
+ } else if (key == "QIconLoaderEngine"_L1 || key == "QThemeIconEngine"_L1) {
+ icon.d = new QIconPrivate(new QThemeIconEngine);
icon.d->engine->read(s);
} else {
- const int index = loader()->indexOf(key);
+ const int index = iceLoader()->indexOf(key);
if (index != -1) {
- if (QIconEnginePlugin *factory = qobject_cast<QIconEnginePlugin*>(loader()->instance(index))) {
+ if (QIconEnginePlugin *factory = qobject_cast<QIconEnginePlugin*>(iceLoader()->instance(index))) {
if (QIconEngine *engine= factory->create()) {
icon.d = new QIconPrivate(engine);
engine->read(s);
diff --git a/src/gui/image/qicon.h b/src/gui/image/qicon.h
index 73afad431e..5100ada548 100644
--- a/src/gui/image/qicon.h
+++ b/src/gui/image/qicon.h
@@ -22,11 +22,168 @@ public:
enum Mode { Normal, Disabled, Active, Selected };
enum State { On, Off };
+ enum class ThemeIcon {
+ AddressBookNew,
+ ApplicationExit,
+ AppointmentNew,
+ CallStart,
+ CallStop,
+ ContactNew,
+ DocumentNew,
+ DocumentOpen,
+ DocumentOpenRecent,
+ DocumentPageSetup,
+ DocumentPrint,
+ DocumentPrintPreview,
+ DocumentProperties,
+ DocumentRevert,
+ DocumentSave,
+ DocumentSaveAs,
+ DocumentSend,
+ EditClear,
+ EditCopy,
+ EditCut,
+ EditDelete,
+ EditFind,
+ EditPaste,
+ EditRedo,
+ EditSelectAll,
+ EditUndo,
+ FolderNew,
+ FormatIndentLess,
+ FormatIndentMore,
+ FormatJustifyCenter,
+ FormatJustifyFill,
+ FormatJustifyLeft,
+ FormatJustifyRight,
+ FormatTextDirectionLtr,
+ FormatTextDirectionRtl,
+ FormatTextBold,
+ FormatTextItalic,
+ FormatTextUnderline,
+ FormatTextStrikethrough,
+ GoDown,
+ GoHome,
+ GoNext,
+ GoPrevious,
+ GoUp,
+ HelpAbout,
+ HelpFaq,
+ InsertImage,
+ InsertLink,
+ InsertText,
+ ListAdd,
+ ListRemove,
+ MailForward,
+ MailMarkImportant,
+ MailMarkRead,
+ MailMarkUnread,
+ MailMessageNew,
+ MailReplyAll,
+ MailReplySender,
+ MailSend,
+ MediaEject,
+ MediaPlaybackPause,
+ MediaPlaybackStart,
+ MediaPlaybackStop,
+ MediaRecord,
+ MediaSeekBackward,
+ MediaSeekForward,
+ MediaSkipBackward,
+ MediaSkipForward,
+ ObjectRotateLeft,
+ ObjectRotateRight,
+ ProcessStop,
+ SystemLockScreen,
+ SystemLogOut,
+ SystemSearch,
+ SystemReboot,
+ SystemShutdown,
+ ToolsCheckSpelling,
+ ViewFullscreen,
+ ViewRefresh,
+ ViewRestore,
+ WindowClose,
+ WindowNew,
+ ZoomFitBest,
+ ZoomIn,
+ ZoomOut,
+
+ AudioCard,
+ AudioInputMicrophone,
+ Battery,
+ CameraPhoto,
+ CameraVideo,
+ CameraWeb,
+ Computer,
+ DriveHarddisk,
+ DriveOptical,
+ InputGaming,
+ InputKeyboard,
+ InputMouse,
+ InputTablet,
+ MediaFlash,
+ MediaOptical,
+ MediaTape,
+ MultimediaPlayer,
+ NetworkWired,
+ NetworkWireless,
+ Phone,
+ Printer,
+ Scanner,
+ VideoDisplay,
+
+ AppointmentMissed,
+ AppointmentSoon,
+ AudioVolumeHigh,
+ AudioVolumeLow,
+ AudioVolumeMedium,
+ AudioVolumeMuted,
+ BatteryCaution,
+ BatteryLow,
+ DialogError,
+ DialogInformation,
+ DialogPassword,
+ DialogQuestion,
+ DialogWarning,
+ FolderDragAccept,
+ FolderOpen,
+ FolderVisiting,
+ ImageLoading,
+ ImageMissing,
+ MailAttachment,
+ MailUnread,
+ MailRead,
+ MailReplied,
+ MediaPlaylistRepeat,
+ MediaPlaylistShuffle,
+ NetworkOffline,
+ PrinterPrinting,
+ SecurityHigh,
+ SecurityLow,
+ SoftwareUpdateAvailable,
+ SoftwareUpdateUrgent,
+ SyncError,
+ SyncSynchronizing,
+ UserAvailable,
+ UserOffline,
+ WeatherClear,
+ WeatherClearNight,
+ WeatherFewClouds,
+ WeatherFewCloudsNight,
+ WeatherFog,
+ WeatherShowers,
+ WeatherSnow,
+ WeatherStorm,
+
+ NThemeIcons
+ };
+
QIcon() noexcept;
QIcon(const QPixmap &pixmap);
QIcon(const QIcon &other);
QIcon(QIcon &&other) noexcept
- : d(qExchange(other.d, nullptr))
+ : d(std::exchange(other.d, nullptr))
{}
explicit QIcon(const QString &fileName); // file or resource name
explicit QIcon(QIconEngine *engine);
@@ -81,6 +238,10 @@ public:
static QIcon fromTheme(const QString &name, const QIcon &fallback);
static bool hasThemeIcon(const QString &name);
+ static QIcon fromTheme(ThemeIcon icon);
+ static QIcon fromTheme(ThemeIcon icon, const QIcon &fallback);
+ static bool hasThemeIcon(ThemeIcon icon);
+
static QStringList themeSearchPaths();
static void setThemeSearchPaths(const QStringList &searchpath);
@@ -93,8 +254,6 @@ public:
static QString fallbackThemeName();
static void setFallbackThemeName(const QString &name);
- Q_DUMMY_COMPARISON_OPERATOR(QIcon)
-
private:
QIconPrivate *d;
#if !defined(QT_NO_DATASTREAM)
diff --git a/src/gui/image/qicon_p.h b/src/gui/image/qicon_p.h
index ab144e61ff..c5bf120620 100644
--- a/src/gui/image/qicon_p.h
+++ b/src/gui/image/qicon_p.h
@@ -42,6 +42,8 @@ public:
int serialNum;
int detach_no;
bool is_mask;
+
+ static void clearIconCache();
};
@@ -82,7 +84,7 @@ public:
QList<QSize> availableSizes(QIcon::Mode mode, QIcon::State state) override;
void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state) override;
void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state) override;
-
+ bool isNull() override;
QString key() const override;
QIconEngine *clone() const override;
diff --git a/src/gui/image/qiconengine.cpp b/src/gui/image/qiconengine.cpp
index 49528c7034..78273bdeb3 100644
--- a/src/gui/image/qiconengine.cpp
+++ b/src/gui/image/qiconengine.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qiconengine.h"
+#include "qiconengine_p.h"
#include "qpainter.h"
QT_BEGIN_NAMESPACE
@@ -307,4 +308,78 @@ QPixmap QIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::St
return arg.pixmap;
}
+
+// ------- QProxyIconEngine -----
+
+void QProxyIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
+{
+ proxiedEngine()->paint(painter, rect, mode, state);
+}
+
+QSize QProxyIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ return proxiedEngine()->actualSize(size, mode, state);
+}
+
+QPixmap QProxyIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ return proxiedEngine()->pixmap(size, mode, state);
+}
+
+void QProxyIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state)
+{
+ proxiedEngine()->addPixmap(pixmap, mode, state);
+}
+
+void QProxyIconEngine::addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ proxiedEngine()->addFile(fileName, size, mode, state);
+}
+
+QString QProxyIconEngine::key() const
+{
+ return proxiedEngine()->key();
+}
+
+QIconEngine *QProxyIconEngine::clone() const
+{
+ return proxiedEngine()->clone();
+}
+
+bool QProxyIconEngine::read(QDataStream &in)
+{
+ return proxiedEngine()->read(in);
+}
+
+bool QProxyIconEngine::write(QDataStream &out) const
+{
+ return proxiedEngine()->write(out);
+}
+
+QList<QSize> QProxyIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state)
+{
+ return proxiedEngine()->availableSizes(mode, state);
+}
+
+QString QProxyIconEngine::iconName()
+{
+ return proxiedEngine()->iconName();
+}
+
+bool QProxyIconEngine::isNull()
+{
+ return proxiedEngine()->isNull();
+}
+
+QPixmap QProxyIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
+{
+ return proxiedEngine()->scaledPixmap(size, mode, state, scale);
+}
+
+void QProxyIconEngine::virtual_hook(int id, void *data)
+{
+ proxiedEngine()->virtual_hook(id, data);
+}
+
+
QT_END_NAMESPACE
diff --git a/src/gui/image/qiconengine_p.h b/src/gui/image/qiconengine_p.h
new file mode 100644
index 0000000000..3cf0998429
--- /dev/null
+++ b/src/gui/image/qiconengine_p.h
@@ -0,0 +1,59 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QICONENGINE_P_H
+#define QICONENGINE_P_H
+
+#include <QtGui/private/qtguiglobal_p.h>
+
+#ifndef QT_NO_ICON
+//
+// 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/QIcon>
+#include <QtGui/QIconEngine>
+
+QT_BEGIN_NAMESPACE
+
+class QIconEngine;
+
+class QProxyIconEngine : public QIconEngine
+{
+public:
+ void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override;
+ QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
+ QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
+
+ void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state) override;
+ void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state) override;
+
+ QString key() const override;
+ QIconEngine *clone() const override;
+ bool read(QDataStream &in) override;
+ bool write(QDataStream &out) const override;
+
+ QList<QSize> availableSizes(QIcon::Mode mode = QIcon::Normal,
+ QIcon::State state = QIcon::Off) override;
+
+ QString iconName() override;
+ bool isNull() override;
+ QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) override;
+
+ void virtual_hook(int id, void *data) override;
+protected:
+ virtual QIconEngine *proxiedEngine() const = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ICON
+
+#endif // QICONENGINE_P_H
diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp
index 6756826690..982b9a26b4 100644
--- a/src/gui/image/qiconloader.cpp
+++ b/src/gui/image/qiconloader.cpp
@@ -14,6 +14,7 @@
#include <QtCore/qmath.h>
#include <QtCore/QList>
#include <QtCore/QDir>
+#include <QtCore/qloggingcategory.h>
#if QT_CONFIG(settings)
#include <QtCore/QSettings>
#endif
@@ -23,6 +24,8 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcIconLoader, "qt.gui.icon.loader")
+
using namespace Qt::StringLiterals;
Q_GLOBAL_STATIC(QIconLoader, iconLoaderInstance)
@@ -90,6 +93,9 @@ void QIconLoader::ensureInitialized()
m_systemTheme = systemFallbackThemeName();
if (qt_iconEngineFactoryLoader()->keyMap().key("svg"_L1, -1) != -1)
m_supportsSvg = true;
+
+ qCDebug(lcIconLoader) << "Initialized icon loader with system theme"
+ << m_systemTheme << "and SVG support" << m_supportsSvg;
}
}
@@ -113,21 +119,45 @@ QIconLoader *QIconLoader::instance()
// icons if the theme has changed.
void QIconLoader::updateSystemTheme()
{
- // Only change if this is not explicitly set by the user
- if (m_userTheme.isEmpty()) {
- QString theme = systemThemeName();
- if (theme.isEmpty())
- theme = fallbackThemeName();
- if (theme != m_systemTheme) {
- m_systemTheme = theme;
- invalidateKey();
- }
- }
+ const QString currentSystemTheme = m_systemTheme;
+ m_systemTheme = systemThemeName();
+ if (m_systemTheme.isEmpty())
+ m_systemTheme = systemFallbackThemeName();
+ if (m_systemTheme != currentSystemTheme)
+ qCDebug(lcIconLoader) << "Updated system theme to" << m_systemTheme;
+ // Invalidate even if the system theme name hasn't changed, as the
+ // theme itself may have changed its underlying icon lookup logic.
+ if (!hasUserTheme())
+ invalidateKey();
+}
+
+void QIconLoader::invalidateKey()
+{
+ // Invalidating the key here will result in QThemeIconEngine
+ // recreating the actual engine the next time the icon is used.
+ // We don't need to clear the QIcon cache itself.
+ m_themeKey++;
+}
+
+QString QIconLoader::themeName() const
+{
+ return m_userTheme.isEmpty() ? m_systemTheme : m_userTheme;
}
void QIconLoader::setThemeName(const QString &themeName)
{
+ if (m_userTheme == themeName)
+ return;
+
+ qCDebug(lcIconLoader) << "Setting user theme name to" << themeName;
+
+ const bool hadUserTheme = hasUserTheme();
m_userTheme = themeName;
+ // if we cleared the user theme, then reset search paths as well,
+ // otherwise we'll keep looking in the user-defined search paths for
+ // a system-provide theme, which will never work.
+ if (!hasUserTheme() && hadUserTheme)
+ setThemeSearchPath(systemIconSearchPaths());
invalidateKey();
}
@@ -138,11 +168,14 @@ QString QIconLoader::fallbackThemeName() const
void QIconLoader::setFallbackThemeName(const QString &themeName)
{
+ qCDebug(lcIconLoader) << "Setting fallback theme name to" << themeName;
m_userFallbackTheme = themeName;
+ invalidateKey();
}
void QIconLoader::setThemeSearchPath(const QStringList &searchPaths)
{
+ qCDebug(lcIconLoader) << "Setting theme search path to" << searchPaths;
m_iconDirs = searchPaths;
themeList.clear();
invalidateKey();
@@ -160,6 +193,7 @@ QStringList QIconLoader::themeSearchPaths() const
void QIconLoader::setFallbackSearchPaths(const QStringList &searchPaths)
{
+ qCDebug(lcIconLoader) << "Setting fallback search path to" << searchPaths;
m_fallbackDirs = searchPaths;
invalidateKey();
}
@@ -215,7 +249,7 @@ QIconCacheGtkReader::QIconCacheGtkReader(const QString &dirName)
: m_isValid(false)
{
QFileInfo info(dirName + "/icon-theme.cache"_L1);
- if (!info.exists() || info.lastModified() < QFileInfo(dirName).lastModified())
+ if (!info.exists() || info.lastModified(QTimeZone::UTC) < QFileInfo(dirName).lastModified(QTimeZone::UTC))
return;
m_file.setFileName(info.absoluteFilePath());
if (!m_file.open(QFile::ReadOnly))
@@ -230,13 +264,13 @@ QIconCacheGtkReader::QIconCacheGtkReader(const QString &dirName)
m_isValid = true;
// Check that all the directories are older than the cache
- auto lastModified = info.lastModified();
+ const QDateTime lastModified = info.lastModified(QTimeZone::UTC);
quint32 dirListOffset = read32(8);
quint32 dirListLen = read32(dirListOffset);
for (uint i = 0; i < dirListLen; ++i) {
quint32 offset = read32(dirListOffset + 4 + 4 * i);
if (!m_isValid || offset >= m_size || lastModified < QFileInfo(dirName + u'/'
- + QString::fromUtf8(reinterpret_cast<const char*>(m_data + offset))).lastModified()) {
+ + QString::fromUtf8(reinterpret_cast<const char*>(m_data + offset))).lastModified(QTimeZone::UTC)) {
m_isValid = false;
return;
}
@@ -324,12 +358,12 @@ QIconTheme::QIconTheme(const QString &themeName)
if (!m_valid) {
themeIndex.setFileName(themeDir + "/index.theme"_L1);
- if (themeIndex.exists())
- m_valid = true;
+ m_valid = themeIndex.exists();
+ qCDebug(lcIconLoader) << "Probing theme file at" << themeIndex.fileName() << m_valid;
}
}
#if QT_CONFIG(settings)
- if (themeIndex.exists()) {
+ if (m_valid) {
const QSettings indexReader(themeIndex.fileName(), QSettings::IniFormat);
const QStringList keys = indexReader.allKeys();
for (const QString &key : keys) {
@@ -358,6 +392,17 @@ QIconTheme::QIconTheme(const QString &themeName)
dirInfo.maxSize = indexReader.value(directoryKey + "/MaxSize"_L1, size).toInt();
dirInfo.scale = indexReader.value(directoryKey + "/Scale"_L1, 1).toInt();
+
+ const QString context = indexReader.value(directoryKey + "/Context"_L1).toString();
+ dirInfo.context = [context]() {
+ if (context == "Applications"_L1)
+ return QIconDirInfo::Applications;
+ else if (context == "MimeTypes"_L1)
+ return QIconDirInfo::MimeTypes;
+ else
+ return QIconDirInfo::UnknownContext;
+ }();
+
m_keyList.append(dirInfo);
}
}
@@ -366,25 +411,42 @@ QIconTheme::QIconTheme(const QString &themeName)
// Parent themes provide fallbacks for missing icons
m_parents = indexReader.value("Icon Theme/Inherits"_L1).toStringList();
m_parents.removeAll(QString());
-
- // Ensure a default platform fallback for all themes
- if (m_parents.isEmpty()) {
- const QString fallback = QIconLoader::instance()->fallbackThemeName();
- if (!fallback.isEmpty())
- m_parents.append(fallback);
- }
-
- // Ensure that all themes fall back to hicolor
- if (!m_parents.contains("hicolor"_L1))
- m_parents.append("hicolor"_L1);
}
#endif // settings
}
+QStringList QIconTheme::parents() const
+{
+ // Respect explicitly declared parents
+ QStringList result = m_parents;
+
+ // Ensure a default fallback for all themes
+ const QString fallback = QIconLoader::instance()->fallbackThemeName();
+ if (!fallback.isEmpty())
+ result.append(fallback);
+
+ // Ensure that all themes fall back to hicolor as the last theme
+ result.removeAll("hicolor"_L1);
+ result.append("hicolor"_L1);
+
+ return result;
+}
+
+QDebug operator<<(QDebug debug, const std::unique_ptr<QIconLoaderEngineEntry> &entry)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote() << entry->filename;
+ return debug;
+}
+
QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName,
const QString &iconName,
- QStringList &visited) const
+ QStringList &visited,
+ DashRule rule) const
{
+ qCDebug(lcIconLoader) << "Finding icon" << iconName << "in theme" << themeName
+ << "skipping" << visited;
+
QThemeIconInfo info;
Q_ASSERT(!themeName.isEmpty());
@@ -394,16 +456,19 @@ QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName,
QIconTheme &theme = themeList[themeName];
if (!theme.isValid()) {
theme = QIconTheme(themeName);
- if (!theme.isValid())
- theme = QIconTheme(fallbackThemeName());
+ if (!theme.isValid()) {
+ qCDebug(lcIconLoader) << "Theme" << themeName << "not found";
+ return info;
+ }
}
const QStringList contentDirs = theme.contentDirs();
QStringView iconNameFallback(iconName);
+ bool searchingGenericFallback = m_iconName.length() > iconName.length();
// Iterate through all icon's fallbacks in current theme
- while (info.entries.empty()) {
+ if (info.entries.empty()) {
const QString svgIconName = iconNameFallback + ".svg"_L1;
const QString pngIconName = iconNameFallback + ".png"_L1;
@@ -419,7 +484,7 @@ QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName,
if (cache->isValid()) {
const QList<QIconDirInfo> subDirsCopy = subDirs;
subDirs.clear();
- subDirs.reserve(result.count());
+ subDirs.reserve(result.size());
for (const char *s : result) {
QString path = QString::fromUtf8(s);
auto it = std::find_if(subDirsCopy.cbegin(), subDirsCopy.cend(),
@@ -435,6 +500,11 @@ QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName,
QString contentDir = contentDirs.at(i) + u'/';
for (int j = 0; j < subDirs.size() ; ++j) {
const QIconDirInfo &dirInfo = subDirs.at(j);
+ if (searchingGenericFallback &&
+ (dirInfo.context == QIconDirInfo::Applications ||
+ dirInfo.context == QIconDirInfo::MimeTypes))
+ continue;
+
const QString subDir = contentDir + dirInfo.path + u'/';
const QString pngPath = subDir + pngIconName;
if (QFile::exists(pngPath)) {
@@ -458,36 +528,47 @@ QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName,
if (!info.entries.empty()) {
info.iconName = iconNameFallback.toString();
- break;
}
-
- // If it's possible - find next fallback for the icon
- const int indexOfDash = iconNameFallback.lastIndexOf(u'-');
- if (indexOfDash == -1)
- break;
-
- iconNameFallback.truncate(indexOfDash);
}
if (info.entries.empty()) {
const QStringList parents = theme.parents();
+ qCDebug(lcIconLoader) << "Did not find matching icons in theme;"
+ << "trying parent themes" << parents
+ << "skipping visited" << visited;
+
// Search recursively through inherited themes
for (int i = 0 ; i < parents.size() ; ++i) {
const QString parentTheme = parents.at(i).trimmed();
if (!visited.contains(parentTheme)) // guard against recursion
- info = findIconHelper(parentTheme, iconName, visited);
+ info = findIconHelper(parentTheme, iconName, visited, QIconLoader::NoFallBack);
if (!info.entries.empty()) // success
break;
}
}
+
+ if (rule == QIconLoader::FallBack && info.entries.empty()) {
+ // If it's possible - find next fallback for the icon
+ const int indexOfDash = iconNameFallback.lastIndexOf(u'-');
+ if (indexOfDash != -1) {
+ qCDebug(lcIconLoader) << "Did not find matching icons in all themes;"
+ << "trying dash fallback";
+ iconNameFallback.truncate(indexOfDash);
+ QStringList _visited;
+ info = findIconHelper(themeName, iconNameFallback.toString(), _visited, QIconLoader::FallBack);
+ }
+ }
+
return info;
}
QThemeIconInfo QIconLoader::lookupFallbackIcon(const QString &iconName) const
{
+ qCDebug(lcIconLoader) << "Looking up fallback icon" << iconName;
+
QThemeIconInfo info;
const QString pngIconName = iconName + ".png"_L1;
@@ -526,64 +607,162 @@ QThemeIconInfo QIconLoader::lookupFallbackIcon(const QString &iconName) const
QThemeIconInfo QIconLoader::loadIcon(const QString &name) const
{
- if (!themeName().isEmpty()) {
- QStringList visited;
- QThemeIconInfo iconInfo = findIconHelper(themeName(), name, visited);
- if (!iconInfo.entries.empty())
- return iconInfo;
+ qCDebug(lcIconLoader) << "Loading icon" << name;
+
+ m_iconName = name;
+ QThemeIconInfo iconInfo;
+ QStringList visitedThemes;
+ if (!themeName().isEmpty())
+ iconInfo = findIconHelper(themeName(), name, visitedThemes, QIconLoader::FallBack);
+
+ if (iconInfo.entries.empty() && !fallbackThemeName().isEmpty())
+ iconInfo = findIconHelper(fallbackThemeName(), name, visitedThemes, QIconLoader::FallBack);
+
+ if (iconInfo.entries.empty())
+ iconInfo = lookupFallbackIcon(name);
+
+ qCDebug(lcIconLoader) << "Resulting icon entries" << iconInfo.entries;
+ return iconInfo;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, QIconEngine *engine)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace();
+ if (engine) {
+ debug.noquote() << engine->key() << "(";
+ debug << static_cast<const void *>(engine);
+ if (!engine->isNull())
+ debug.quote() << ", " << engine->iconName();
+ else
+ debug << ", null";
+ debug << ")";
+ } else {
+ debug << "QIconEngine(nullptr)";
+ }
+ return debug;
+}
+#endif
- return lookupFallbackIcon(name);
+QIconEngine *QIconLoader::iconEngine(const QString &iconName) const
+{
+ qCDebug(lcIconLoader) << "Resolving icon engine for icon" << iconName;
+
+ std::unique_ptr<QIconEngine> iconEngine;
+ if (hasUserTheme())
+ iconEngine.reset(new QIconLoaderEngine(iconName));
+ if (!iconEngine || iconEngine->isNull()) {
+ qCDebug(lcIconLoader) << "Icon is not available from theme or fallback theme.";
+ if (auto *platformTheme = QGuiApplicationPrivate::platformTheme()) {
+ qCDebug(lcIconLoader) << "Trying platform engine.";
+ std::unique_ptr<QIconEngine> themeEngine(platformTheme->createIconEngine(iconName));
+ if (themeEngine && !themeEngine->isNull()) {
+ iconEngine = std::move(themeEngine);
+ qCDebug(lcIconLoader) << "Icon provided by platform engine.";
+ }
+ }
}
+ // We need to maintain the invariant that the QIcon has a valid engine
+ if (!iconEngine)
+ iconEngine.reset(new QIconLoaderEngine(iconName));
- return QThemeIconInfo();
+ qCDebug(lcIconLoader) << "Resulting engine" << iconEngine.get();
+ return iconEngine.release();
}
+/*!
+ \internal
+ \class QThemeIconEngine
+ \inmodule QtGui
+
+ \brief A named-based icon engine for providing theme icons.
-// -------- Icon Loader Engine -------- //
+ The engine supports invalidation of prior lookups, e.g. when
+ the platform theme changes or the user sets an explicit icon
+ theme.
+ The actual icon lookup is handed over to an engine provided
+ by QIconLoader::iconEngine().
+*/
-QIconLoaderEngine::QIconLoaderEngine(const QString& iconName)
- : m_iconName(iconName), m_key(0)
+QThemeIconEngine::QThemeIconEngine(const QString& iconName)
+ : QProxyIconEngine()
+ , m_iconName(iconName)
{
}
-QIconLoaderEngine::~QIconLoaderEngine() = default;
+QThemeIconEngine::QThemeIconEngine(const QThemeIconEngine &other)
+ : QProxyIconEngine()
+ , m_iconName(other.m_iconName)
+{
+}
-QIconLoaderEngine::QIconLoaderEngine(const QIconLoaderEngine &other)
- : QIconEngine(other),
- m_iconName(other.m_iconName),
- m_key(0)
+QString QThemeIconEngine::key() const
{
+ // Although we proxy the underlying engine, that's an implementation
+ // detail, so from the point of view of QIcon, and in terms of
+ // serialization, we are the one and only theme icon engine.
+ return u"QThemeIconEngine"_s;
}
-QIconEngine *QIconLoaderEngine::clone() const
+QIconEngine *QThemeIconEngine::clone() const
{
- return new QIconLoaderEngine(*this);
+ return new QThemeIconEngine(*this);
}
-bool QIconLoaderEngine::read(QDataStream &in) {
+bool QThemeIconEngine::read(QDataStream &in) {
in >> m_iconName;
return true;
}
-bool QIconLoaderEngine::write(QDataStream &out) const
+bool QThemeIconEngine::write(QDataStream &out) const
{
out << m_iconName;
return true;
}
-bool QIconLoaderEngine::hasIcon() const
+QIconEngine *QThemeIconEngine::proxiedEngine() const
{
- return !(m_info.entries.empty());
+ const auto *iconLoader = QIconLoader::instance();
+ auto mostRecentThemeKey = iconLoader->themeKey();
+ if (mostRecentThemeKey != m_themeKey) {
+ qCDebug(lcIconLoader) << "Theme key" << mostRecentThemeKey << "is different"
+ << "than cached key" << m_themeKey << "for icon" << m_iconName;
+ m_proxiedEngine.reset(iconLoader->iconEngine(m_iconName));
+ m_themeKey = mostRecentThemeKey;
+ }
+ return m_proxiedEngine.get();
}
-// Lazily load the icon
-void QIconLoaderEngine::ensureLoaded()
+/*!
+ \internal
+ \class QIconLoaderEngine
+ \inmodule QtGui
+
+ \brief An icon engine based on icon entries collected by QIconLoader.
+
+ The design and implementation of QIconLoader is based on
+ the XDG icon specification.
+*/
+
+QIconLoaderEngine::QIconLoaderEngine(const QString& iconName)
+ : m_iconName(iconName)
+ , m_info(QIconLoader::instance()->loadIcon(m_iconName))
{
- if (QIconLoader::instance()->themeKey() != m_key) {
- m_info = QIconLoader::instance()->loadIcon(m_iconName);
- m_key = QIconLoader::instance()->themeKey();
- }
+}
+
+QIconLoaderEngine::~QIconLoaderEngine() = default;
+
+QIconEngine *QIconLoaderEngine::clone() const
+{
+ Q_UNREACHABLE();
+ return nullptr; // Cannot be cloned
+}
+
+bool QIconLoaderEngine::hasIcon() const
+{
+ return !(m_info.entries.empty());
}
void QIconLoaderEngine::paint(QPainter *painter, const QRect &rect,
@@ -691,8 +870,6 @@ QSize QIconLoaderEngine::actualSize(const QSize &size, QIcon::Mode mode,
Q_UNUSED(mode);
Q_UNUSED(state);
- ensureLoaded();
-
QIconLoaderEngineEntry *entry = entryForSize(m_info, size);
if (entry) {
const QIconDirInfo &dir = entry->dir;
@@ -701,7 +878,7 @@ QSize QIconLoaderEngine::actualSize(const QSize &size, QIcon::Mode mode,
} else if (dir.type == QIconDirInfo::Fallback) {
return QIcon(entry->filename).actualSize(size, mode, state);
} else {
- int result = qMin<int>(dir.size, qMin(size.width(), size.height()));
+ int result = qMin<int>(dir.size * dir.scale, qMin(size.width(), size.height()));
return QSize(result, result);
}
}
@@ -761,8 +938,6 @@ QPixmap ScalableEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State
QPixmap QIconLoaderEngine::pixmap(const QSize &size, QIcon::Mode mode,
QIcon::State state)
{
- ensureLoaded();
-
QIconLoaderEngineEntry *entry = entryForSize(m_info, size);
if (entry)
return entry->pixmap(size, mode, state);
@@ -772,24 +947,21 @@ QPixmap QIconLoaderEngine::pixmap(const QSize &size, QIcon::Mode mode,
QString QIconLoaderEngine::key() const
{
- return "QIconLoaderEngine"_L1;
+ return u"QIconLoaderEngine"_s;
}
QString QIconLoaderEngine::iconName()
{
- ensureLoaded();
return m_info.iconName;
}
bool QIconLoaderEngine::isNull()
{
- ensureLoaded();
return m_info.entries.empty();
}
QPixmap QIconLoaderEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
{
- ensureLoaded();
const int integerScale = qCeil(scale);
QIconLoaderEngineEntry *entry = entryForSize(m_info, size / integerScale, integerScale);
return entry ? entry->pixmap(size, mode, state) : QPixmap();
@@ -799,7 +971,7 @@ QList<QSize> QIconLoaderEngine::availableSizes(QIcon::Mode mode, QIcon::State st
{
Q_UNUSED(mode);
Q_UNUSED(state);
- ensureLoaded();
+
const qsizetype N = qsizetype(m_info.entries.size());
QList<QSize> sizes;
sizes.reserve(N);
diff --git a/src/gui/image/qiconloader_p.h b/src/gui/image/qiconloader_p.h
index cd3227c207..3cfa9381d1 100644
--- a/src/gui/image/qiconloader_p.h
+++ b/src/gui/image/qiconloader_p.h
@@ -22,6 +22,7 @@
#include <QtGui/QIconEngine>
#include <QtGui/QPixmapCache>
#include <private/qicon_p.h>
+#include <private/qiconengine_p.h>
#include <private/qfactoryloader_p.h>
#include <QtCore/QHash>
#include <QtCore/QList>
@@ -37,6 +38,7 @@ class QIconLoader;
struct QIconDirInfo
{
enum Type { Fixed, Scalable, Threshold, Fallback };
+ enum Context { UnknownContext, Applications, MimeTypes };
QIconDirInfo(const QString &_path = QString()) :
path(_path),
size(0),
@@ -44,7 +46,8 @@ struct QIconDirInfo
minSize(0),
threshold(0),
scale(1),
- type(Threshold) {}
+ type(Threshold),
+ context(UnknownContext) {}
QString path;
short size;
short maxSize;
@@ -52,6 +55,7 @@ struct QIconDirInfo
short threshold;
short scale;
Type type;
+ Context context;
};
Q_DECLARE_TYPEINFO(QIconDirInfo, Q_RELOCATABLE_TYPE);
@@ -86,6 +90,27 @@ struct QThemeIconInfo
QString iconName;
};
+class QThemeIconEngine : public QProxyIconEngine
+{
+public:
+ QThemeIconEngine(const QString& iconName = QString());
+ QIconEngine *clone() const override;
+ bool read(QDataStream &in) override;
+ bool write(QDataStream &out) const override;
+
+protected:
+ QIconEngine *proxiedEngine() const override;
+
+private:
+ QThemeIconEngine(const QThemeIconEngine &other);
+ QString key() const override;
+
+ QString m_iconName;
+ mutable uint m_themeKey = 0;
+
+ mutable std::unique_ptr<QIconEngine> m_proxiedEngine;
+};
+
class QIconLoaderEngine : public QIconEngine
{
public:
@@ -96,8 +121,6 @@ public:
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
QIconEngine *clone() const override;
- bool read(QDataStream &in) override;
- bool write(QDataStream &out) const override;
QString iconName() override;
bool isNull() override;
@@ -107,14 +130,13 @@ public:
Q_GUI_EXPORT static QIconLoaderEngineEntry *entryForSize(const QThemeIconInfo &info, const QSize &size, int scale = 1);
private:
+ Q_DISABLE_COPY(QIconLoaderEngine)
+
QString key() const override;
bool hasIcon() const;
- void ensureLoaded();
- QIconLoaderEngine(const QIconLoaderEngine &other);
- QThemeIconInfo m_info;
QString m_iconName;
- uint m_key;
+ QThemeIconInfo m_info;
friend class QIconLoader;
};
@@ -126,7 +148,7 @@ class QIconTheme
public:
QIconTheme(const QString &name);
QIconTheme() : m_valid(false) {}
- QStringList parents() { return m_parents; }
+ QStringList parents() const;
QList<QIconDirInfo> keyList() { return m_keyList; }
QStringList contentDirs() { return m_contentDirs; }
bool isValid() { return m_valid; }
@@ -146,7 +168,7 @@ public:
QThemeIconInfo loadIcon(const QString &iconName) const;
uint themeKey() const { return m_themeKey; }
- QString themeName() const { return m_userTheme.isEmpty() ? m_systemTheme : m_userTheme; }
+ QString themeName() const;
void setThemeName(const QString &themeName);
QString fallbackThemeName() const;
void setFallbackThemeName(const QString &themeName);
@@ -158,14 +180,18 @@ public:
QIconDirInfo dirInfo(int dirindex);
static QIconLoader *instance();
void updateSystemTheme();
- void invalidateKey() { m_themeKey++; }
+ void invalidateKey();
void ensureInitialized();
bool hasUserTheme() const { return !m_userTheme.isEmpty(); }
+ QIconEngine *iconEngine(const QString &iconName) const;
+
private:
+ enum DashRule { FallBack, NoFallBack };
QThemeIconInfo findIconHelper(const QString &themeName,
const QString &iconName,
- QStringList &visited) const;
+ QStringList &visited,
+ DashRule rule) const;
QThemeIconInfo lookupFallbackIcon(const QString &iconName) const;
uint m_themeKey;
@@ -178,6 +204,7 @@ private:
mutable QStringList m_iconDirs;
mutable QHash <QString, QIconTheme> themeList;
mutable QStringList m_fallbackDirs;
+ mutable QString m_iconName;
};
QT_END_NAMESPACE
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index ebe26b1687..3bbf21320e 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <limits.h>
#include <qpa/qplatformpixmap.h>
+#include <private/qcolorspace_p.h>
#include <private/qcolortransform_p.h>
#include <private/qmemrotate_p.h>
#include <private/qimagescale_p.h>
@@ -35,8 +36,9 @@
#include <private/qfont_p.h>
#if QT_CONFIG(thread)
-#include "qsemaphore.h"
-#include "qthreadpool.h"
+#include <qsemaphore.h>
+#include <qthreadpool.h>
+#include <private/qthreadpool_p.h>
#endif
#include <qtgui_tracepoints_p.h>
@@ -44,6 +46,7 @@
#include <memory>
QT_BEGIN_NAMESPACE
+class QCmyk32;
using namespace Qt::StringLiterals;
@@ -63,6 +66,17 @@ QT_WARNING_DISABLE_MSVC(4723)
return QImage(); \
}
+Q_TRACE_PREFIX(qtgui,
+ "#include <qimagereader.h>"
+);
+
+Q_TRACE_METADATA(qtgui,
+"ENUM { } QImage::Format;" \
+"FLAGS { } Qt::ImageConversionFlags;"
+);
+
+Q_TRACE_PARAM_REPLACE(Qt::AspectRatioMode, int);
+Q_TRACE_PARAM_REPLACE(Qt::TransformationMode, int);
static QImage rotated90(const QImage &src);
static QImage rotated180(const QImage &src);
@@ -70,7 +84,7 @@ static QImage rotated270(const QImage &src);
static int next_qimage_serial_number()
{
- static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0);
+ Q_CONSTINIT static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0);
return 1 + serial.fetchAndAddRelaxed(1);
}
@@ -94,12 +108,12 @@ QImageData::QImageData()
Creates a new image data.
Returns \nullptr if invalid parameters are give or anything else failed.
*/
-QImageData * QImageData::create(const QSize &size, QImage::Format format)
+QImageData * Q_TRACE_INSTRUMENT(qtgui) QImageData::create(const QSize &size, QImage::Format format)
{
if (size.isEmpty() || format <= QImage::Format_Invalid || format >= QImage::NImageFormats)
return nullptr; // invalid parameter(s)
- Q_TRACE_SCOPE(QImageData_create, size, static_cast<int>(format));
+ Q_TRACE_SCOPE(QImageData_create, size, format);
int width = size.width();
int height = size.height();
@@ -292,6 +306,7 @@ bool QImageData::checkForAlphaPixels() const
case QImage::Format_RGBX64:
case QImage::Format_RGBX16FPx4:
case QImage::Format_RGBX32FPx4:
+ case QImage::Format_CMYK8888:
break;
case QImage::Format_Invalid:
case QImage::NImageFormats:
@@ -348,7 +363,7 @@ bool QImageData::checkForAlphaPixels() const
refer to the \l{How to Create Qt Plugins}{Plugin HowTo}.
\warning Painting on a QImage with the format
- QImage::Format_Indexed8 is not supported.
+ QImage::Format_Indexed8 or QImage::Format_CMYK8888 is not supported.
\tableofcontents
@@ -604,8 +619,8 @@ bool QImageData::checkForAlphaPixels() const
\endtable
- \sa QImageReader, QImageWriter, QPixmap, QSvgRenderer, {Image Composition Example},
- {Image Viewer Example}, {Scribble Example}, {Pixelator Example}
+ \sa QImageReader, QImageWriter, QPixmap, QSvgRenderer,
+ {Image Composition Example}, {Scribble Example}
*/
/*!
@@ -698,40 +713,62 @@ bool QImageData::checkForAlphaPixels() const
The unused bits are always zero.
\value Format_ARGB4444_Premultiplied The image is stored using a
premultiplied 16-bit ARGB format (4-4-4-4).
- \value Format_RGBX8888 The image is stored using a 32-bit byte-ordered RGB(x) format (8-8-8-8).
- This is the same as the Format_RGBA8888 except alpha must always be 255. (added in Qt 5.2)
- \value Format_RGBA8888 The image is stored using a 32-bit byte-ordered RGBA format (8-8-8-8).
+ \value [since 5.2]
+ Format_RGBX8888 The image is stored using a 32-bit byte-ordered RGB(x) format (8-8-8-8).
+ This is the same as the Format_RGBA8888 except alpha must always be 255.
+ \value [since 5.2]
+ Format_RGBA8888 The image is stored using a 32-bit byte-ordered RGBA format (8-8-8-8).
Unlike ARGB32 this is a byte-ordered format, which means the 32bit
encoding differs between big endian and little endian architectures,
being respectively (0xRRGGBBAA) and (0xAABBGGRR). The order of the colors
- is the same on any architecture if read as bytes 0xRR,0xGG,0xBB,0xAA. (added in Qt 5.2)
- \value Format_RGBA8888_Premultiplied The image is stored using a
- premultiplied 32-bit byte-ordered RGBA format (8-8-8-8). (added in Qt 5.2)
- \value Format_BGR30 The image is stored using a 32-bit BGR format (x-10-10-10). (added in Qt 5.4)
- \value Format_A2BGR30_Premultiplied The image is stored using a 32-bit premultiplied ABGR format (2-10-10-10). (added in Qt 5.4)
- \value Format_RGB30 The image is stored using a 32-bit RGB format (x-10-10-10). (added in Qt 5.4)
- \value Format_A2RGB30_Premultiplied The image is stored using a 32-bit premultiplied ARGB format (2-10-10-10). (added in Qt 5.4)
- \value Format_Alpha8 The image is stored using an 8-bit alpha only format. (added in Qt 5.5)
- \value Format_Grayscale8 The image is stored using an 8-bit grayscale format. (added in Qt 5.5)
- \value Format_Grayscale16 The image is stored using an 16-bit grayscale format. (added in Qt 5.13)
- \value Format_RGBX64 The image is stored using a 64-bit halfword-ordered RGB(x) format (16-16-16-16).
- This is the same as the Format_RGBA64 except alpha must always be 65535. (added in Qt 5.12)
- \value Format_RGBA64 The image is stored using a 64-bit halfword-ordered RGBA format (16-16-16-16). (added in Qt 5.12)
- \value Format_RGBA64_Premultiplied The image is stored using a premultiplied 64-bit halfword-ordered
- RGBA format (16-16-16-16). (added in Qt 5.12)
- \value Format_BGR888 The image is stored using a 24-bit BGR format. (added in Qt 5.14)
- \value Format_RGBX16FPx4 The image is stored using a 4 16-bit halfword floating point RGBx format (16FP-16FP-16FP-16FP).
- This is the same as the Format_RGBA16FPx4 except alpha must always be 1.0. (added in Qt 6.2)
- \value Format_RGBA16FPx4 The image is stored using a 4 16-bit halfword floating point RGBA format (16FP-16FP-16FP-16FP). (added in Qt 6.2)
- \value Format_RGBA16FPx4_Premultiplied The image is stored using a premultiplied 4 16-bit halfword floating point
- RGBA format (16FP-16FP-16FP-16FP). (added in Qt 6.2)
- \value Format_RGBX32FPx4 The image is stored using a 4 32-bit floating point RGBx format (32FP-32FP-32FP-32FP).
- This is the same as the Format_RGBA32FPx4 except alpha must always be 1.0. (added in Qt 6.2)
- \value Format_RGBA32FPx4 The image is stored using a 4 32-bit floating point RGBA format (32FP-32FP-32FP-32FP). (added in Qt 6.2)
- \value Format_RGBA32FPx4_Premultiplied The image is stored using a premultiplied 4 32-bit floating point
- RGBA format (32FP-32FP-32FP-32FP). (added in Qt 6.2)
-
- \note Drawing into a QImage with QImage::Format_Indexed8 is not
+ is the same on any architecture if read as bytes 0xRR,0xGG,0xBB,0xAA.
+ \value [since 5.2]
+ Format_RGBA8888_Premultiplied The image is stored using a
+ premultiplied 32-bit byte-ordered RGBA format (8-8-8-8).
+ \value [since 5.4]
+ Format_BGR30 The image is stored using a 32-bit BGR format (x-10-10-10).
+ \value [since 5.4]
+ Format_A2BGR30_Premultiplied The image is stored using a 32-bit premultiplied ABGR format (2-10-10-10).
+ \value [since 5.4]
+ Format_RGB30 The image is stored using a 32-bit RGB format (x-10-10-10).
+ \value [since 5.4]
+ Format_A2RGB30_Premultiplied The image is stored using a 32-bit premultiplied ARGB format (2-10-10-10).
+ \value [since 5.5]
+ Format_Alpha8 The image is stored using an 8-bit alpha only format.
+ \value [since 5.5]
+ Format_Grayscale8 The image is stored using an 8-bit grayscale format.
+ \value [since 5.13]
+ Format_Grayscale16 The image is stored using an 16-bit grayscale format.
+ \value [since 5.12]
+ Format_RGBX64 The image is stored using a 64-bit halfword-ordered RGB(x) format (16-16-16-16).
+ This is the same as the Format_RGBA64 except alpha must always be 65535.
+ \value [since 5.12]
+ Format_RGBA64 The image is stored using a 64-bit halfword-ordered RGBA format (16-16-16-16).
+ \value [since 5.12]
+ Format_RGBA64_Premultiplied The image is stored using a premultiplied 64-bit halfword-ordered
+ RGBA format (16-16-16-16).
+ \value [since 5.14]
+ Format_BGR888 The image is stored using a 24-bit BGR format.
+ \value [since 6.2]
+ Format_RGBX16FPx4 The image is stored using a four 16-bit halfword floating point RGBx format (16FP-16FP-16FP-16FP).
+ This is the same as the Format_RGBA16FPx4 except alpha must always be 1.0.
+ \value [since 6.2]
+ Format_RGBA16FPx4 The image is stored using a four 16-bit halfword floating point RGBA format (16FP-16FP-16FP-16FP).
+ \value [since 6.2]
+ Format_RGBA16FPx4_Premultiplied The image is stored using a premultiplied four 16-bit halfword floating point
+ RGBA format (16FP-16FP-16FP-16FP).
+ \value [since 6.2]
+ Format_RGBX32FPx4 The image is stored using a four 32-bit floating point RGBx format (32FP-32FP-32FP-32FP).
+ This is the same as the Format_RGBA32FPx4 except alpha must always be 1.0.
+ \value [since 6.2]
+ Format_RGBA32FPx4 The image is stored using a four 32-bit floating point RGBA format (32FP-32FP-32FP-32FP).
+ \value [since 6.2]
+ Format_RGBA32FPx4_Premultiplied The image is stored using a premultiplied four 32-bit floating point
+ RGBA format (32FP-32FP-32FP-32FP).
+ \value [since 6.8]
+ Format_CMYK8888 The image is stored using a 32-bit byte-ordered CMYK format.
+
+ \note Drawing into a QImage with format QImage::Format_Indexed8 or QImage::Format_CMYK8888 is not
supported.
\note Avoid most rendering directly to most of these formats using QPainter. Rendering
@@ -807,7 +844,7 @@ QImageData *QImageData::create(uchar *data, int width, int height, qsizetype bp
// recalculate the total with this value
params.bytesPerLine = bpl;
- if (mul_overflow<qsizetype>(bpl, height, &params.totalSize))
+ if (qMulOverflow<qsizetype>(bpl, height, &params.totalSize))
return nullptr;
}
@@ -1135,9 +1172,10 @@ static void copyPhysicalMetadata(QImageData *dst, const QImageData *src)
static void copyMetadata(QImageData *dst, const QImageData *src)
{
- // Doesn't copy colortable and alpha_clut, or offset.
+ // Doesn't copy colortable and alpha_clut.
copyPhysicalMetadata(dst, src);
dst->text = src->text;
+ dst->offset = src->offset;
dst->colorSpace = src->colorSpace;
}
@@ -1182,7 +1220,7 @@ static void copyMetadata(QImage *dst, const QImage &src)
\sa QImage()
*/
-QImage QImage::copy(const QRect& r) const
+QImage Q_TRACE_INSTRUMENT(qtgui) QImage::copy(const QRect& r) const
{
Q_TRACE_SCOPE(QImage_copy, r);
if (!d)
@@ -1202,7 +1240,6 @@ QImage QImage::copy(const QRect& r) const
} else
memcpy(image.bits(), bits(), d->nbytes);
image.d->colortable = d->colortable;
- image.d->offset = d->offset;
image.d->has_alpha_clut = d->has_alpha_clut;
copyMetadata(image.d, d);
return image;
@@ -1291,7 +1328,6 @@ QImage QImage::copy(const QRect& r) const
}
copyMetadata(image.d, d);
- image.d->offset = offset();
image.d->has_alpha_clut = d->has_alpha_clut;
return image;
}
@@ -1487,12 +1523,14 @@ void QImage::setDevicePixelRatio(qreal scaleFactor)
}
/*!
- Returns the size of the pixmap in device independent pixels.
+ Returns the size of the image in device independent pixels.
- This value should be used when using the pixmap size in user interface
+ This value should be used when using the image size in user interface
size calculations.
- The return value is equivalent to pixmap.size() / pixmap.devicePixelRatio(),
+ The return value is equivalent to image.size() / image.devicePixelRatio().
+
+ \since 6.2
*/
QSizeF QImage::deviceIndependentSize() const
{
@@ -1910,7 +1948,7 @@ void QImage::fill(const QColor &color)
if (!hasAlphaChannel())
a = 1.0f;
if (depth() == 64) {
- QRgbaFloat16 c16{r, g, b, a};
+ QRgbaFloat16 c16{qfloat16(r), qfloat16(g), qfloat16(b), qfloat16(a)};
if (d->format == Format_RGBA16FPx4_Premultiplied)
c16 = c16.premultiplied();
qt_rectfill<QRgbaFloat16>(reinterpret_cast<QRgbaFloat16 *>(d->data), c16,
@@ -1996,11 +2034,11 @@ void QImage::invertPixels(InvertMode mode)
qfloat16 *p = reinterpret_cast<qfloat16 *>(d->data);
qfloat16 *end = reinterpret_cast<qfloat16 *>(d->data + d->nbytes);
while (p < end) {
- p[0] = 1.0f - p[0];
- p[1] = 1.0f - p[1];
- p[2] = 1.0f - p[2];
+ p[0] = qfloat16(1) - p[0];
+ p[1] = qfloat16(1) - p[1];
+ p[2] = qfloat16(1) - p[2];
if (mode == InvertRgba)
- p[3] = 1.0f - p[3];
+ p[3] = qfloat16(1) - p[3];
p += 4;
}
} else if (format() >= QImage::Format_RGBX32FPx4 && format() <= QImage::Format_RGBA32FPx4_Premultiplied) {
@@ -2189,7 +2227,6 @@ QImage QImage::convertToFormat_helper(Format format, Qt::ImageConversionFlags fl
QIMAGE_SANITYCHECK_MEMORY(image);
- image.d->offset = offset();
copyMetadata(image.d, d);
converter(image.d, d, flags);
@@ -2619,6 +2656,9 @@ void QImage::setPixel(int x, int y, uint index_or_rgb)
case Format_A2RGB30_Premultiplied:
((uint *)s)[x] = qConvertArgb32ToA2rgb30<PixelOrderRGB>(index_or_rgb);
return;
+ case Format_RGBX64:
+ ((QRgba64 *)s)[x] = QRgba64::fromArgb32(index_or_rgb | 0xff000000);
+ return;
case Format_RGBA64:
case Format_RGBA64_Premultiplied:
((QRgba64 *)s)[x] = QRgba64::fromArgb32(index_or_rgb);
@@ -2803,7 +2843,7 @@ void QImage::setPixelColor(int x, int y, const QColor &color)
color.getRgbF(&r, &g, &b, &a);
if (d->format == Format_RGBX16FPx4)
a = 1.0f;
- QRgbaFloat16 c16f{r, g, b, a};
+ QRgbaFloat16 c16f{qfloat16(r), qfloat16(g), qfloat16(b), qfloat16(a)};
if (d->format == Format_RGBA16FPx4_Premultiplied)
c16f = c16f.premultiplied();
((QRgbaFloat16 *)s)[x] = c16f;
@@ -2978,7 +3018,7 @@ bool QImage::isGrayscale() const
\sa isNull(), {QImage#Image Transformations}{Image
Transformations}
*/
-QImage QImage::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const
+QImage Q_TRACE_INSTRUMENT(qtgui) QImage::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const
{
if (!d) {
qWarning("QImage::scaled: Image is a null image");
@@ -3015,7 +3055,7 @@ QImage QImage::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::Transf
\sa {QImage#Image Transformations}{Image Transformations}
*/
-QImage QImage::scaledToWidth(int w, Qt::TransformationMode mode) const
+QImage Q_TRACE_INSTRUMENT(qtgui) QImage::scaledToWidth(int w, Qt::TransformationMode mode) const
{
if (!d) {
qWarning("QImage::scaleWidth: Image is a null image");
@@ -3045,7 +3085,7 @@ QImage QImage::scaledToWidth(int w, Qt::TransformationMode mode) const
\sa {QImage#Image Transformations}{Image Transformations}
*/
-QImage QImage::scaledToHeight(int h, Qt::TransformationMode mode) const
+QImage Q_TRACE_INSTRUMENT(qtgui) QImage::scaledToHeight(int h, Qt::TransformationMode mode) const
{
if (!d) {
qWarning("QImage::scaleHeight: Image is a null image");
@@ -3078,7 +3118,7 @@ QImage QImage::scaledToHeight(int h, Qt::TransformationMode mode) const
\sa createHeuristicMask(), {QImage#Image Transformations}{Image
Transformations}
*/
-QImage QImage::createAlphaMask(Qt::ImageConversionFlags flags) const
+QImage Q_TRACE_INSTRUMENT(qtgui) QImage::createAlphaMask(Qt::ImageConversionFlags flags) const
{
if (!d || d->format == QImage::Format_RGB32)
return QImage();
@@ -3530,7 +3570,7 @@ static inline void rgbSwapped_generic(int width, int height, const QImage *src,
/*!
\internal
*/
-QImage QImage::rgbSwapped_helper() const
+QImage Q_TRACE_INSTRUMENT(qtgui) QImage::rgbSwapped_helper() const
{
if (isNull())
return *this;
@@ -3886,10 +3926,15 @@ bool QImage::save(QIODevice* device, const char* format, int quality) const
bool QImageData::doImageIO(const QImage *image, QImageWriter *writer, int quality) const
{
if (quality > 100 || quality < -1)
- qWarning("QPixmap::save: Quality out of range [-1, 100]");
+ qWarning("QImage::save: Quality out of range [-1, 100]");
if (quality >= 0)
writer->setQuality(qMin(quality,100));
- return writer->write(*image);
+ const bool result = writer->write(*image);
+#ifdef QT_DEBUG
+ if (!result)
+ qWarning("QImage::save: failed to write image - %s", qPrintable(writer->errorString()));
+#endif
+ return result;
}
/*****************************************************************************
@@ -4672,6 +4717,8 @@ QImage QImage::smoothScaled(int w, int h) const
static QImage rotated90(const QImage &image)
{
QImage out(image.height(), image.width(), image.format());
+ if (out.isNull())
+ return out;
copyMetadata(&out, image);
if (image.colorCount() > 0)
out.setColorTable(image.colorTable());
@@ -4700,6 +4747,8 @@ static QImage rotated180(const QImage &image)
return image.mirrored(true, true);
QImage out(image.width(), image.height(), image.format());
+ if (out.isNull())
+ return out;
copyMetadata(&out, image);
if (image.colorCount() > 0)
out.setColorTable(image.colorTable());
@@ -4712,6 +4761,8 @@ static QImage rotated180(const QImage &image)
static QImage rotated270(const QImage &image)
{
QImage out(image.height(), image.width(), image.format());
+ if (out.isNull())
+ return out;
copyMetadata(&out, image);
if (image.colorCount() > 0)
out.setColorTable(image.colorTable());
@@ -4757,12 +4808,15 @@ static QImage rotated270(const QImage &image)
Transformations}
*/
-QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode ) const
+QImage Q_TRACE_INSTRUMENT(qtgui) QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode ) const
{
if (!d)
return QImage();
- Q_TRACE_SCOPE(QImage_transformed, matrix, mode);
+ Q_TRACE_PARAM_REPLACE(const QTransform &, double[9]);
+ Q_TRACE_SCOPE(QImage_transformed, QList<double>({matrix.m11(), matrix.m12(), matrix.m13(),
+ matrix.m21(), matrix.m22(), matrix.m23(),
+ matrix.m31(), matrix.m32(), matrix.m33()}).data(), mode);
// source image data
const int ws = width();
@@ -4834,14 +4888,24 @@ QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode
|| (ws * hs) >= (1<<20)
#endif
) {
+ QImage scaledImage;
if (mat.m11() < 0.0F && mat.m22() < 0.0F) { // horizontal/vertical flip
- return smoothScaled(wd, hd).mirrored(true, true).convertToFormat(format());
+ scaledImage = smoothScaled(wd, hd).mirrored(true, true);
} else if (mat.m11() < 0.0F) { // horizontal flip
- return smoothScaled(wd, hd).mirrored(true, false).convertToFormat(format());
+ scaledImage = smoothScaled(wd, hd).mirrored(true, false);
} else if (mat.m22() < 0.0F) { // vertical flip
- return smoothScaled(wd, hd).mirrored(false, true).convertToFormat(format());
+ scaledImage = smoothScaled(wd, hd).mirrored(false, true);
} else { // no flipping
- return smoothScaled(wd, hd).convertToFormat(format());
+ scaledImage = smoothScaled(wd, hd);
+ }
+
+ switch (format()) {
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ case QImage::Format_Indexed8:
+ return scaledImage;
+ default:
+ return scaledImage.convertToFormat(format());
}
}
}
@@ -4883,7 +4947,14 @@ QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode
if (target_format >= QImage::Format_RGB32) {
// Prevent QPainter from applying devicePixelRatio corrections
- const QImage sImage = (devicePixelRatio() != 1) ? QImage(constBits(), width(), height(), format()) : *this;
+ QImage sImage = (devicePixelRatio() != 1) ? QImage(constBits(), width(), height(), format()) : *this;
+ if (sImage.d != d
+ && (d->format == QImage::Format_MonoLSB
+ || d->format == QImage::Format_Mono
+ || d->format == QImage::Format_Indexed8)) {
+ sImage.d->colortable = d->colortable;
+ sImage.d->has_alpha_clut = d->has_alpha_clut;
+ }
Q_ASSERT(sImage.devicePixelRatio() == 1);
Q_ASSERT(sImage.devicePixelRatio() == dImage.devicePixelRatio());
@@ -4953,6 +5024,9 @@ void QImage::setColorSpace(const QColorSpace &colorSpace)
return;
if (d->colorSpace == colorSpace)
return;
+ if (colorSpace.isValid() && !qt_compatibleColorModel(pixelFormat().colorModel(), colorSpace.colorModel()))
+ return;
+
detachMetadata(false);
if (d)
d->colorSpace = colorSpace;
@@ -4965,21 +5039,60 @@ void QImage::setColorSpace(const QColorSpace &colorSpace)
If the image has no valid color space, the method does nothing.
+ \note If \a colorSpace is not compatible with the current format, the image
+ will be converted to one that is.
+
\sa convertedToColorSpace(), setColorSpace()
*/
void QImage::convertToColorSpace(const QColorSpace &colorSpace)
{
- if (!d)
- return;
- if (!d->colorSpace.isValid())
+ if (!d || !d->colorSpace.isValid())
return;
- if (!colorSpace.isValid()) {
+ if (!colorSpace.isValidTarget()) {
qWarning() << "QImage::convertToColorSpace: Output colorspace is not valid";
return;
}
if (d->colorSpace == colorSpace)
return;
+ if (!qt_compatibleColorModel(pixelFormat().colorModel(), colorSpace.colorModel())) {
+ *this = convertedToColorSpace(colorSpace);
+ return;
+ }
applyColorTransform(d->colorSpace.transformationToColorSpace(colorSpace));
+ if (d->ref.loadRelaxed() != 1)
+ detachMetadata(false);
+ d->colorSpace = colorSpace;
+}
+
+/*!
+ \since 6.8
+
+ Converts the image to \a colorSpace and \a format.
+
+ If the image has no valid color space, the method does nothing,
+ nor if the color space is not compatible with with the format.
+
+ The specified image conversion \a flags control how the image data
+ is handled during the format conversion process.
+
+ \sa convertedToColorSpace(), setColorSpace()
+*/
+void QImage::convertToColorSpace(const QColorSpace &colorSpace, QImage::Format format, Qt::ImageConversionFlags flags)
+{
+ if (!d || !d->colorSpace.isValid())
+ return;
+ if (!colorSpace.isValidTarget()) {
+ qWarning() << "QImage::convertToColorSpace: Output colorspace is not valid";
+ return;
+ }
+ if (!qt_compatibleColorModel(toPixelFormat(format).colorModel(), colorSpace.colorModel())) {
+ qWarning() << "QImage::convertToColorSpace: Color space is not compatible with format";
+ return;
+ }
+
+ if (d->colorSpace == colorSpace)
+ return convertTo(format, flags);
+ applyColorTransform(d->colorSpace.transformationToColorSpace(colorSpace), format, flags);
d->colorSpace = colorSpace;
}
@@ -4990,16 +5103,56 @@ void QImage::convertToColorSpace(const QColorSpace &colorSpace)
If the image has no valid color space, a null QImage is returned.
- \sa convertToColorSpace()
+ \note If \a colorSpace is not compatible with the current format,
+ the returned image will also be converted to a format this is.
+ For more control over returned image format, see the three argument
+ overload of this method.
+
+ \sa convertToColorSpace(), colorTransformed()
*/
QImage QImage::convertedToColorSpace(const QColorSpace &colorSpace) const
{
- if (!d || !d->colorSpace.isValid() || !colorSpace.isValid())
+ if (!d || !d->colorSpace.isValid())
return QImage();
+ if (!colorSpace.isValidTarget()) {
+ qWarning() << "QImage::convertedToColorSpace: Output colorspace is not valid";
+ return QImage();
+ }
if (d->colorSpace == colorSpace)
return *this;
- QImage image = copy();
- image.convertToColorSpace(colorSpace);
+ QImage image = colorTransformed(d->colorSpace.transformationToColorSpace(colorSpace));
+ image.setColorSpace(colorSpace);
+ return image;
+}
+
+/*!
+ \since 6.8
+
+ Returns the image converted to \a colorSpace and \a format.
+
+ If the image has no valid color space, a null QImage is returned.
+
+ The specified image conversion \a flags control how the image data
+ is handled during the format conversion process.
+
+ \sa colorTransformed()
+*/
+QImage QImage::convertedToColorSpace(const QColorSpace &colorSpace, QImage::Format format, Qt::ImageConversionFlags flags) const
+{
+ if (!d || !d->colorSpace.isValid())
+ return QImage();
+ if (!colorSpace.isValidTarget()) {
+ qWarning() << "QImage::convertedToColorSpace: Output colorspace is not valid";
+ return QImage();
+ }
+ if (!qt_compatibleColorModel(toPixelFormat(format).colorModel(), colorSpace.colorModel())) {
+ qWarning() << "QImage::convertedToColorSpace: Color space is not compatible with format";
+ return QImage();
+ }
+ if (d->colorSpace == colorSpace)
+ return convertedTo(format, flags);
+ QImage image = colorTransformed(d->colorSpace.transformationToColorSpace(colorSpace), format, flags);
+ image.setColorSpace(colorSpace);
return image;
}
@@ -5024,6 +5177,13 @@ void QImage::applyColorTransform(const QColorTransform &transform)
{
if (transform.isIdentity())
return;
+
+ if (!qt_compatibleColorModel(pixelFormat().colorModel(), QColorTransformPrivate::get(transform)->colorSpaceIn->colorModel) ||
+ !qt_compatibleColorModel(pixelFormat().colorModel(), QColorTransformPrivate::get(transform)->colorSpaceOut->colorModel)) {
+ qWarning() << "QImage::applyColorTransform can not apply format switching transform without switching format";
+ return;
+ }
+
detach();
if (!d)
return;
@@ -5042,7 +5202,8 @@ void QImage::applyColorTransform(const QColorTransform &transform)
&& oldFormat != QImage::Format_RGBA64_Premultiplied)
convertTo(QImage::Format_RGBA64);
} else if (oldFormat != QImage::Format_ARGB32 && oldFormat != QImage::Format_RGB32
- && oldFormat != QImage::Format_ARGB32_Premultiplied) {
+ && oldFormat != QImage::Format_ARGB32_Premultiplied && oldFormat != QImage::Format_CMYK8888
+ && oldFormat != QImage::Format_Grayscale8 && oldFormat != QImage::Format_Grayscale16) {
if (hasAlphaChannel())
convertTo(QImage::Format_ARGB32);
else
@@ -5056,7 +5217,10 @@ void QImage::applyColorTransform(const QColorTransform &transform)
case Format_RGBA32FPx4_Premultiplied:
flags = QColorTransformPrivate::Premultiplied;
break;
+ case Format_Grayscale8:
+ case Format_Grayscale16:
case Format_RGB32:
+ case Format_CMYK8888:
case Format_RGBX64:
case Format_RGBX32FPx4:
flags = QColorTransformPrivate::InputOpaque;
@@ -5071,7 +5235,21 @@ void QImage::applyColorTransform(const QColorTransform &transform)
std::function<void(int,int)> transformSegment;
- if (qt_fpColorPrecision(format())) {
+ if (format() == Format_Grayscale8) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ uint8_t *scanline = reinterpret_cast<uint8_t *>(d->data + y * d->bytes_per_line);
+ QColorTransformPrivate::get(transform)->applyGray(scanline, scanline, width(), flags);
+ }
+ };
+ } else if (format() == Format_Grayscale16) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ uint16_t *scanline = reinterpret_cast<uint16_t *>(d->data + y * d->bytes_per_line);
+ QColorTransformPrivate::get(transform)->applyGray(scanline, scanline, width(), flags);
+ }
+ };
+ } else if (qt_fpColorPrecision(format())) {
transformSegment = [&](int yStart, int yEnd) {
for (int y = yStart; y < yEnd; ++y) {
QRgbaFloat32 *scanline = reinterpret_cast<QRgbaFloat32 *>(d->data + y * d->bytes_per_line);
@@ -5085,6 +5263,13 @@ void QImage::applyColorTransform(const QColorTransform &transform)
QColorTransformPrivate::get(transform)->apply(scanline, scanline, width(), flags);
}
};
+ } else if (oldFormat == QImage::Format_CMYK8888) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ QCmyk32 *scanline = reinterpret_cast<QCmyk32 *>(d->data + y * d->bytes_per_line);
+ QColorTransformPrivate::get(transform)->apply(scanline, scanline, width(), flags);
+ }
+ };
} else {
transformSegment = [&](int yStart, int yEnd) {
for (int y = yStart; y < yEnd; ++y) {
@@ -5097,7 +5282,7 @@ void QImage::applyColorTransform(const QColorTransform &transform)
#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
int segments = (qsizetype(width()) * height()) >> 16;
segments = std::min(segments, height());
- QThreadPool *threadPool = QThreadPool::globalInstance();
+ QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) {
QSemaphore semaphore;
int y = 0;
@@ -5119,23 +5304,497 @@ void QImage::applyColorTransform(const QColorTransform &transform)
}
/*!
+ \since 6.8
+
+ Applies the color transformation \a transform to all pixels in the image, and converts the format of the image to \a toFormat.
+
+ The specified image conversion \a flags control how the image data
+ is handled during the format conversion process.
+*/
+void QImage::applyColorTransform(const QColorTransform &transform, QImage::Format toFormat, Qt::ImageConversionFlags flags)
+{
+ if (!d)
+ return;
+ if (transform.isIdentity())
+ return convertTo(toFormat, flags);
+
+ *this = colorTransformed(transform, toFormat, flags);
+}
+
+/*!
\since 6.4
Returns the image color transformed using \a transform on all pixels in the image.
+ \note If \a transform has a source color space which is incompatible with the format of this image,
+ returns a null QImage. If \a transform has a target color space which is incompatible with the format
+ of this image, the image will also be converted to a compatible format. For more control about the
+ choice of the target pixel format, see the three argument overload of this method.
+
\sa applyColorTransform()
*/
QImage QImage::colorTransformed(const QColorTransform &transform) const &
{
- if (!d || !d->colorSpace.isValid())
+ if (!d)
return QImage();
if (transform.isIdentity())
return *this;
+
+ QColorSpace::ColorModel inColorModel = QColorTransformPrivate::get(transform)->colorSpaceIn->colorModel;
+ QColorSpace::ColorModel outColorModel = QColorTransformPrivate::get(transform)->colorSpaceOut->colorModel;
+ if (!qt_compatibleColorModel(pixelFormat().colorModel(), inColorModel)) {
+ qWarning() << "QImage::colorTransformed: Invalid input color space for transform";
+ return QImage();
+ }
+ if (!qt_compatibleColorModel(pixelFormat().colorModel(), outColorModel)) {
+ // All model switching transforms are opaque in at least one end.
+ switch (outColorModel) {
+ case QColorSpace::ColorModel::Rgb:
+ return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_RGBX64 : QImage::Format_RGB32);
+ case QColorSpace::ColorModel::Gray:
+ return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_Grayscale16 : QImage::Format_Grayscale8);
+ case QColorSpace::ColorModel::Cmyk:
+ return colorTransformed(transform, QImage::Format_CMYK8888);
+ case QColorSpace::ColorModel::Undefined:
+ break;
+ }
+ return QImage();
+ }
+
QImage image = copy();
image.applyColorTransform(transform);
return image;
}
+static bool isRgb32Data(QImage::Format f)
+{
+ switch (f) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static bool isRgb64Data(QImage::Format f)
+{
+ switch (f) {
+ case QImage::Format_RGBX64:
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static bool isRgb32fpx4Data(QImage::Format f)
+{
+ switch (f) {
+ case QImage::Format_RGBX32FPx4:
+ case QImage::Format_RGBA32FPx4:
+ case QImage::Format_RGBA32FPx4_Premultiplied:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+/*!
+ \since 6.8
+
+ Returns the image color transformed using \a transform on all pixels in the image, returning an image of format \a toFormat.
+
+ The specified image conversion \a flags control how the image data
+ is handled during the format conversion process.
+
+ \note If \a transform has a source color space which is incompatible with the format of this image,
+ or a target color space that is incompatible with \a toFormat, returns a null QImage.
+
+ \sa applyColorTransform()
+*/
+QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format toFormat, Qt::ImageConversionFlags flags) const &
+{
+ if (!d)
+ return QImage();
+ if (toFormat == QImage::Format_Invalid)
+ toFormat = format();
+ if (transform.isIdentity())
+ return convertedTo(toFormat, flags);
+
+ QColorSpace::ColorModel inColorModel = QColorTransformPrivate::get(transform)->colorSpaceIn->colorModel;
+ QColorSpace::ColorModel outColorModel = QColorTransformPrivate::get(transform)->colorSpaceOut->colorModel;
+ if (!qt_compatibleColorModel(pixelFormat().colorModel(), inColorModel)) {
+ qWarning() << "QImage::colorTransformed: Invalid input color space for transform";
+ return QImage();
+ }
+ if (!qt_compatibleColorModel(toPixelFormat(toFormat).colorModel(), outColorModel)) {
+ qWarning() << "QImage::colorTransformed: Invalid output color space for transform";
+ return QImage();
+ }
+
+ QImage fromImage = *this;
+
+ QImage::Format tmpFormat = toFormat;
+ switch (toFormat) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ case QImage::Format_RGBX32FPx4:
+ case QImage::Format_RGBA32FPx4:
+ case QImage::Format_RGBA32FPx4_Premultiplied:
+ case QImage::Format_RGBX64:
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
+ case QImage::Format_Grayscale8:
+ case QImage::Format_Grayscale16:
+ case QImage::Format_CMYK8888:
+ // can be output natively
+ break;
+ case QImage::Format_RGB16:
+ case QImage::Format_RGB444:
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB666:
+ case QImage::Format_RGB888:
+ case QImage::Format_BGR888:
+ case QImage::Format_RGBX8888:
+ tmpFormat = QImage::Format_RGB32;
+ break;
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ case QImage::Format_Indexed8:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB4444_Premultiplied:
+ case QImage::Format_RGBA8888:
+ case QImage::Format_RGBA8888_Premultiplied:
+ tmpFormat = QImage::Format_ARGB32;
+ break;
+ case QImage::Format_BGR30:
+ case QImage::Format_RGB30:
+ tmpFormat = QImage::Format_RGBX64;
+ break;
+ case QImage::Format_A2BGR30_Premultiplied:
+ case QImage::Format_A2RGB30_Premultiplied:
+ tmpFormat = QImage::Format_RGBA64;
+ break;
+ case QImage::Format_RGBX16FPx4:
+ case QImage::Format_RGBA16FPx4:
+ case QImage::Format_RGBA16FPx4_Premultiplied:
+ tmpFormat = QImage::Format_RGBA32FPx4;
+ break;
+ case QImage::Format_Alpha8:
+ return convertedTo(QImage::Format_Alpha8);
+ case QImage::Format_Invalid:
+ case QImage::NImageFormats:
+ Q_UNREACHABLE();
+ break;
+ }
+ QColorSpace::ColorModel inColorData = qt_csColorData(pixelFormat().colorModel());
+ QColorSpace::ColorModel outColorData = qt_csColorData(toPixelFormat(toFormat).colorModel());
+ // Ensure only precision increasing transforms
+ if (inColorData != outColorData) {
+ if (fromImage.format() == QImage::Format_Grayscale8 && outColorData == QColorSpace::ColorModel::Rgb)
+ tmpFormat = QImage::Format_RGB32;
+ else if (tmpFormat == QImage::Format_Grayscale8 && qt_highColorPrecision(fromImage.format()))
+ tmpFormat = QImage::Format_Grayscale16;
+ else if (fromImage.format() == QImage::Format_Grayscale16 && outColorData == QColorSpace::ColorModel::Rgb)
+ tmpFormat = QImage::Format_RGBX64;
+ } else {
+ if (tmpFormat == QImage::Format_Grayscale8 && fromImage.format() == QImage::Format_Grayscale16)
+ tmpFormat = QImage::Format_Grayscale16;
+ else if (qt_fpColorPrecision(fromImage.format()) && !qt_fpColorPrecision(tmpFormat))
+ tmpFormat = QImage::Format_RGBA32FPx4;
+ else if (isRgb32Data(tmpFormat) && qt_highColorPrecision(fromImage.format(), true))
+ tmpFormat = QImage::Format_RGBA64;
+ }
+
+ QImage toImage(size(), tmpFormat);
+ copyMetadata(&toImage, *this);
+
+ std::function<void(int, int)> transformSegment;
+ QColorTransformPrivate::TransformFlags transFlags = QColorTransformPrivate::Unpremultiplied;
+
+ if (inColorData != outColorData) {
+ // Needs color model switching transform
+ if (inColorData == QColorSpace::ColorModel::Gray && outColorData == QColorSpace::ColorModel::Rgb) {
+ // Gray -> RGB
+ if (format() == QImage::Format_Grayscale8) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const quint8 *in_scanline = reinterpret_cast<const quint8 *>(d->data + y * d->bytes_per_line);
+ QRgb *out_scanline = reinterpret_cast<QRgb *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ } else {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const quint16 *in_scanline = reinterpret_cast<const quint16 *>(d->data + y * d->bytes_per_line);
+ QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ }
+ } else if (inColorData == QColorSpace::ColorModel::Gray && outColorData == QColorSpace::ColorModel::Cmyk) {
+ // Gray -> CMYK
+ if (format() == QImage::Format_Grayscale8) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const quint8 *in_scanline = reinterpret_cast<const quint8 *>(d->data + y * d->bytes_per_line);
+ QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ } else {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const quint16 *in_scanline = reinterpret_cast<const quint16 *>(d->data + y * d->bytes_per_line);
+ QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ }
+ } else if (inColorData == QColorSpace::ColorModel::Rgb && outColorData == QColorSpace::ColorModel::Gray) {
+ // RGB -> Gray
+ if (tmpFormat == QImage::Format_Grayscale8) {
+ fromImage.convertTo(QImage::Format_RGB32);
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgb *in_scanline = reinterpret_cast<const QRgb *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ quint8 *out_scanline = reinterpret_cast<quint8 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyReturnGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ } else {
+ fromImage.convertTo(QImage::Format_RGBX64);
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgba64 *in_scanline = reinterpret_cast<const QRgba64 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyReturnGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ }
+ } else if (inColorData == QColorSpace::ColorModel::Cmyk && outColorData == QColorSpace::ColorModel::Gray) {
+ // CMYK -> Gray
+ if (tmpFormat == QImage::Format_Grayscale8) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ quint8 *out_scanline = reinterpret_cast<quint8 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyReturnGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ } else {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyReturnGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ }
+ } else if (inColorData == QColorSpace::ColorModel::Cmyk && outColorData == QColorSpace::ColorModel::Rgb) {
+ // CMYK -> RGB
+ if (isRgb32Data(tmpFormat) ) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QRgb *out_scanline = reinterpret_cast<QRgb *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ } else if (isRgb64Data(tmpFormat)) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ } else {
+ Q_ASSERT(isRgb32fpx4Data(tmpFormat));
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ }
+ };
+ }
+ } else if (inColorData == QColorSpace::ColorModel::Rgb && outColorData == QColorSpace::ColorModel::Cmyk) {
+ // RGB -> CMYK
+ if (!fromImage.hasAlphaChannel())
+ transFlags = QColorTransformPrivate::InputOpaque;
+ else if (qPixelLayouts[fromImage.format()].premultiplied)
+ transFlags = QColorTransformPrivate::Premultiplied;
+ if (isRgb32Data(fromImage.format()) ) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgb *in_scanline = reinterpret_cast<const QRgb *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ }
+ };
+ } else if (isRgb64Data(fromImage.format())) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgba64 *in_scanline = reinterpret_cast<const QRgba64 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ }
+ };
+ } else {
+ Q_ASSERT(isRgb32fpx4Data(fromImage.format()));
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgbaFloat32 *in_scanline = reinterpret_cast<const QRgbaFloat32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ }
+ };
+ }
+ } else {
+ Q_UNREACHABLE();
+ }
+ } else {
+ // Conversion on same color model
+ if (pixelFormat().colorModel() == QPixelFormat::Indexed) {
+ for (int i = 0; i < d->colortable.size(); ++i)
+ fromImage.d->colortable[i] = transform.map(d->colortable[i]);
+ return fromImage.convertedTo(toFormat, flags);
+ }
+
+ QImage::Format oldFormat = format();
+ if (qt_fpColorPrecision(oldFormat)) {
+ if (oldFormat != QImage::Format_RGBX32FPx4 && oldFormat != QImage::Format_RGBA32FPx4
+ && oldFormat != QImage::Format_RGBA32FPx4_Premultiplied)
+ fromImage.convertTo(QImage::Format_RGBA32FPx4);
+ } else if (qt_highColorPrecision(oldFormat, true)) {
+ if (oldFormat != QImage::Format_RGBX64 && oldFormat != QImage::Format_RGBA64
+ && oldFormat != QImage::Format_RGBA64_Premultiplied && oldFormat != QImage::Format_Grayscale16)
+ fromImage.convertTo(QImage::Format_RGBA64);
+ } else if (oldFormat != QImage::Format_ARGB32 && oldFormat != QImage::Format_RGB32
+ && oldFormat != QImage::Format_ARGB32_Premultiplied && oldFormat != QImage::Format_CMYK8888
+ && oldFormat != QImage::Format_Grayscale8 && oldFormat != QImage::Format_Grayscale16) {
+ if (hasAlphaChannel())
+ fromImage.convertTo(QImage::Format_ARGB32);
+ else
+ fromImage.convertTo(QImage::Format_RGB32);
+ }
+
+ if (!fromImage.hasAlphaChannel())
+ transFlags = QColorTransformPrivate::InputOpaque;
+ else if (qPixelLayouts[fromImage.format()].premultiplied)
+ transFlags = QColorTransformPrivate::Premultiplied;
+
+ if (fromImage.format() == Format_Grayscale8) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const quint8 *in_scanline = reinterpret_cast<const quint8 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ if (tmpFormat == Format_Grayscale8) {
+ quint8 *out_scanline = reinterpret_cast<quint8 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), transFlags);
+ } else {
+ Q_ASSERT(tmpFormat == Format_Grayscale16);
+ quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), transFlags);
+ }
+ }
+ };
+ } else if (fromImage.format() == Format_Grayscale16) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const quint16 *in_scanline = reinterpret_cast<const quint16 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), transFlags);
+ }
+ };
+ } else if (fromImage.format() == Format_CMYK8888) {
+ Q_ASSERT(tmpFormat == Format_CMYK8888);
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ }
+ };
+ } else if (isRgb32fpx4Data(fromImage.format())) {
+ Q_ASSERT(isRgb32fpx4Data(tmpFormat));
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgbaFloat32 *in_scanline = reinterpret_cast<const QRgbaFloat32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ }
+ };
+ } else if (isRgb64Data(fromImage.format())) {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgba64 *in_scanline = reinterpret_cast<const QRgba64 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ if (isRgb32fpx4Data(tmpFormat)) {
+ QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ } else {
+ Q_ASSERT(isRgb64Data(tmpFormat));
+ QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ }
+ }
+ };
+ } else {
+ transformSegment = [&](int yStart, int yEnd) {
+ for (int y = yStart; y < yEnd; ++y) {
+ const QRgb *in_scanline = reinterpret_cast<const QRgb *>(fromImage.constBits() + y * fromImage.bytesPerLine());
+ if (isRgb32fpx4Data(tmpFormat)) {
+ QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ } else if (isRgb64Data(tmpFormat)) {
+ QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ } else {
+ Q_ASSERT(isRgb32Data(tmpFormat));
+ QRgb *out_scanline = reinterpret_cast<QRgb *>(toImage.d->data + y * toImage.bytesPerLine());
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
+ }
+ }
+ };
+ }
+ }
+
+#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
+ int segments = (qsizetype(width()) * height()) >> 16;
+ segments = std::min(segments, height());
+ QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
+ if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) {
+ QSemaphore semaphore;
+ int y = 0;
+ for (int i = 0; i < segments; ++i) {
+ int yn = (height() - y) / (segments - i);
+ threadPool->start([&, y, yn]() {
+ transformSegment(y, y + yn);
+ semaphore.release(1);
+ });
+ y += yn;
+ }
+ semaphore.acquire(segments);
+ } else
+#endif
+ transformSegment(0, height());
+
+ if (tmpFormat != toFormat)
+ toImage.convertTo(toFormat);
+
+ return toImage;
+}
+
/*!
\since 6.4
\overload
@@ -5146,12 +5805,48 @@ QImage QImage::colorTransformed(const QColorTransform &transform) const &
*/
QImage QImage::colorTransformed(const QColorTransform &transform) &&
{
- if (!d || !d->colorSpace.isValid())
+ if (!d)
return QImage();
+
+ QColorSpace::ColorModel inColorModel = QColorTransformPrivate::get(transform)->colorSpaceIn->colorModel;
+ QColorSpace::ColorModel outColorModel = QColorTransformPrivate::get(transform)->colorSpaceOut->colorModel;
+ if (!qt_compatibleColorModel(pixelFormat().colorModel(), inColorModel)) {
+ qWarning() << "QImage::colorTransformed: Invalid input color space for transform";
+ return QImage();
+ }
+ if (!qt_compatibleColorModel(pixelFormat().colorModel(), outColorModel)) {
+ // There is currently no inplace conversion of both colorspace and format, so just use the normal version.
+ switch (outColorModel) {
+ case QColorSpace::ColorModel::Rgb:
+ return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_RGBX64 : QImage::Format_RGB32);
+ case QColorSpace::ColorModel::Gray:
+ return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_Grayscale16 : QImage::Format_Grayscale8);
+ case QColorSpace::ColorModel::Cmyk:
+ return colorTransformed(transform, QImage::Format_CMYK8888);
+ case QColorSpace::ColorModel::Undefined:
+ break;
+ }
+ return QImage();
+ }
+
applyColorTransform(transform);
return std::move(*this);
}
+/*!
+ \since 6.8
+ \overload
+
+ Returns the image color transformed using \a transform on all pixels in the image.
+
+ \sa applyColorTransform()
+*/
+QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format format, Qt::ImageConversionFlags flags) &&
+{
+ // There is currently no inplace conversion of both colorspace and format, so just use the normal version.
+ return colorTransformed(transform, format, flags);
+}
+
bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFlags flags)
{
if (format == newFormat)
@@ -5676,6 +6371,19 @@ static constexpr QPixelFormat pixelformats[] = {
/*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
/*INTERPRETATION*/ QPixelFormat::FloatingPoint,
/*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
+ //QImage::Format_CMYK8888:
+ QPixelFormat(QPixelFormat::CMYK,
+ /*RED*/ 8,
+ /*GREEN*/ 8,
+ /*BLUE*/ 8,
+ /*FOURTH*/ 8,
+ /*FIFTH*/ 0,
+ /*ALPHA*/ 0,
+ /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
+ /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
+ /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
+ /*INTERPRETATION*/ QPixelFormat::UnsignedInteger,
+ /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
};
static_assert(sizeof(pixelformats) / sizeof(*pixelformats) == QImage::NImageFormats);
@@ -5736,8 +6444,7 @@ QMap<QString, QString> qt_getImageText(const QImage &image, const QString &descr
QMap<QString, QString> qt_getImageTextFromDescription(const QString &description)
{
QMap<QString, QString> text;
- const auto pairs = QStringView{description}.split(u"\n\n");
- for (const auto &pair : pairs) {
+ for (const auto &pair : QStringView{description}.tokenize(u"\n\n")) {
int index = pair.indexOf(u':');
if (index >= 0 && pair.indexOf(u' ') < index) {
if (!pair.trimmed().isEmpty())
diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h
index aa58af4a76..cba50e5e4c 100644
--- a/src/gui/image/qimage.h
+++ b/src/gui/image/qimage.h
@@ -75,6 +75,7 @@ public:
Format_RGBX32FPx4,
Format_RGBA32FPx4,
Format_RGBA32FPx4_Premultiplied,
+ Format_CMYK8888,
#ifndef Q_QDOC
NImageFormats
#endif
@@ -96,7 +97,7 @@ public:
QImage(const QImage &);
QImage(QImage &&other) noexcept
- : QPaintDevice(), d(qExchange(other.d, nullptr))
+ : QPaintDevice(), d(std::exchange(other.d, nullptr))
{}
~QImage();
@@ -230,13 +231,18 @@ public:
void invertPixels(InvertMode = InvertRgb);
QColorSpace colorSpace() const;
- [[nodiscard]] QImage convertedToColorSpace(const QColorSpace &) const;
- void convertToColorSpace(const QColorSpace &);
- void setColorSpace(const QColorSpace &);
+ [[nodiscard]] QImage convertedToColorSpace(const QColorSpace &colorSpace) const;
+ [[nodiscard]] QImage convertedToColorSpace(const QColorSpace &colorSpace, QImage::Format format, Qt::ImageConversionFlags flags = Qt::AutoColor) const;
+ void convertToColorSpace(const QColorSpace &colorSpace);
+ void convertToColorSpace(const QColorSpace &colorSpace, QImage::Format format, Qt::ImageConversionFlags flags = Qt::AutoColor);
+ void setColorSpace(const QColorSpace &colorSpace);
QImage colorTransformed(const QColorTransform &transform) const &;
+ QImage colorTransformed(const QColorTransform &transform, QImage::Format format, Qt::ImageConversionFlags flags = Qt::AutoColor) const &;
QImage colorTransformed(const QColorTransform &transform) &&;
+ QImage colorTransformed(const QColorTransform &transform, QImage::Format format, Qt::ImageConversionFlags flags = Qt::AutoColor) &&;
void applyColorTransform(const QColorTransform &transform);
+ void applyColorTransform(const QColorTransform &transform, QImage::Format format, Qt::ImageConversionFlags flags = Qt::AutoColor);
bool load(QIODevice *device, const char *format);
bool load(const QString &fileName, const char *format = nullptr);
diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp
index fa75bed3c8..a806954df2 100644
--- a/src/gui/image/qimage_conversions.cpp
+++ b/src/gui/image/qimage_conversions.cpp
@@ -4,6 +4,7 @@
#include <private/qguiapplication_p.h>
#include <private/qcolortransform_p.h>
#include <private/qcolortrclut_p.h>
+#include <private/qcmyk_p.h>
#include <private/qdrawhelper_p.h>
#include <private/qendian_p.h>
#include <private/qpixellayout_p.h>
@@ -15,6 +16,7 @@
#if QT_CONFIG(thread)
#include <qsemaphore.h>
#include <qthreadpool.h>
+#include <private/qthreadpool_p.h>
#ifdef Q_OS_WASM
// WebAssembly has threads; however we can't block the main thread.
#else
@@ -164,7 +166,7 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio
if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied &&
!destLayout->hasAlphaChannel && destLayout->storeFromRGB32) {
// Avoid unnecessary premultiply and unpremultiply when converting from unpremultiplied src format.
- fetch = qPixelLayouts[src->format + 1].fetchToARGB32PM;
+ fetch = qPixelLayouts[qt_toPremultipliedFormat(src->format)].fetchToARGB32PM;
if (dest->format == QImage::Format_RGB32)
store = storeRGB32FromARGB32;
else
@@ -203,7 +205,7 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio
int segments = (qsizetype(src->width) * src->height) >> 16;
segments = std::min(segments, src->height);
- QThreadPool *threadPool = QThreadPool::globalInstance();
+ QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
if (segments <= 1 || !threadPool || threadPool->contains(QThread::currentThread()))
return convertSegment(0, src->height);
@@ -258,7 +260,7 @@ void convert_generic_over_rgb64(QImageData *dest, const QImageData *src, Qt::Ima
int segments = (qsizetype(src->width) * src->height) >> 16;
segments = std::min(segments, src->height);
- QThreadPool *threadPool = QThreadPool::globalInstance();
+ QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
if (segments <= 1 || !threadPool || threadPool->contains(QThread::currentThread()))
return convertSegment(0, src->height);
@@ -312,7 +314,7 @@ void convert_generic_over_rgba32f(QImageData *dest, const QImageData *src, Qt::I
int segments = (qsizetype(src->width) * src->height) >> 16;
segments = std::min(segments, src->height);
- QThreadPool *threadPool = QThreadPool::globalInstance();
+ QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
if (segments <= 1 || !threadPool || threadPool->contains(QThread::currentThread()))
return convertSegment(0, src->height);
@@ -382,7 +384,7 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im
if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied &&
!destLayout->hasAlphaChannel && destLayout->storeFromRGB32) {
// Avoid unnecessary premultiply and unpremultiply when converting from unpremultiplied src format.
- fetch = qPixelLayouts[data->format + 1].fetchToARGB32PM;
+ fetch = qPixelLayouts[qt_toPremultipliedFormat(data->format)].fetchToARGB32PM;
if (data->format == QImage::Format_RGB32)
store = storeRGB32FromARGB32;
else
@@ -419,7 +421,7 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im
#ifdef QT_USE_THREAD_PARALLEL_IMAGE_CONVERSIONS
int segments = (qsizetype(data->width) * data->height) >> 16;
segments = std::min(segments, data->height);
- QThreadPool *threadPool = QThreadPool::globalInstance();
+ QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) {
QSemaphore semaphore;
int y = 0;
@@ -484,9 +486,8 @@ bool convert_generic_inplace_over_rgb64(QImageData *data, QImage::Format dst_for
if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied &&
destLayout->hasAlphaChannel && !destLayout->premultiplied) {
// Avoid unnecessary premultiply and unpremultiply when converting between two unpremultiplied formats.
- // This abuses the fact unpremultiplied formats are always before their premultiplied counterparts.
- fetch = qPixelLayouts[data->format + 1].fetchToRGBA64PM;
- store = qStoreFromRGBA64PM[dst_format + 1];
+ fetch = qPixelLayouts[qt_toPremultipliedFormat(data->format)].fetchToRGBA64PM;
+ store = qStoreFromRGBA64PM[qt_toPremultipliedFormat(dst_format)];
}
auto convertSegment = [=](int yStart, int yEnd) {
@@ -513,7 +514,7 @@ bool convert_generic_inplace_over_rgb64(QImageData *data, QImage::Format dst_for
#ifdef QT_USE_THREAD_PARALLEL_IMAGE_CONVERSIONS
int segments = (qsizetype(data->width) * data->height) >> 16;
segments = std::min(segments, data->height);
- QThreadPool *threadPool = QThreadPool::globalInstance();
+ QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) {
QSemaphore semaphore;
int y = 0;
@@ -579,9 +580,8 @@ bool convert_generic_inplace_over_rgba32f(QImageData *data, QImage::Format dst_f
if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied &&
destLayout->hasAlphaChannel && !destLayout->premultiplied) {
// Avoid unnecessary premultiply and unpremultiply when converting between two unpremultiplied formats.
- // This abuses the fact unpremultiplied formats are always before their premultiplied counterparts.
- fetch = qFetchToRGBA32F[data->format + 1];
- store = qStoreFromRGBA32F[dst_format + 1];
+ fetch = qFetchToRGBA32F[qt_toPremultipliedFormat(data->format)];
+ store = qStoreFromRGBA32F[qt_toPremultipliedFormat(dst_format)];
}
auto convertSegment = [=](int yStart, int yEnd) {
@@ -608,7 +608,7 @@ bool convert_generic_inplace_over_rgba32f(QImageData *data, QImage::Format dst_f
#ifdef QT_USE_THREAD_PARALLEL_IMAGE_CONVERSIONS
int segments = (qsizetype(data->width) * data->height) >> 16;
segments = std::min(segments, data->height);
- QThreadPool *threadPool = QThreadPool::globalInstance();
+ QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) {
QSemaphore semaphore;
int y = 0;
@@ -1033,8 +1033,7 @@ static inline uint qUnpremultiplyRgb30(uint rgb30)
case 3:
return rgb30;
}
- Q_UNREACHABLE();
- return 0;
+ Q_UNREACHABLE_RETURN(0);
}
template<bool rgbswap>
@@ -1323,11 +1322,11 @@ static void convert_ARGB32_to_RGBA64(QImageData *dest, const QImageData *src, Qt
const uchar *src_data = src->data;
uchar *dest_data = dest->data;
- const FetchAndConvertPixelsFunc64 fetch = qPixelLayouts[src->format + 1].fetchToRGBA64PM;
+ const FetchAndConvertPixelsFunc64 fetch = qPixelLayouts[qt_toPremultipliedFormat(src->format)].fetchToRGBA64PM;
for (int i = 0; i < src->height; ++i) {
fetch(reinterpret_cast<QRgba64 *>(dest_data), src_data, 0, src->width, nullptr, nullptr);
- src_data += src->bytes_per_line;;
+ src_data += src->bytes_per_line;
dest_data += dest->bytes_per_line;
}
}
@@ -1425,7 +1424,7 @@ static void convert_ARGB_to_gray8(QImageData *dest, const QImageData *src, Qt::I
for (int i = 0; i < src->height; ++i) {
const QRgb *src_line = reinterpret_cast<const QRgb *>(src_data);
- tfd->apply(dest_data, src_line, src->width, flags);
+ tfd->applyReturnGray(dest_data, src_line, src->width, flags);
src_data += sbpl;
dest_data += dbpl;
}
@@ -1462,7 +1461,7 @@ static void convert_ARGB_to_gray16(QImageData *dest, const QImageData *src, Qt::
const int len = std::min(src->width - j, BufferSize);
for (int k = 0; k < len; ++k)
tmp_line[k] = QRgba64::fromArgb32(src_line[j + k]);
- tfd->apply(dest_line + j, tmp_line, len, flags);
+ tfd->applyReturnGray(dest_line + j, tmp_line, len, flags);
j += len;
}
src_data += sbpl;
@@ -1499,7 +1498,7 @@ static void convert_RGBA64_to_gray8(QImageData *dest, const QImageData *src, Qt:
int j = 0;
while (j < src->width) {
const int len = std::min(src->width - j, BufferSize);
- tfd->apply(gray_line, src_line + j, len, flags);
+ tfd->applyReturnGray(gray_line, src_line + j, len, flags);
for (int k = 0; k < len; ++k)
dest_line[j + k] = qt_div_257(gray_line[k]);
j += len;
@@ -1534,7 +1533,7 @@ static void convert_RGBA64_to_gray16(QImageData *dest, const QImageData *src, Qt
for (int i = 0; i < src->height; ++i) {
const QRgba64 *src_line = reinterpret_cast<const QRgba64 *>(src_data);
quint16 *dest_line = reinterpret_cast<quint16 *>(dest_data);
- tfd->apply(dest_line, src_line, src->width, flags);
+ tfd->applyReturnGray(dest_line, src_line, src->width, flags);
src_data += sbpl;
dest_data += dbpl;
}
@@ -2456,6 +2455,34 @@ static bool convert_Grayscale8_to_Indexed8_inplace(QImageData *data, Qt::ImageCo
return true;
}
+template <bool SourceIsPremultiplied>
+static void convert_ARGB32_to_CMYK8888(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_RGB32 ||
+ src->format == QImage::Format_ARGB32 ||
+ src->format == QImage::Format_ARGB32_Premultiplied);
+ Q_ASSERT(dest->format == QImage::Format_CMYK8888);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const uchar *src_data = src->data;
+ uchar *dest_data = dest->data;
+ for (int y = 0; y < src->height; ++y) {
+ const QRgb *srcRgba = reinterpret_cast<const QRgb *>(src_data);
+ uint *destCmyk = reinterpret_cast<uint *>(dest_data);
+
+ for (int x = 0; x < src->width; ++x) {
+ QRgb sourcePixel = srcRgba[x];
+ if constexpr (SourceIsPremultiplied)
+ sourcePixel = qUnpremultiply(sourcePixel);
+
+ destCmyk[x] = QCmyk32::fromRgba(sourcePixel).toUint();
+ }
+
+ src_data += src->bytes_per_line;;
+ dest_data += dest->bytes_per_line;
+ }
+}
// first index source, second dest
Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormats] = {};
@@ -2592,6 +2619,11 @@ static void qInitImageConversions()
qimage_converter_map[QImage::Format_RGBX32FPx4][QImage::Format_RGBA32FPx4] = convert_passthrough;
qimage_converter_map[QImage::Format_RGBX32FPx4][QImage::Format_RGBA32FPx4_Premultiplied] = convert_passthrough;
+ qimage_converter_map[QImage::Format_CMYK8888][QImage::Format_CMYK8888] = convert_passthrough;
+ qimage_converter_map[QImage::Format_RGB32][QImage::Format_CMYK8888] = convert_ARGB32_to_CMYK8888<false>;
+ qimage_converter_map[QImage::Format_ARGB32][QImage::Format_CMYK8888] = convert_ARGB32_to_CMYK8888<false>;
+ qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_CMYK8888] = convert_ARGB32_to_CMYK8888<true>;
+
// Inline converters:
qimage_inplace_converter_map[QImage::Format_Indexed8][QImage::Format_Grayscale8] =
convert_Indexed8_to_Grayscale8_inplace;
diff --git a/src/gui/image/qimage_neon.cpp b/src/gui/image/qimage_neon.cpp
index d3437b2818..b513dc2894 100644
--- a/src/gui/image/qimage_neon.cpp
+++ b/src/gui/image/qimage_neon.cpp
@@ -18,7 +18,7 @@ Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_neon(quint32 *dst, cons
// align dst on 128 bits
const int offsetToAlignOn16Bytes = (reinterpret_cast<quintptr>(dst) >> 2) & 0x3;
- for (int i = 0; i < offsetToAlignOn16Bytes; ++i) {
+ for (int i = 0; i < qMin(len, offsetToAlignOn16Bytes); ++i) {
*dst++ = qRgb(src[0], src[1], src[2]);
src += 3;
}
diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h
index ba22c99ed7..0d42f94253 100644
--- a/src/gui/image/qimage_p.h
+++ b/src/gui/image/qimage_p.h
@@ -21,6 +21,8 @@
#include <QtCore/private/qnumeric_p.h>
#include <QtCore/qlist.h>
#include <QtCore/qmap.h>
+#include <QtCore/qttypetraits.h>
+
QT_BEGIN_NAMESPACE
@@ -92,18 +94,18 @@ QImageData::calculateImageParameters(qsizetype width, qsizetype height, qsizetyp
// calculate the size, taking care of overflows
qsizetype bytes_per_line;
- if (mul_overflow(width, depth, &bytes_per_line))
+ if (qMulOverflow(width, depth, &bytes_per_line))
return invalid;
- if (add_overflow(bytes_per_line, qsizetype(31), &bytes_per_line))
+ if (qAddOverflow(bytes_per_line, qsizetype(31), &bytes_per_line))
return invalid;
// bytes per scanline (must be multiple of 4)
bytes_per_line = (bytes_per_line >> 5) << 2; // can't overflow
qsizetype total_size;
- if (mul_overflow(height, bytes_per_line, &total_size))
+ if (qMulOverflow(height, bytes_per_line, &total_size))
return invalid;
qsizetype dummy;
- if (mul_overflow(height, qsizetype(sizeof(uchar *)), &dummy))
+ if (qMulOverflow(height, qsizetype(sizeof(uchar *)), &dummy))
return invalid; // why is this here?
#if 1 || QT_VERSION < QT_VERSION_CHECK(6,0,0) // ### can only fix this if QImage dimensions are not int anymore
// Disallow images where width * depth calculations might overflow
@@ -193,6 +195,9 @@ inline int qt_depthForFormat(QImage::Format format)
case QImage::Format_RGBA32FPx4_Premultiplied:
depth = 128;
break;
+ case QImage::Format_CMYK8888:
+ depth = 32;
+ break;
}
return depth;
}
@@ -246,6 +251,7 @@ inline QImage::Format qt_opaqueVersion(QImage::Format format)
case QImage::Format_RGBX32FPx4:
case QImage::Format_Grayscale8:
case QImage::Format_Grayscale16:
+ case QImage::Format_CMYK8888:
return format;
case QImage::Format_Mono:
case QImage::Format_MonoLSB:
@@ -309,12 +315,84 @@ inline QImage::Format qt_alphaVersion(QImage::Format format)
case QImage::Format_Alpha8:
case QImage::Format_Grayscale8:
case QImage::Format_Invalid:
+ case QImage::Format_CMYK8888:
case QImage::NImageFormats:
break;
}
return QImage::Format_ARGB32_Premultiplied;
}
+// Returns an opaque version that is compatible with format
+inline QImage::Format qt_maybeDataCompatibleOpaqueVersion(QImage::Format format)
+{
+ switch (format) {
+ case QImage::Format_ARGB6666_Premultiplied:
+ return QImage::Format_RGB666;
+ case QImage::Format_ARGB4444_Premultiplied:
+ return QImage::Format_RGB444;
+ case QImage::Format_RGBA8888:
+ case QImage::Format_RGBA8888_Premultiplied:
+ return QImage::Format_RGBX8888;
+ case QImage::Format_A2BGR30_Premultiplied:
+ return QImage::Format_BGR30;
+ case QImage::Format_A2RGB30_Premultiplied:
+ return QImage::Format_RGB30;
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
+ return QImage::Format_RGBX64;
+ case QImage::Format_RGBA16FPx4:
+ case QImage::Format_RGBA16FPx4_Premultiplied:
+ return QImage::Format_RGBX16FPx4;
+ case QImage::Format_RGBA32FPx4:
+ case QImage::Format_RGBA32FPx4_Premultiplied:
+ return QImage::Format_RGBX32FPx4;
+ case QImage::Format_ARGB32_Premultiplied:
+ case QImage::Format_ARGB32:
+ return QImage::Format_RGB32;
+ case QImage::Format_RGB16:
+ case QImage::Format_RGB32:
+ case QImage::Format_RGB444:
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB666:
+ case QImage::Format_RGB888:
+ case QImage::Format_BGR888:
+ case QImage::Format_RGBX8888:
+ case QImage::Format_BGR30:
+ case QImage::Format_RGB30:
+ case QImage::Format_RGBX64:
+ case QImage::Format_RGBX16FPx4:
+ case QImage::Format_RGBX32FPx4:
+ case QImage::Format_Grayscale8:
+ case QImage::Format_Grayscale16:
+ case QImage::Format_CMYK8888:
+ return format; // Already opaque
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ case QImage::Format_Indexed8:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_Alpha8:
+ case QImage::Format_Invalid:
+ case QImage::NImageFormats:
+ break;
+ }
+ return format; // No compatible opaque versions
+}
+
+constexpr QImage::Format qt_toUnpremultipliedFormat(QImage::Format format)
+{
+ // Assumes input is already a premultiplied format with an unpremultiplied counterpart
+ // This abuses the fact unpremultiplied formats are always before their premultiplied counterparts.
+ return static_cast<QImage::Format>(qToUnderlying(format) - 1);
+}
+
+constexpr QImage::Format qt_toPremultipliedFormat(QImage::Format format)
+{
+ // Assumes input is already an unpremultiplied format
+ // This abuses the fact unpremultiplied formats are always before their premultiplied counterparts.
+ return static_cast<QImage::Format>(qToUnderlying(format) + 1);
+}
+
inline bool qt_highColorPrecision(QImage::Format format, bool opaque = false)
{
// Formats with higher color precision than ARGB32_Premultiplied.
@@ -359,10 +437,98 @@ inline bool qt_fpColorPrecision(QImage::Format format)
return false;
}
-inline QImage::Format qt_maybeAlphaVersionWithSameDepth(QImage::Format format)
+inline QColorSpace::ColorModel qt_csColorData(QPixelFormat::ColorModel format)
{
- const QImage::Format toFormat = qt_alphaVersion(format);
- return qt_depthForFormat(format) == qt_depthForFormat(toFormat) ? toFormat : format;
+ switch (format) {
+ case QPixelFormat::ColorModel::RGB:
+ case QPixelFormat::ColorModel::BGR:
+ case QPixelFormat::ColorModel::Indexed:
+ return QColorSpace::ColorModel::Rgb;
+ case QPixelFormat::ColorModel::Alpha:
+ return QColorSpace::ColorModel::Undefined; // No valid colors
+ case QPixelFormat::ColorModel::Grayscale:
+ return QColorSpace::ColorModel::Gray;
+ case QPixelFormat::ColorModel::CMYK:
+ return QColorSpace::ColorModel::Cmyk;
+ default:
+ break;
+ }
+ return QColorSpace::ColorModel::Undefined;
+}
+
+inline bool qt_compatibleColorModel(QPixelFormat::ColorModel data, QColorSpace::ColorModel cs)
+{
+ QColorSpace::ColorModel dataCs = qt_csColorData(data);
+
+ if (data == QPixelFormat::ColorModel::Alpha)
+ return true; // Alpha data has no colors and can be handled by any color space
+
+ if (cs == QColorSpace::ColorModel::Undefined || dataCs == QColorSpace::ColorModel::Undefined)
+ return false;
+
+ if (dataCs == cs)
+ return true; // Matching color models
+
+ if (dataCs == QColorSpace::ColorModel::Gray)
+ return true; // Can apply any CS with white point to Gray data
+
+ return false;
+}
+
+inline QImage::Format qt_maybeDataCompatibleAlphaVersion(QImage::Format format)
+{
+ switch (format) {
+ case QImage::Format_RGB32:
+ return QImage::Format_ARGB32_Premultiplied;
+ case QImage::Format_RGB666:
+ return QImage::Format_ARGB6666_Premultiplied;
+ case QImage::Format_RGB444:
+ return QImage::Format_ARGB4444_Premultiplied;
+ case QImage::Format_RGBX8888:
+ return QImage::Format_RGBA8888_Premultiplied;
+ case QImage::Format_BGR30:
+ return QImage::Format_A2BGR30_Premultiplied;
+ case QImage::Format_RGB30:
+ return QImage::Format_A2RGB30_Premultiplied;
+ case QImage::Format_RGBX64:
+ return QImage::Format_RGBA64_Premultiplied;
+ case QImage::Format_RGBX16FPx4:
+ return QImage::Format_RGBA16FPx4_Premultiplied;
+ case QImage::Format_RGBX32FPx4:
+ return QImage::Format_RGBA32FPx4_Premultiplied;
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB4444_Premultiplied:
+ case QImage::Format_RGBA8888:
+ case QImage::Format_RGBA8888_Premultiplied:
+ case QImage::Format_A2BGR30_Premultiplied:
+ case QImage::Format_A2RGB30_Premultiplied:
+ case QImage::Format_Alpha8:
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA64_Premultiplied:
+ case QImage::Format_RGBA16FPx4:
+ case QImage::Format_RGBA16FPx4_Premultiplied:
+ case QImage::Format_RGBA32FPx4:
+ case QImage::Format_RGBA32FPx4_Premultiplied:
+ return format; // Already alpha versions
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ case QImage::Format_Indexed8:
+ case QImage::Format_RGB16:
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB888:
+ case QImage::Format_BGR888:
+ case QImage::Format_Grayscale8:
+ case QImage::Format_Grayscale16:
+ case QImage::Format_CMYK8888:
+ case QImage::Format_Invalid:
+ case QImage::NImageFormats:
+ break;
+ }
+ return format; // No data-compatible alpha version
}
inline QImage::Format qt_opaqueVersionForPainting(QImage::Format format)
diff --git a/src/gui/image/qimageiohandler.cpp b/src/gui/image/qimageiohandler.cpp
index 41a4e8a803..1dcfd9a074 100644
--- a/src/gui/image/qimageiohandler.cpp
+++ b/src/gui/image/qimageiohandler.cpp
@@ -124,20 +124,15 @@
variants should return a list of supported variant names
(QList<QByteArray>) in this option.
- \value OptimizedWrite. A handler which supports this option
+ \value OptimizedWrite A handler which supports this option
is expected to turn on optimization flags when writing.
- \value ProgressiveScanWrite. A handler which supports
+ \value ProgressiveScanWrite A handler which supports
this option is expected to write the image as a progressive scan image.
- \value ImageTransformation. A handler which supports this option can read
+ \value ImageTransformation A handler which supports this option can read
the transformation metadata of an image. A handler that supports this option
should not apply the transformation itself.
-
-\if !defined(qt6)
- \value TransformedByDefault. A handler that reports support for this feature
- will have image transformation metadata applied by default on read.
-\endif
*/
/*! \enum QImageIOHandler::Transformation
diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp
index f50dd8bdb7..9366e9cbb1 100644
--- a/src/gui/image/qimagereader.cpp
+++ b/src/gui/image/qimagereader.cpp
@@ -11,7 +11,6 @@
\inmodule QtGui
\reentrant
\ingroup painting
- \ingroup io
The most common way to read images is through QImage and QPixmap's
constructors, or by calling QImage::load() and
@@ -138,6 +137,9 @@ QT_BEGIN_NAMESPACE
using namespace QImageReaderWriterHelpers;
using namespace Qt::StringLiterals;
+Q_TRACE_POINT(qtgui, QImageReader_read_before_reading, QImageReader *reader, const QString &filename);
+Q_TRACE_POINT(qtgui, QImageReader_read_after_reading, QImageReader *reader, bool result);
+
static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
const QByteArray &format,
bool autoDetectImageFormat,
@@ -461,7 +463,7 @@ public:
static int maxAlloc;
};
-int QImageReaderPrivate::maxAlloc = 128; // 128 MB is enough for an 8K 32bpp image
+int QImageReaderPrivate::maxAlloc = 256; // 256 MB is enough for an 8K 64bpp image
/*!
\internal
@@ -484,9 +486,9 @@ QImageReaderPrivate::QImageReaderPrivate(QImageReader *qq)
*/
QImageReaderPrivate::~QImageReaderPrivate()
{
+ delete handler;
if (deleteDevice)
delete device;
- delete handler;
}
/*!
@@ -527,14 +529,15 @@ bool QImageReaderPrivate::initHandler()
int currentExtension = 0;
QString fileName = file->fileName();
+ bool fileIsOpen;
do {
file->setFileName(fileName + u'.'
+ QLatin1StringView(extensions.at(currentExtension++).constData()));
- file->open(QIODevice::ReadOnly);
- } while (!file->isOpen() && currentExtension < extensions.size());
+ fileIsOpen = file->open(QIODevice::ReadOnly);
+ } while (!fileIsOpen && currentExtension < extensions.size());
- if (!device->isOpen()) {
+ if (!fileIsOpen) {
imageReaderError = QImageReader::FileNotFoundError;
errorString = QImageReader::tr("File not found");
file->setFileName(fileName); // restore the old file name
@@ -556,7 +559,7 @@ bool QImageReaderPrivate::initHandler()
*/
void QImageReaderPrivate::getText()
{
- if (text.isEmpty() && initHandler() && handler->supportsOption(QImageIOHandler::Description))
+ if (text.isEmpty() && q->supportsOption(QImageIOHandler::Description))
text = qt_getImageTextFromDescription(handler->option(QImageIOHandler::Description).toString());
}
@@ -746,12 +749,12 @@ bool QImageReader::decideFormatFromContent() const
*/
void QImageReader::setDevice(QIODevice *device)
{
+ delete d->handler;
+ d->handler = nullptr;
if (d->device && d->deleteDevice)
delete d->device;
d->device = device;
d->deleteDevice = false;
- delete d->handler;
- d->handler = nullptr;
d->text.clear();
}
@@ -846,10 +849,7 @@ int QImageReader::quality() const
*/
QSize QImageReader::size() const
{
- if (!d->initHandler())
- return QSize();
-
- if (d->handler->supportsOption(QImageIOHandler::Size))
+ if (supportsOption(QImageIOHandler::Size))
return d->handler->option(QImageIOHandler::Size).toSize();
return QSize();
@@ -869,10 +869,7 @@ QSize QImageReader::size() const
*/
QImage::Format QImageReader::imageFormat() const
{
- if (!d->initHandler())
- return QImage::Format_Invalid;
-
- if (d->handler->supportsOption(QImageIOHandler::ImageFormat))
+ if (supportsOption(QImageIOHandler::ImageFormat))
return (QImage::Format)d->handler->option(QImageIOHandler::ImageFormat).toInt();
return QImage::Format_Invalid;
@@ -944,6 +941,10 @@ QRect QImageReader::clipRect() const
support scaling), QImageReader will use QImage::scale() with
Qt::SmoothScaling.
+ If only one dimension is set in \a size, the other one will be
+ computed from the image's \l {size()} {natural size} so as to
+ maintain the aspect ratio.
+
\sa scaledSize(), setClipRect(), setScaledClipRect()
*/
void QImageReader::setScaledSize(const QSize &size)
@@ -994,9 +995,7 @@ QRect QImageReader::scaledClipRect() const
*/
void QImageReader::setBackgroundColor(const QColor &color)
{
- if (!d->initHandler())
- return;
- if (d->handler->supportsOption(QImageIOHandler::BackgroundColor))
+ if (supportsOption(QImageIOHandler::BackgroundColor))
d->handler->setOption(QImageIOHandler::BackgroundColor, color);
}
@@ -1011,9 +1010,7 @@ void QImageReader::setBackgroundColor(const QColor &color)
*/
QColor QImageReader::backgroundColor() const
{
- if (!d->initHandler())
- return QColor();
- if (d->handler->supportsOption(QImageIOHandler::BackgroundColor))
+ if (supportsOption(QImageIOHandler::BackgroundColor))
return qvariant_cast<QColor>(d->handler->option(QImageIOHandler::BackgroundColor));
return QColor();
}
@@ -1028,9 +1025,7 @@ QColor QImageReader::backgroundColor() const
*/
bool QImageReader::supportsAnimation() const
{
- if (!d->initHandler())
- return false;
- if (d->handler->supportsOption(QImageIOHandler::Animation))
+ if (supportsOption(QImageIOHandler::Animation))
return d->handler->option(QImageIOHandler::Animation).toBool();
return false;
}
@@ -1042,10 +1037,7 @@ bool QImageReader::supportsAnimation() const
*/
QByteArray QImageReader::subType() const
{
- if (!d->initHandler())
- return QByteArray();
-
- if (d->handler->supportsOption(QImageIOHandler::SubType))
+ if (supportsOption(QImageIOHandler::SubType))
return d->handler->option(QImageIOHandler::SubType).toByteArray();
return QByteArray();
}
@@ -1057,10 +1049,7 @@ QByteArray QImageReader::subType() const
*/
QList<QByteArray> QImageReader::supportedSubTypes() const
{
- if (!d->initHandler())
- return QList<QByteArray>();
-
- if (d->handler->supportsOption(QImageIOHandler::SupportedSubTypes))
+ if (supportsOption(QImageIOHandler::SupportedSubTypes))
return qvariant_cast<QList<QByteArray> >(d->handler->option(QImageIOHandler::SupportedSubTypes));
return QList<QByteArray>();
}
@@ -1076,7 +1065,7 @@ QList<QByteArray> QImageReader::supportedSubTypes() const
QImageIOHandler::Transformations QImageReader::transformation() const
{
int option = QImageIOHandler::TransformationNone;
- if (d->initHandler() && d->handler->supportsOption(QImageIOHandler::ImageTransformation))
+ if (supportsOption(QImageIOHandler::ImageTransformation))
option = d->handler->option(QImageIOHandler::ImageTransformation).toInt();
return QImageIOHandler::Transformations(option);
}
@@ -1194,20 +1183,39 @@ bool QImageReader::read(QImage *image)
if (!d->initHandler())
return false;
+ QSize scaledSize = d->scaledSize;
+ if ((scaledSize.width() <= 0 && scaledSize.height() > 0) ||
+ (scaledSize.height() <= 0 && scaledSize.width() > 0)) {
+ // if only one dimension is given, let's try to calculate the second one
+ // based on the original image size and maintaining the aspect ratio
+ if (const QSize originalSize = size(); !originalSize.isEmpty()) {
+ if (scaledSize.width() <= 0) {
+ const auto ratio = qreal(scaledSize.height()) / originalSize.height();
+ scaledSize.setWidth(qRound(originalSize.width() * ratio));
+ } else {
+ const auto ratio = qreal(scaledSize.width()) / originalSize.width();
+ scaledSize.setHeight(qRound(originalSize.height() * ratio));
+ }
+ }
+ }
+
+ const bool supportScaledSize = supportsOption(QImageIOHandler::ScaledSize) && scaledSize.isValid();
+ const bool supportClipRect = supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull();
+ const bool supportScaledClipRect = supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull();
+
// set the handler specific options.
- if (d->handler->supportsOption(QImageIOHandler::ScaledSize) && d->scaledSize.isValid()) {
- if ((d->handler->supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull())
- || d->clipRect.isNull()) {
+ if (supportScaledSize) {
+ if (supportClipRect || d->clipRect.isNull()) {
// Only enable the ScaledSize option if there is no clip rect, or
// if the handler also supports ClipRect.
- d->handler->setOption(QImageIOHandler::ScaledSize, d->scaledSize);
+ d->handler->setOption(QImageIOHandler::ScaledSize, scaledSize);
}
}
- if (d->handler->supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull())
+ if (supportClipRect)
d->handler->setOption(QImageIOHandler::ClipRect, d->clipRect);
- if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull())
+ if (supportScaledClipRect)
d->handler->setOption(QImageIOHandler::ScaledClipRect, d->scaledClipRect);
- if (d->handler->supportsOption(QImageIOHandler::Quality))
+ if (supportsOption(QImageIOHandler::Quality))
d->handler->setOption(QImageIOHandler::Quality, d->quality);
// read the image
@@ -1228,9 +1236,9 @@ bool QImageReader::read(QImage *image)
// provide default implementations for any unsupported image
// options
- if (d->handler->supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull()) {
- if (d->handler->supportsOption(QImageIOHandler::ScaledSize) && d->scaledSize.isValid()) {
- if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ if (supportClipRect) {
+ if (supportScaledSize) {
+ if (supportScaledClipRect) {
// all features are supported by the handler; nothing to do.
} else {
// the image is already scaled, so apply scaled clipping.
@@ -1238,12 +1246,12 @@ bool QImageReader::read(QImage *image)
*image = image->copy(d->scaledClipRect);
}
} else {
- if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ if (supportScaledClipRect) {
// supports scaled clipping but not scaling, most
// likely a broken handler.
} else {
- if (d->scaledSize.isValid()) {
- *image = image->scaled(d->scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ if (scaledSize.isValid()) {
+ *image = image->scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
if (d->scaledClipRect.isValid()) {
*image = image->copy(d->scaledClipRect);
@@ -1251,8 +1259,8 @@ bool QImageReader::read(QImage *image)
}
}
} else {
- if (d->handler->supportsOption(QImageIOHandler::ScaledSize) && d->scaledSize.isValid() && d->clipRect.isNull()) {
- if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ if (supportScaledSize && d->clipRect.isNull()) {
+ if (supportScaledClipRect) {
// nothing to do (ClipRect is ignored!)
} else {
// provide all workarounds.
@@ -1261,7 +1269,7 @@ bool QImageReader::read(QImage *image)
}
}
} else {
- if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ if (supportScaledClipRect) {
// this makes no sense; a handler that supports
// ScaledClipRect but not ScaledSize is broken, and we
// can't work around it.
@@ -1269,8 +1277,8 @@ bool QImageReader::read(QImage *image)
// provide all workarounds.
if (d->clipRect.isValid())
*image = image->copy(d->clipRect);
- if (d->scaledSize.isValid())
- *image = image->scaled(d->scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ if (scaledSize.isValid())
+ *image = image->scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
if (d->scaledClipRect.isValid())
*image = image->copy(d->scaledClipRect);
}
@@ -1281,7 +1289,7 @@ bool QImageReader::read(QImage *image)
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')
+ if (suffix.size() == 3 && suffix[0] == '@' && suffix[1] >= '2' && suffix[1] <= '9' && suffix[2] == 'x')
image->setDevicePixelRatio(suffix[1] - '0');
}
if (autoTransform())
@@ -1570,6 +1578,8 @@ int QImageReader::allocationLimit()
loading corrupt image files. It is normally not needed to change it. The
default limit is large enough for all commonly used image sizes.
+ At runtime, this value may be overridden by the environment variable \c QT_IMAGEIO_MAXALLOC.
+
\note The memory requirements are calculated for a minimum of 32 bits per pixel, since Qt will
typically convert an image to that depth when it is used in GUI. This means that the effective
allocation limit is significantly smaller than \a mbLimit when reading 1 bpp and 8 bpp images.
diff --git a/src/gui/image/qimagereaderwriterhelpers.cpp b/src/gui/image/qimagereaderwriterhelpers.cpp
index 0500df790b..502b0f95f0 100644
--- a/src/gui/image/qimagereaderwriterhelpers.cpp
+++ b/src/gui/image/qimagereaderwriterhelpers.cpp
@@ -15,9 +15,9 @@ namespace QImageReaderWriterHelpers {
#ifndef QT_NO_IMAGEFORMATPLUGIN
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, irhLoader,
(QImageIOHandlerFactoryInterface_iid, "/imageformats"_L1))
-Q_GLOBAL_STATIC(QMutex, loaderMutex)
+Q_GLOBAL_STATIC(QMutex, irhLoaderMutex)
static void appendImagePluginFormats(QFactoryLoader *loader,
QImageIOPlugin::Capability cap,
@@ -68,9 +68,9 @@ static void appendImagePluginMimeTypes(QFactoryLoader *loader,
QSharedPointer<QFactoryLoader> pluginLoader()
{
- loaderMutex()->lock();
- return QSharedPointer<QFactoryLoader>(loader(), [](QFactoryLoader *) {
- loaderMutex()->unlock();
+ irhLoaderMutex()->lock();
+ return QSharedPointer<QFactoryLoader>(irhLoader(), [](QFactoryLoader *) {
+ irhLoaderMutex()->unlock();
});
}
@@ -89,7 +89,7 @@ QList<QByteArray> supportedImageFormats(Capability cap)
formats << _qt_BuiltInFormats[i].extension;
#ifndef QT_NO_IMAGEFORMATPLUGIN
- appendImagePluginFormats(loader(), pluginCapability(cap), &formats);
+ appendImagePluginFormats(irhLoader(), pluginCapability(cap), &formats);
#endif // QT_NO_IMAGEFORMATPLUGIN
std::sort(formats.begin(), formats.end());
@@ -97,15 +97,17 @@ QList<QByteArray> supportedImageFormats(Capability cap)
return formats;
}
+static constexpr QByteArrayView imagePrefix() noexcept { return "image/"; }
+
QList<QByteArray> supportedMimeTypes(Capability cap)
{
QList<QByteArray> mimeTypes;
mimeTypes.reserve(_qt_NumFormats);
for (const auto &fmt : _qt_BuiltInFormats)
- mimeTypes.append(QByteArrayLiteral("image/") + fmt.mimeType);
+ mimeTypes.emplace_back(imagePrefix() + fmt.mimeType);
#ifndef QT_NO_IMAGEFORMATPLUGIN
- appendImagePluginMimeTypes(loader(), pluginCapability(cap), &mimeTypes);
+ appendImagePluginMimeTypes(irhLoader(), pluginCapability(cap), &mimeTypes);
#endif // QT_NO_IMAGEFORMATPLUGIN
std::sort(mimeTypes.begin(), mimeTypes.end());
@@ -113,11 +115,11 @@ QList<QByteArray> supportedMimeTypes(Capability cap)
return mimeTypes;
}
-QList<QByteArray> imageFormatsForMimeType(const QByteArray &mimeType, Capability cap)
+QList<QByteArray> imageFormatsForMimeType(QByteArrayView mimeType, Capability cap)
{
QList<QByteArray> formats;
- if (mimeType.startsWith("image/")) {
- const QByteArray type = mimeType.mid(sizeof("image/") - 1);
+ if (mimeType.startsWith(imagePrefix())) {
+ const QByteArrayView type = mimeType.mid(imagePrefix().size());
for (const auto &fmt : _qt_BuiltInFormats) {
if (fmt.mimeType == type && !formats.contains(fmt.extension))
formats << fmt.extension;
@@ -127,7 +129,7 @@ QList<QByteArray> imageFormatsForMimeType(const QByteArray &mimeType, Capability
#ifndef QT_NO_IMAGEFORMATPLUGIN
QList<QByteArray> mimeTypes;
QList<QByteArray> keys;
- appendImagePluginMimeTypes(loader(), pluginCapability(cap), &mimeTypes, &keys);
+ appendImagePluginMimeTypes(irhLoader(), pluginCapability(cap), &mimeTypes, &keys);
for (int i = 0; i < mimeTypes.size(); ++i) {
if (mimeTypes.at(i) == mimeType) {
const auto &key = keys.at(i);
diff --git a/src/gui/image/qimagereaderwriterhelpers_p.h b/src/gui/image/qimagereaderwriterhelpers_p.h
index 8e999db5b9..930a57b536 100644
--- a/src/gui/image/qimagereaderwriterhelpers_p.h
+++ b/src/gui/image/qimagereaderwriterhelpers_p.h
@@ -94,7 +94,7 @@ enum Capability {
};
QList<QByteArray> supportedImageFormats(Capability cap);
QList<QByteArray> supportedMimeTypes(Capability cap);
-QList<QByteArray> imageFormatsForMimeType(const QByteArray &mimeType, Capability cap);
+QList<QByteArray> imageFormatsForMimeType(QByteArrayView mimeType, Capability cap);
}
diff --git a/src/gui/image/qimagewriter.cpp b/src/gui/image/qimagewriter.cpp
index 6f0d7642d8..d2176e4189 100644
--- a/src/gui/image/qimagewriter.cpp
+++ b/src/gui/image/qimagewriter.cpp
@@ -9,7 +9,6 @@
\inmodule QtGui
\reentrant
\ingroup painting
- \ingroup io
QImageWriter supports setting format specific options, such as
compression level and quality, prior to storing the
@@ -315,9 +314,9 @@ QImageWriter::QImageWriter(const QString &fileName, const QByteArray &format)
*/
QImageWriter::~QImageWriter()
{
+ delete d->handler;
if (d->deleteDevice)
delete d->device;
- delete d->handler;
delete d;
}
@@ -362,13 +361,13 @@ QByteArray QImageWriter::format() const
*/
void QImageWriter::setDevice(QIODevice *device)
{
+ delete d->handler;
+ d->handler = nullptr;
if (d->device && d->deleteDevice)
delete d->device;
d->device = device;
d->deleteDevice = false;
- delete d->handler;
- d->handler = nullptr;
}
/*!
diff --git a/src/gui/image/qmovie.cpp b/src/gui/image/qmovie.cpp
index 875d88225f..0d13639d35 100644
--- a/src/gui/image/qmovie.cpp
+++ b/src/gui/image/qmovie.cpp
@@ -53,7 +53,7 @@
Call supportedFormats() for a list of formats that QMovie supports.
- \sa QLabel, QImageReader, {Movie Example}
+ \sa QLabel, QImageReader
*/
/*! \enum QMovie::MovieState
@@ -144,11 +144,11 @@
#include "qrect.h"
#include "qelapsedtimer.h"
#include "qtimer.h"
-#include "qpair.h"
#include "qmap.h"
#include "qlist.h"
#include "qbuffer.h"
#include "qdir.h"
+#include "qloggingcategory.h"
#include "private/qobject_p.h"
#include "private/qproperty_p.h"
@@ -156,6 +156,8 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcImageIo)
+
class QFrameInfo
{
public:
@@ -306,6 +308,19 @@ QFrameInfo QMoviePrivate::infoForFrame(int frameNumber)
return QFrameInfo(); // Invalid
}
+ // For an animated image format, the tradition is that QMovie calls read()
+ // until canRead() == false, because the number of frames may not be known
+ // in advance; but if we're abusing a multi-frame format as an animation,
+ // canRead() may remain true, and we need to stop after reading the maximum
+ // number of frames that the image provides.
+ const bool supportsAnimation = reader->supportsOption(QImageIOHandler::Animation);
+ const int stopAtFrame = supportsAnimation ? -1 : frameCount();
+
+ // For an animated image format, QImageIOHandler::nextImageDelay() should
+ // provide the time to wait until showing the next frame; but multi-frame
+ // formats are not expected to provide this value, so use 1000 ms by default.
+ const auto nextFrameDelay = [&]() { return supportsAnimation ? reader->nextImageDelay() : 1000; };
+
if (cacheMode == QMovie::CacheNone) {
if (frameNumber != currentFrameNumber+1) {
// Non-sequential frame access
@@ -335,8 +350,12 @@ QFrameInfo QMoviePrivate::infoForFrame(int frameNumber)
}
}
}
- if (reader->canRead()) {
+ qCDebug(lcImageIo, "CacheNone: read frame %d of %d", frameNumber, stopAtFrame);
+ if (stopAtFrame > 0 ? (frameNumber < stopAtFrame) : reader->canRead()) {
// reader says we can read. Attempt to actually read image
+ // But if it's a non-animated multi-frame format and we know the frame count, stop there.
+ if (stopAtFrame > 0)
+ reader->jumpToImage(frameNumber);
QImage anImage = reader->read();
if (anImage.isNull()) {
// Reading image failed.
@@ -344,7 +363,7 @@ QFrameInfo QMoviePrivate::infoForFrame(int frameNumber)
}
if (frameNumber > greatestFrameNumber)
greatestFrameNumber = frameNumber;
- return QFrameInfo(QPixmap::fromImage(std::move(anImage)), reader->nextImageDelay());
+ return QFrameInfo(QPixmap::fromImage(std::move(anImage)), nextFrameDelay());
} else if (frameNumber != 0) {
// We've read all frames now. Return an end marker
haveReadAll = true;
@@ -360,15 +379,19 @@ QFrameInfo QMoviePrivate::infoForFrame(int frameNumber)
if (frameNumber > greatestFrameNumber) {
// Frame hasn't been read from file yet. Try to do it
for (int i = greatestFrameNumber + 1; i <= frameNumber; ++i) {
- if (reader->canRead()) {
+ qCDebug(lcImageIo, "CacheAll: read frame %d of %d", frameNumber, stopAtFrame);
+ if (stopAtFrame > 0 ? (frameNumber < stopAtFrame) : reader->canRead()) {
// reader says we can read. Attempt to actually read image
+ // But if it's a non-animated multi-frame format and we know the frame count, stop there.
+ if (stopAtFrame > 0)
+ reader->jumpToImage(frameNumber);
QImage anImage = reader->read();
if (anImage.isNull()) {
// Reading image failed.
return QFrameInfo(); // Invalid
}
greatestFrameNumber = i;
- QFrameInfo info(QPixmap::fromImage(std::move(anImage)), reader->nextImageDelay());
+ QFrameInfo info(QPixmap::fromImage(std::move(anImage)), nextFrameDelay());
// Cache it!
frameMap.insert(i, info);
if (i == frameNumber) {
@@ -426,11 +449,7 @@ bool QMoviePrivate::next()
}
// Image and delay OK, update internal state
currentFrameNumber = nextFrameNumber++;
- QSize scaledSize = reader->scaledSize();
- if (scaledSize.isValid() && (scaledSize != info.pixmap.size()))
- currentPixmap = QPixmap::fromImage( info.pixmap.toImage().scaled(scaledSize) );
- else
- currentPixmap = info.pixmap;
+ currentPixmap = info.pixmap;
if (!speed)
return true;
diff --git a/src/gui/image/qpicture.h b/src/gui/image/qpicture.h
index c8b0966a28..bc8be6c4e9 100644
--- a/src/gui/image/qpicture.h
+++ b/src/gui/image/qpicture.h
@@ -6,8 +6,8 @@
#include <QtGui/qtguiglobal.h>
#include <QtCore/qiodevice.h>
+#include <QtCore/qshareddata.h>
#include <QtCore/qstringlist.h>
-#include <QtCore/qsharedpointer.h>
#include <QtGui/qpaintdevice.h>
QT_BEGIN_NAMESPACE
diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp
index 081e28d1c6..89b8d5303b 100644
--- a/src/gui/image/qpixmap.cpp
+++ b/src/gui/image/qpixmap.cpp
@@ -38,6 +38,9 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
+Q_TRACE_PARAM_REPLACE(Qt::AspectRatioMode, int);
+Q_TRACE_PARAM_REPLACE(Qt::TransformationMode, int);
+
// MSVC 19.28 does show spurious warning "C4723: potential divide by 0" for code that divides
// by height() in release builds. Anyhow, all the code paths in this file are only executed
// for valid QPixmap's, where height() cannot be 0. Therefore disable the warning.
@@ -620,7 +623,9 @@ void QPixmap::setDevicePixelRatio(qreal scaleFactor)
This value should be used when using the pixmap size in user interface
size calculations.
- The return value is equivalent to pixmap.size() / pixmap.devicePixelRatio(),
+ The return value is equivalent to pixmap.size() / pixmap.devicePixelRatio().
+
+ \since 6.2
*/
QSizeF QPixmap::deviceIndependentSize() const
{
@@ -712,7 +717,7 @@ bool QPixmap::load(const QString &fileName, const char *format, Qt::ImageConvers
QString key = "qt_pixmap"_L1
% info.absoluteFilePath()
- % HexString<uint>(info.lastModified().toSecsSinceEpoch())
+ % HexString<uint>(info.lastModified(QTimeZone::UTC).toSecsSinceEpoch())
% HexString<quint64>(info.size())
% HexString<uint>(data ? data->pixelType() : QPlatformPixmap::PixmapType);
@@ -1030,7 +1035,7 @@ bool QPixmap::convertFromImage(const QImage &image, Qt::ImageConversionFlags fla
Transformations}
*/
-QPixmap QPixmap::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const
+QPixmap Q_TRACE_INSTRUMENT(qtgui) QPixmap::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const
{
if (isNull()) {
qWarning("QPixmap::scaled: Pixmap is a null pixmap");
@@ -1068,7 +1073,7 @@ QPixmap QPixmap::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::Tran
\sa isNull(), {QPixmap#Pixmap Transformations}{Pixmap
Transformations}
*/
-QPixmap QPixmap::scaledToWidth(int w, Qt::TransformationMode mode) const
+QPixmap Q_TRACE_INSTRUMENT(qtgui) QPixmap::scaledToWidth(int w, Qt::TransformationMode mode) const
{
if (isNull()) {
qWarning("QPixmap::scaleWidth: Pixmap is a null pixmap");
@@ -1098,7 +1103,7 @@ QPixmap QPixmap::scaledToWidth(int w, Qt::TransformationMode mode) const
\sa isNull(), {QPixmap#Pixmap Transformations}{Pixmap
Transformations}
*/
-QPixmap QPixmap::scaledToHeight(int h, Qt::TransformationMode mode) const
+QPixmap Q_TRACE_INSTRUMENT(qtgui) QPixmap::scaledToHeight(int h, Qt::TransformationMode mode) const
{
if (isNull()) {
qWarning("QPixmap::scaleHeight: Pixmap is a null pixmap");
@@ -1275,8 +1280,9 @@ QPixmap QPixmap::transformed(const QTransform &transform,
QPixmap using the fromImage(). If this is too expensive an
operation, you can use QBitmap::fromImage() instead.
- To convert a QPixmap to and from HICON you can use the QtWinExtras
- functions QtWin::toHICON() and QtWin::fromHICON() respectively.
+ To convert a QPixmap to and from HICON you can use the
+ QImage::toHICON() and QImage::fromHICON() functions respectively
+ (after converting the QPixmap to a QImage, as explained above).
\section1 Pixmap Transformations
diff --git a/src/gui/image/qpixmap.h b/src/gui/image/qpixmap.h
index 544425754b..5be98be14d 100644
--- a/src/gui/image/qpixmap.h
+++ b/src/gui/image/qpixmap.h
@@ -8,8 +8,8 @@
#include <QtGui/qpaintdevice.h>
#include <QtGui/qcolor.h>
#include <QtCore/qnamespace.h>
+#include <QtCore/qshareddata.h>
#include <QtCore/qstring.h> // char*->QString conversion
-#include <QtCore/qsharedpointer.h>
#include <QtGui/qimage.h>
#include <QtGui/qtransform.h>
@@ -129,7 +129,6 @@ private:
QPixmap(const QSize &s, int type);
void doInit(int, int, int);
- Q_DUMMY_COMPARISON_OPERATOR(QPixmap)
friend class QPlatformPixmap;
friend class QBitmap;
friend class QPaintDevice;
@@ -164,7 +163,7 @@ inline void QPixmap::scroll(int dx, int dy, int ax, int ay, int awidth, int ahei
inline bool QPixmap::loadFromData(const QByteArray &buf, const char *format,
Qt::ImageConversionFlags flags)
{
- return loadFromData(reinterpret_cast<const uchar *>(buf.constData()), buf.size(), format, flags);
+ return loadFromData(reinterpret_cast<const uchar *>(buf.constData()), uint(buf.size()), format, flags);
}
diff --git a/src/gui/image/qpixmap_win.cpp b/src/gui/image/qpixmap_win.cpp
index 47f0eae2ef..fc601bccfc 100644
--- a/src/gui/image/qpixmap_win.cpp
+++ b/src/gui/image/qpixmap_win.cpp
@@ -3,6 +3,7 @@
#include "qbitmap.h"
#include "qpixmap.h"
+#include <private/qpixmap_win_p.h>
#include <qpa/qplatformpixmap.h>
#include "qpixmap_raster_p.h"
@@ -163,7 +164,7 @@ static QImage copyImageData(const BITMAPINFOHEADER &header, const RGBQUAD *color
Q_ASSERT(DWORD(image.sizeInBytes()) == header.biSizeImage);
memcpy(image.bits(), data, header.biSizeImage);
if (format == QImage::Format_RGB888)
- image = image.rgbSwapped();
+ image = std::move(image).rgbSwapped();
break;
default:
Q_UNREACHABLE();
@@ -206,7 +207,7 @@ static HBITMAP qt_createIconMask(QImage bm)
return hbm;
}
-Q_GUI_EXPORT HBITMAP qt_createIconMask(const QBitmap &bitmap)
+HBITMAP qt_createIconMask(const QBitmap &bitmap)
{
return qt_createIconMask(bitmap.toImage().convertToFormat(QImage::Format_Mono));
}
@@ -224,7 +225,7 @@ static inline QImage::Format format32(int hbitmapFormat)
return QImage::Format_ARGB32_Premultiplied;
}
-Q_GUI_EXPORT HBITMAP qt_imageToWinHBITMAP(const QImage &imageIn, int hbitmapFormat = 0)
+HBITMAP qt_imageToWinHBITMAP(const QImage &imageIn, int hbitmapFormat)
{
if (imageIn.isNull())
return nullptr;
@@ -304,6 +305,7 @@ Q_GUI_EXPORT HBITMAP qt_imageToWinHBITMAP(const QImage &imageIn, int hbitmapForm
return nullptr;
}
if (!pixels) {
+ DeleteObject(bitmap);
qErrnoWarning("%s, did not allocate pixel data", __FUNCTION__);
return nullptr;
}
@@ -323,7 +325,7 @@ Q_GUI_EXPORT HBITMAP qt_imageToWinHBITMAP(const QImage &imageIn, int hbitmapForm
It is the caller's responsibility to free the \c HBITMAP data
after use.
- For usage with with standard GDI calls, such as \c BitBlt(), the image
+ For usage with standard GDI calls, such as \c BitBlt(), the image
should have the format QImage::Format_RGB32.
When using the resulting HBITMAP for the \c AlphaBlend() GDI function,
@@ -350,7 +352,7 @@ HBITMAP QImage::toHBITMAP() const
return qt_imageToWinHBITMAP(*this);
}
-Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0)
+HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat)
{
if (p.isNull())
return nullptr;
@@ -449,7 +451,7 @@ static QImage imageFromWinHBITMAP_GetDiBits(HBITMAP bitmap, bool forceQuads, int
return copyImageData(info.bmiHeader, bmiColorTable256.bmiColors, data.data(), imageFormat);
}
-Q_GUI_EXPORT QImage qt_imageFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0)
+QImage qt_imageFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat)
{
QImage result = imageFromWinHBITMAP_DibSection(bitmap, hbitmapFormat);
if (result.isNull())
@@ -481,7 +483,7 @@ QImage QImage::fromHBITMAP(HBITMAP hbitmap)
return qt_imageFromWinHBITMAP(hbitmap);
}
-Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0)
+QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat)
{
return QPixmap::fromImage(imageFromWinHBITMAP_GetDiBits(bitmap, /* forceQuads */ true, hbitmapFormat));
}
@@ -532,7 +534,7 @@ HICON QImage::toHICON(const QImage &mask) const
return hIcon;
}
-Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &p)
+HICON qt_pixmapToWinHICON(const QPixmap &p)
{
QImage mask;
QBitmap maskBitmap = p.mask();
@@ -541,7 +543,7 @@ Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &p)
return p.toImage().toHICON(mask);
}
-Q_GUI_EXPORT QImage qt_imageFromWinHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h)
+QImage qt_imageFromWinHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h)
{
QImage image(w, h, QImage::Format_ARGB32_Premultiplied);
if (image.isNull())
@@ -641,7 +643,7 @@ QImage QImage::fromHICON(HICON icon)
return image;
}
-Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon)
+QPixmap qt_pixmapFromWinHICON(HICON icon)
{
return QPixmap::fromImage(QImage::fromHICON(icon));
}
diff --git a/src/gui/image/qpixmap_win_p.h b/src/gui/image/qpixmap_win_p.h
new file mode 100644
index 0000000000..64abca3428
--- /dev/null
+++ b/src/gui/image/qpixmap_win_p.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QPIXMAP_WIN_P_H
+#define QPIXMAP_WIN_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 <QtGui/private/qtguiglobal_p.h>
+#include <QtCore/qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+class QBitmap;
+class QImage;
+class QPixmap;
+
+Q_GUI_EXPORT HBITMAP qt_createIconMask(const QBitmap &bitmap);
+Q_GUI_EXPORT HBITMAP qt_imageToWinHBITMAP(const QImage &imageIn, int hbitmapFormat = 0);
+Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0);
+Q_GUI_EXPORT QImage qt_imageFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0);
+Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0);
+Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &p);
+Q_GUI_EXPORT QImage qt_imageFromWinHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h);
+Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon);
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAP_WIN_P_H
diff --git a/src/gui/image/qpixmapcache.cpp b/src/gui/image/qpixmapcache.cpp
index e77603ced1..45c9743f93 100644
--- a/src/gui/image/qpixmapcache.cpp
+++ b/src/gui/image/qpixmapcache.cpp
@@ -1,7 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#define Q_TEST_QPIXMAPCACHE
#include "qpixmapcache.h"
#include "qobject.h"
#include "qdebug.h"
@@ -9,6 +8,8 @@
#include "qthread.h"
#include "qcoreapplication.h"
+using namespace std::chrono_literals;
+
QT_BEGIN_NAMESPACE
/*!
@@ -183,7 +184,6 @@ public:
void timerEvent(QTimerEvent *) override;
bool insert(const QString& key, const QPixmap &pixmap, int cost);
QPixmapCache::Key insert(const QPixmap &pixmap, int cost);
- bool replace(const QPixmapCache::Key &key, const QPixmap &pixmap, int cost);
bool remove(const QString &key);
bool remove(const QPixmapCache::Key &key);
@@ -203,7 +203,8 @@ public:
bool flushDetachedPixmaps(bool nt);
private:
- enum { soon_time = 10000, flush_time = 30000 };
+ static constexpr auto soon_time = 10s;
+ static constexpr auto flush_time = 30s;
int *keyArray;
int theid;
int ps;
@@ -217,10 +218,15 @@ QT_BEGIN_INCLUDE_NAMESPACE
#include "qpixmapcache.moc"
QT_END_INCLUDE_NAMESPACE
-size_t qHash(const QPixmapCache::Key &k, size_t seed)
+/*!
+ size_t QPixmapCache::qHash(const Key &key, size_t seed = 0);
+ \since 6.6
+
+ Returns the hash value for the \a key, using \a seed to seed the calculation.
+*/
+size_t QPixmapCache::Key::hash(size_t seed) const noexcept
{
- const auto *keyData = QPMCache::get(k);
- return qHash(keyData ? keyData->key : 0, seed);
+ return qHash(this->d ? this->d->key : 0, seed);
}
QPMCache::QPMCache()
@@ -253,25 +259,12 @@ bool QPMCache::flushDetachedPixmaps(bool nt)
{
auto mc = maxCost();
const qsizetype currentTotal = totalCost();
+ const qsizetype oldSize = size();
if (currentTotal)
setMaxCost(nt ? currentTotal * 3 / 4 : currentTotal - 1);
setMaxCost(mc);
ps = totalCost();
-
- bool any = false;
- QHash<QString, QPixmapCache::Key>::iterator it = cacheKeys.begin();
- while (it != cacheKeys.end()) {
- const auto value = it.value();
- if (value.isValid() && !contains(value)) {
- releaseKey(value);
- it = cacheKeys.erase(it);
- any = true;
- } else {
- ++it;
- }
- }
-
- return any;
+ return size() != oldSize;
}
void QPMCache::timerEvent(QTimerEvent *)
@@ -290,17 +283,9 @@ void QPMCache::timerEvent(QTimerEvent *)
QPixmap *QPMCache::object(const QString &key) const
{
- QPixmapCache::Key cacheKey = cacheKeys.value(key);
- if (!cacheKey.d || !cacheKey.d->isValid) {
- const_cast<QPMCache *>(this)->cacheKeys.remove(key);
- return nullptr;
- }
- QPixmap *ptr = QCache<QPixmapCache::Key, QPixmapCacheEntry>::object(cacheKey);
- //We didn't find the pixmap in the cache, the key is not valid anymore
- if (!ptr) {
- const_cast<QPMCache *>(this)->cacheKeys.remove(key);
- }
- return ptr;
+ if (const auto it = cacheKeys.find(key); it != cacheKeys.cend())
+ return object(it.value());
+ return nullptr;
}
QPixmap *QPMCache::object(const QPixmapCache::Key &key) const
@@ -315,31 +300,24 @@ QPixmap *QPMCache::object(const QPixmapCache::Key &key) const
bool QPMCache::insert(const QString& key, const QPixmap &pixmap, int cost)
{
- QPixmapCache::Key &cacheKey = cacheKeys[key];
//If for the same key we add already a pixmap we should delete it
- if (cacheKey.d)
- QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(cacheKey);
-
- //we create a new key the old one has been removed
- cacheKey = createKey();
+ remove(key);
- bool success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey, new QPixmapCacheEntry(cacheKey, pixmap), cost);
- if (success) {
- if (!theid) {
- theid = startTimer(flush_time);
- t = false;
- }
- } else {
- //Insertion failed we released the new allocated key
- cacheKeys.remove(key);
+ // this will create a new key; the old one has been removed
+ auto k = insert(pixmap, cost);
+ if (k.isValid()) {
+ k.d->stringKey = key;
+ cacheKeys[key] = std::move(k);
+ return true;
}
- return success;
+ return false;
}
QPixmapCache::Key QPMCache::insert(const QPixmap &pixmap, int cost)
{
- QPixmapCache::Key cacheKey = createKey();
+ QPixmapCache::Key cacheKey = createKey(); // invalidated by ~QPixmapCacheEntry on failed insert
bool success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey, new QPixmapCacheEntry(cacheKey, pixmap), cost);
+ Q_ASSERT(success || !cacheKey.isValid());
if (success) {
if (!theid) {
theid = startTimer(flush_time);
@@ -349,34 +327,10 @@ QPixmapCache::Key QPMCache::insert(const QPixmap &pixmap, int cost)
return cacheKey;
}
-bool QPMCache::replace(const QPixmapCache::Key &key, const QPixmap &pixmap, int cost)
-{
- Q_ASSERT(key.isValid());
- //If for the same key we had already an entry so we should delete the pixmap and use the new one
- QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(key);
-
- QPixmapCache::Key cacheKey = createKey();
-
- bool success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey, new QPixmapCacheEntry(cacheKey, pixmap), cost);
- if (success) {
- if (!theid) {
- theid = startTimer(flush_time);
- t = false;
- }
- const_cast<QPixmapCache::Key&>(key) = cacheKey;
- }
- return success;
-}
-
bool QPMCache::remove(const QString &key)
{
- auto cacheKey = cacheKeys.constFind(key);
- //The key was not in the cache
- if (cacheKey == cacheKeys.constEnd())
- return false;
- const bool result = QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(cacheKey.value());
- cacheKeys.erase(cacheKey);
- return result;
+ const auto cacheKey = cacheKeys.take(key);
+ return cacheKey.isValid() && remove(cacheKey);
}
bool QPMCache::remove(const QPixmapCache::Key &key)
@@ -410,7 +364,11 @@ QPixmapCache::Key QPMCache::createKey()
void QPMCache::releaseKey(const QPixmapCache::Key &key)
{
QPixmapCache::KeyData *keyData = key.d;
- if (!keyData || keyData->key > keyArraySize || keyData->key <= 0)
+ if (!keyData)
+ return;
+ if (!keyData->stringKey.isNull())
+ cacheKeys.remove(keyData->stringKey);
+ if (keyData->key > keyArraySize || keyData->key <= 0)
return;
keyData->key--;
keyArray[keyData->key] = freeKey;
@@ -471,7 +429,7 @@ QPixmapCacheEntry::~QPixmapCacheEntry()
bool QPixmapCache::find(const QString &key, QPixmap *pixmap)
{
- if (!qt_pixmapcache_thread_test())
+ if (key.isEmpty() || !qt_pixmapcache_thread_test())
return false;
QPixmap *ptr = pm_cache()->object(key);
if (ptr && pixmap)
@@ -523,7 +481,7 @@ bool QPixmapCache::find(const Key &key, QPixmap *pixmap)
bool QPixmapCache::insert(const QString &key, const QPixmap &pixmap)
{
- if (!qt_pixmapcache_thread_test())
+ if (key.isEmpty() || !qt_pixmapcache_thread_test())
return false;
return pm_cache()->insert(key, pixmap, cost(pixmap));
}
@@ -550,24 +508,25 @@ QPixmapCache::Key QPixmapCache::insert(const QPixmap &pixmap)
return pm_cache()->insert(pixmap, cost(pixmap));
}
+#if QT_DEPRECATED_SINCE(6, 6)
/*!
+ \fn bool QPixmapCache::replace(const Key &key, const QPixmap &pixmap)
+
+ \deprecated [6.6] Use \c{remove(key); key = insert(pixmap);} instead.
+
Replaces the pixmap associated with the given \a key with the \a pixmap
specified. Returns \c true if the \a pixmap has been correctly inserted into
the cache; otherwise returns \c false.
+ The passed \a key is updated to reference \a pixmap now. Other copies of \a
+ key, if any, still refer to the old pixmap, which is, however, removed from
+ the cache by this function.
+
\sa setCacheLimit(), insert()
\since 4.6
*/
-bool QPixmapCache::replace(const Key &key, const QPixmap &pixmap)
-{
- if (!qt_pixmapcache_thread_test())
- return false;
- //The key is not valid anymore, a flush happened before probably
- if (!key.d || !key.d->isValid)
- return false;
- return pm_cache()->replace(key, pixmap, cost(pixmap));
-}
+#endif // QT_DEPRECATED_SINCE(6, 6)
/*!
Returns the cache limit (in kilobytes).
@@ -604,7 +563,7 @@ void QPixmapCache::setCacheLimit(int n)
*/
void QPixmapCache::remove(const QString &key)
{
- if (!qt_pixmapcache_thread_test())
+ if (key.isEmpty() || !qt_pixmapcache_thread_test())
return;
pm_cache()->remove(key);
}
@@ -642,14 +601,14 @@ void QPixmapCache::clear()
}
}
-void QPixmapCache::flushDetachedPixmaps()
+Q_AUTOTEST_EXPORT void qt_qpixmapcache_flush_detached_pixmaps() // for tst_qpixmapcache
{
if (!qt_pixmapcache_thread_test())
return;
pm_cache()->flushDetachedPixmaps(true);
}
-int QPixmapCache::totalUsed()
+Q_AUTOTEST_EXPORT int qt_qpixmapcache_qpixmapcache_total_used() // for tst_qpixmapcache
{
if (!qt_pixmapcache_thread_test())
return 0;
diff --git a/src/gui/image/qpixmapcache.h b/src/gui/image/qpixmapcache.h
index 59bd89f3d7..72ee1b797f 100644
--- a/src/gui/image/qpixmapcache.h
+++ b/src/gui/image/qpixmapcache.h
@@ -31,6 +31,10 @@ public:
bool isValid() const noexcept;
private:
+ friend size_t qHash(const QPixmapCache::Key &k, size_t seed = 0) noexcept
+ { return k.hash(seed); }
+ size_t hash(size_t seed) const noexcept;
+
KeyData *d;
friend class QPMCache;
friend class QPixmapCache;
@@ -42,18 +46,30 @@ public:
static bool find(const Key &key, QPixmap *pixmap);
static bool insert(const QString &key, const QPixmap &pixmap);
static Key insert(const QPixmap &pixmap);
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_DEPRECATED_VERSION_X_6_6("Use remove(key), followed by key = insert(pixmap).")
+ QT_GUI_INLINE_SINCE(6, 6)
static bool replace(const Key &key, const QPixmap &pixmap);
+#endif
static void remove(const QString &key);
static void remove(const Key &key);
static void clear();
-
-#ifdef Q_TEST_QPIXMAPCACHE
- static void flushDetachedPixmaps();
- static int totalUsed();
-#endif
};
Q_DECLARE_SHARED(QPixmapCache::Key)
+#if QT_DEPRECATED_SINCE(6, 6)
+#if QT_GUI_INLINE_IMPL_SINCE(6, 6)
+bool QPixmapCache::replace(const Key &key, const QPixmap &pixmap)
+{
+ if (!key.isValid())
+ return false;
+ remove(key);
+ const_cast<Key&>(key) = insert(pixmap);
+ return key.isValid();
+}
+#endif // QT_GUI_INLINE_IMPL_SINCE(6, 6)
+#endif // QT_DEPRECATED_SINCE(6, 6)
+
QT_END_NAMESPACE
#endif // QPIXMAPCACHE_H
diff --git a/src/gui/image/qpixmapcache_p.h b/src/gui/image/qpixmapcache_p.h
index 6418296f56..43c4d9784c 100644
--- a/src/gui/image/qpixmapcache_p.h
+++ b/src/gui/image/qpixmapcache_p.h
@@ -23,8 +23,6 @@
QT_BEGIN_NAMESPACE
-size_t qHash(const QPixmapCache::Key &k, size_t seed = 0);
-
class QPixmapCache::KeyData
{
public:
@@ -33,6 +31,7 @@ public:
: isValid(other.isValid), key(other.key), ref(1) {}
~KeyData() {}
+ QString stringKey;
bool isValid;
int key;
int ref;
diff --git a/src/gui/image/qplatformpixmap.cpp b/src/gui/image/qplatformpixmap.cpp
index 2e622723ee..a297736095 100644
--- a/src/gui/image/qplatformpixmap.cpp
+++ b/src/gui/image/qplatformpixmap.cpp
@@ -65,10 +65,10 @@ QPlatformPixmap *QPlatformPixmap::createCompatiblePlatformPixmap() const
return d;
}
-static QImage makeBitmapCompliantIfNeeded(QPlatformPixmap *d, const QImage &image, Qt::ImageConversionFlags flags)
+static QImage makeBitmapCompliantIfNeeded(QPlatformPixmap *d, QImage image, Qt::ImageConversionFlags flags)
{
if (d->pixelType() == QPlatformPixmap::BitmapType) {
- QImage img = image.convertToFormat(QImage::Format_MonoLSB, flags);
+ QImage img = std::move(image).convertToFormat(QImage::Format_MonoLSB, flags);
// make sure image.color(0) == Qt::color0 (white)
// and image.color(1) == Qt::color1 (black)
@@ -98,7 +98,7 @@ bool QPlatformPixmap::fromFile(const QString &fileName, const char *format,
QImage image = QImageReader(fileName, format).read();
if (image.isNull())
return false;
- fromImage(makeBitmapCompliantIfNeeded(this, image, flags), flags);
+ fromImage(makeBitmapCompliantIfNeeded(this, std::move(image), flags), flags);
return !isNull();
}
@@ -110,7 +110,7 @@ bool QPlatformPixmap::fromData(const uchar *buf, uint len, const char *format, Q
QImage image = QImageReader(&b, format).read();
if (image.isNull())
return false;
- fromImage(makeBitmapCompliantIfNeeded(this, image, flags), flags);
+ fromImage(makeBitmapCompliantIfNeeded(this, std::move(image), flags), flags);
return !isNull();
}
@@ -132,9 +132,9 @@ QBitmap QPlatformPixmap::mask() const
if (!hasAlphaChannel())
return QBitmap();
- const QImage img = toImage();
+ QImage img = toImage();
bool shouldConvert = (img.format() != QImage::Format_ARGB32 && img.format() != QImage::Format_ARGB32_Premultiplied);
- const QImage image = (shouldConvert ? img.convertToFormat(QImage::Format_ARGB32_Premultiplied) : img);
+ const QImage image = (shouldConvert ? std::move(img).convertToFormat(QImage::Format_ARGB32_Premultiplied) : img);
const int w = image.width();
const int h = image.height();
@@ -160,7 +160,7 @@ QBitmap QPlatformPixmap::mask() const
}
}
- return QBitmap::fromImage(mask);
+ return QBitmap::fromImage(std::move(mask));
}
void QPlatformPixmap::setMask(const QBitmap &mask)
@@ -168,7 +168,7 @@ void QPlatformPixmap::setMask(const QBitmap &mask)
QImage image = toImage();
if (mask.size().isEmpty()) {
if (image.depth() != 1) { // hw: ????
- image = image.convertToFormat(QImage::Format_RGB32);
+ image = std::move(image).convertToFormat(QImage::Format_RGB32);
}
} else {
const int w = image.width();
@@ -188,7 +188,7 @@ void QPlatformPixmap::setMask(const QBitmap &mask)
}
default: {
const QImage imageMask = mask.toImage().convertToFormat(QImage::Format_MonoLSB);
- image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ image = std::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied);
for (int y = 0; y < h; ++y) {
const uchar *mscan = imageMask.scanLine(y);
QRgb *tscan = (QRgb *)image.scanLine(y);
diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp
index d8a7354e8f..615a36fa36 100644
--- a/src/gui/image/qpnghandler.cpp
+++ b/src/gui/image/qpnghandler.cpp
@@ -554,10 +554,10 @@ bool QPngHandlerPrivate::readPngHeader()
#endif
png_uint_32 profLen;
png_get_iCCP(png_ptr, info_ptr, &name, &compressionType, &profileData, &profLen);
- colorSpace = QColorSpace::fromIccProfile(QByteArray((const char *)profileData, profLen));
- if (!colorSpace.isValid()) {
- qCDebug(lcImageIo) << "QPngHandler: Failed to parse ICC profile";
- } else {
+ Q_UNUSED(name);
+ Q_UNUSED(compressionType);
+ if (profLen > 0) {
+ colorSpace = QColorSpace::fromIccProfile(QByteArray((const char *)profileData, profLen));
QColorSpacePrivate *csD = QColorSpacePrivate::get(colorSpace);
if (csD->description.isEmpty())
csD->description = QString::fromLatin1((const char *)name);
@@ -798,7 +798,7 @@ static void set_text(const QImage &image, png_structp png_ptr, png_infop info_pt
int i = 0;
while (it != text.constEnd()) {
text_ptr[i].key = qstrdup(QStringView{it.key()}.left(79).toLatin1().constData());
- bool noCompress = (it.value().length() < 40);
+ bool noCompress = (it.value().size() < 40);
#ifdef PNG_iTXt_SUPPORTED
bool needsItxt = false;
@@ -926,15 +926,15 @@ bool QPNGImageWriter::writeImage(const QImage& image, int compression_in, const
color_type, 0, 0, 0); // sets #channels
#ifdef PNG_iCCP_SUPPORTED
- if (image.colorSpace().isValid()) {
- QColorSpace cs = image.colorSpace();
- // Support the old gamma making it override transferfunction.
- if (gamma != 0.0 && !qFuzzyCompare(cs.gamma(), 1.0f / gamma))
- cs = cs.withTransferFunction(QColorSpace::TransferFunction::Gamma, 1.0f / gamma);
+ QColorSpace cs = image.colorSpace();
+ // Support the old gamma making it override transferfunction (if possible)
+ if (cs.isValid() && gamma != 0.0 && !qFuzzyCompare(cs.gamma(), 1.0f / gamma))
+ cs = cs.withTransferFunction(QColorSpace::TransferFunction::Gamma, 1.0f / gamma);
+ QByteArray iccProfile = cs.iccProfile();
+ if (!iccProfile.isEmpty()) {
QByteArray iccProfileName = cs.description().toLatin1();
if (iccProfileName.isEmpty())
iccProfileName = QByteArrayLiteral("Custom");
- QByteArray iccProfile = cs.iccProfile();
png_set_iCCP(png_ptr, info_ptr,
#if PNG_LIBPNG_VER < 10500
iccProfileName.data(), PNG_COMPRESSION_TYPE_BASE, iccProfile.data(),
@@ -942,7 +942,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, int compression_in, const
iccProfileName.constData(), PNG_COMPRESSION_TYPE_BASE,
(png_const_bytep)iccProfile.constData(),
#endif
- iccProfile.length());
+ iccProfile.size());
} else
#endif
if (gamma != 0.0) {
diff --git a/src/gui/image/qppmhandler.cpp b/src/gui/image/qppmhandler.cpp
index c809b341f8..3a4af46195 100644
--- a/src/gui/image/qppmhandler.cpp
+++ b/src/gui/image/qppmhandler.cpp
@@ -11,8 +11,8 @@
#include <qloggingcategory.h>
#include <qrgba64.h>
#include <qvariant.h>
-
-#include <ctype.h>
+#include <private/qlocale_p.h>
+#include <private/qtools_p.h>
QT_BEGIN_NAMESPACE
@@ -32,7 +32,7 @@ static void discard_pbm_line(QIODevice *d)
} while (res > 0 && buf[res-1] != '\n');
}
-static int read_pbm_int(QIODevice *d, bool *ok)
+static int read_pbm_int(QIODevice *d, bool *ok, int maxDigits = -1)
{
char c;
int val = -1;
@@ -41,7 +41,7 @@ static int read_pbm_int(QIODevice *d, bool *ok)
for (;;) {
if (!d->getChar(&c)) // end of file
break;
- digit = isdigit((uchar) c);
+ digit = QtMiscUtils::isAsciiDigit(c);
if (val != -1) {
if (digit) {
const int cValue = c - '0';
@@ -50,6 +50,8 @@ static int read_pbm_int(QIODevice *d, bool *ok)
} else {
hasOverflow = true;
}
+ if (maxDigits > 0 && --maxDigits == 0)
+ break;
continue;
} else {
if (c == '#') // comment
@@ -59,12 +61,14 @@ static int read_pbm_int(QIODevice *d, bool *ok)
}
if (digit) // first digit
val = c - '0';
- else if (isspace((uchar) c))
+ else if (ascii_isspace(c))
continue;
else if (c == '#')
discard_pbm_line(d);
else
break;
+ if (maxDigits > 0 && --maxDigits == 0)
+ break;
}
if (val < 0)
*ok = false;
@@ -77,7 +81,7 @@ static bool read_pbm_header(QIODevice *device, char& type, int& w, int& h, int&
if (device->read(buf, 3) != 3) // read P[1-6]<white-space>
return false;
- if (!(buf[0] == 'P' && isdigit((uchar) buf[1]) && isspace((uchar) buf[2])))
+ if (!(buf[0] == 'P' && QtMiscUtils::isAsciiDigit(buf[1]) && ascii_isspace(buf[2])))
return false;
type = buf[1];
@@ -213,7 +217,7 @@ static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, Q
b = 0;
for (int i=0; i<8; i++) {
if (i < bitsLeft)
- b = (b << 1) | (read_pbm_int(device, &ok) & 1);
+ b = (b << 1) | (read_pbm_int(device, &ok, 1) & 1);
else
b = (b << 1) | (0 & 1); // pad it our self if we need to
}
@@ -265,13 +269,12 @@ static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, Q
return true;
}
-static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, const QByteArray &sourceFormat)
+static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, QByteArrayView sourceFormat)
{
QByteArray str;
QImage image = sourceImage;
- QByteArray format = sourceFormat;
+ const QByteArrayView format = sourceFormat.left(3); // ignore RAW part
- format = format.left(3); // ignore RAW part
bool gray = format == "pgm";
if (format == "pbm") {
@@ -322,7 +325,7 @@ static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, const QBy
switch (image.depth()) {
case 1: {
str.insert(1, '4');
- if (out->write(str, str.length()) != str.length())
+ if (out->write(str, str.size()) != str.size())
return false;
w = (w+7)/8;
for (uint y=0; y<h; y++) {
@@ -336,12 +339,12 @@ static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, const QBy
case 8: {
str.insert(1, gray ? '5' : '6');
str.append("255\n");
- if (out->write(str, str.length()) != str.length())
+ if (out->write(str, str.size()) != str.size())
return false;
qsizetype bpl = qsizetype(w) * (gray ? 1 : 3);
uchar *buf = new uchar[bpl];
if (image.format() == QImage::Format_Indexed8) {
- QList<QRgb> color = image.colorTable();
+ const QList<QRgb> color = image.colorTable();
for (uint y=0; y<h; y++) {
const uchar *b = image.constScanLine(y);
uchar *p = buf;
@@ -389,7 +392,7 @@ static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, const QBy
case 32: {
str.insert(1, '6');
str.append("255\n");
- if (out->write(str, str.length()) != str.length())
+ if (out->write(str, str.size()) != str.size())
return false;
qsizetype bpl = qsizetype(w) * 3;
uchar *buf = new uchar[bpl];
diff --git a/src/gui/image/qxbmhandler.cpp b/src/gui/image/qxbmhandler.cpp
index c3ab5fbb1d..8206fe3b29 100644
--- a/src/gui/image/qxbmhandler.cpp
+++ b/src/gui/image/qxbmhandler.cpp
@@ -10,12 +10,14 @@
#include <qiodevice.h>
#include <qloggingcategory.h>
#include <qvariant.h>
+#include <private/qtools_p.h>
#include <stdio.h>
-#include <ctype.h>
QT_BEGIN_NAMESPACE
+using namespace QtMiscUtils;
+
Q_DECLARE_LOGGING_CATEGORY(lcImageIo)
/*****************************************************************************
@@ -24,8 +26,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcImageIo)
static inline int hex2byte(char *p)
{
- return ((isdigit((uchar) *p) ? *p - '0' : toupper((uchar) *p) - 'A' + 10) << 4) |
- (isdigit((uchar) *(p+1)) ? *(p+1) - '0' : toupper((uchar) *(p+1)) - 'A' + 10);
+ return QtMiscUtils::fromHex(p[0]) * 16 | QtMiscUtils::fromHex(p[1]);
}
static bool read_xbm_header(QIODevice *device, int& w, int& h)
@@ -55,11 +56,9 @@ static bool read_xbm_header(QIODevice *device, int& w, int& h)
}
auto parseDefine = [] (const char *buf, int len) -> int {
- auto isAsciiLetterOrNumber = [] (char ch) -> bool {
- return (ch >= '0' && ch <= '9') ||
- (ch >= 'A' && ch <= 'Z') ||
- (ch >= 'a' && ch <= 'z') ||
- ch == '_' || ch == '.';
+ auto checkChar = [] (char ch) -> bool {
+ return isAsciiLetterOrNumber(ch)
+ || ch == '_' || ch == '.';
};
auto isAsciiSpace = [] (char ch) -> bool {
return ch == ' ' || ch == '\t';
@@ -71,7 +70,7 @@ static bool read_xbm_header(QIODevice *device, int& w, int& h)
int index = defineLen;
while (buf[index] && isAsciiSpace(buf[index]))
++index;
- while (buf[index] && isAsciiLetterOrNumber(buf[index]))
+ while (buf[index] && checkChar(buf[index]))
++index;
while (buf[index] && isAsciiSpace(buf[index]))
++index;
@@ -129,9 +128,10 @@ static bool read_xbm_body(QIODevice *device, int w, int h, QImage *outImage)
while (y < h) { // for all encoded bytes...
if (p && p < (buf + readBytes - 3)) { // p = "0x.."
- if (!isxdigit(p[2]) || !isxdigit(p[3]))
+ const int byte = hex2byte(p + 2);
+ if (byte < 0) // non-hex char encountered
return false;
- *b++ = hex2byte(p+2);
+ *b++ = byte;
p += 2;
if (++x == w && ++y < h) {
b = outImage->scanLine(y);
@@ -164,7 +164,7 @@ static bool write_xbm_image(const QImage &sourceImage, QIODevice *device, const
int h = image.height();
int i;
QString s = fileName; // get file base name
- int msize = s.length() + 100;
+ int msize = s.size() + 100;
char *buf = new char[msize];
qsnprintf(buf, msize, "#define %s_width %d\n", s.toUtf8().data(), w);
diff --git a/src/gui/image/qxpmhandler.cpp b/src/gui/image/qxpmhandler.cpp
index aabee7fcd7..50f9f035a6 100644
--- a/src/gui/image/qxpmhandler.cpp
+++ b/src/gui/image/qxpmhandler.cpp
@@ -15,12 +15,15 @@
#include <private/qcolor_p.h>
#include <private/qduplicatetracker_p.h> // for easier std::pmr detection
+#include <private/qtools_p.h>
#include <algorithm>
#include <array>
QT_BEGIN_NAMESPACE
+using namespace QtMiscUtils;
+
Q_DECLARE_LOGGING_CATEGORY(lcImageIo)
static quint64 xpmHash(const QString &str)
@@ -736,15 +739,13 @@ static QString fbname(const QString &fileName) // get file basename (sort of)
int i = qMax(s.lastIndexOf(u'/'), s.lastIndexOf(u'\\'));
if (i < 0)
i = 0;
- auto isAsciiLetterOrNumber = [](QChar ch) -> bool {
- return (ch.unicode() >= '0' && ch.unicode() <= '9') ||
- (ch.unicode() >= 'A' && ch.unicode() <= 'Z') ||
- (ch.unicode() >= 'a' && ch.unicode() <= 'z') ||
- ch.unicode() == '_';
+ auto checkChar = [](QChar ch) -> bool {
+ uchar uc = ch.unicode();
+ return isAsciiLetterOrNumber(uc) || uc == '_';
};
int start = -1;
- for (; i < s.length(); ++i) {
- if (isAsciiLetterOrNumber(s.at(i))) {
+ for (; i < s.size(); ++i) {
+ if (checkChar(s.at(i))) {
start = i;
} else if (start > 0)
break;
@@ -898,8 +899,8 @@ static bool read_xpm_body(
}
} else {
QRgb c_rgb = 0;
- if (((buf.length()-1) % 3) && (buf[0] == '#')) {
- buf.truncate(((buf.length()-1) / 4 * 3) + 1); // remove alpha channel left by imagemagick
+ if (((buf.size()-1) % 3) && (buf[0] == '#')) {
+ buf.truncate(((buf.size()-1) / 4 * 3) + 1); // remove alpha channel left by imagemagick
}
if (buf[0] == '#') {
c_rgb = qt_get_hex_rgb(buf).value_or(0);
@@ -932,7 +933,7 @@ static bool read_xpm_body(
if (image.depth() == 8) {
uchar *p = image.scanLine(y);
uchar *d = (uchar *)buf.data();
- uchar *end = d + buf.length();
+ uchar *end = d + buf.size();
int x;
if (cpp == 1) {
char b[2];
@@ -958,7 +959,7 @@ static bool read_xpm_body(
} else {
QRgb *p = (QRgb*)image.scanLine(y);
uchar *d = (uchar *)buf.data();
- uchar *end = d + buf.length();
+ uchar *end = d + buf.size();
int x;
char b[16];
b[cpp] = '\0';
diff --git a/src/gui/itemmodels/qfileinfogatherer.cpp b/src/gui/itemmodels/qfileinfogatherer.cpp
index 10c36ea8dc..41fb0a0db5 100644
--- a/src/gui/itemmodels/qfileinfogatherer.cpp
+++ b/src/gui/itemmodels/qfileinfogatherer.cpp
@@ -2,8 +2,10 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qfileinfogatherer_p.h"
+#include <qcoreapplication.h>
#include <qdebug.h>
-#include <qdiriterator.h>
+#include <qdirlisting.h>
+#include <private/qabstractfileiconprovider_p.h>
#include <private/qfileinfo_p.h>
#ifndef Q_OS_WIN
# include <unistd.h>
@@ -18,7 +20,7 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
#ifdef QT_BUILD_INTERNAL
-static QBasicAtomicInt fetchedRoot = Q_BASIC_ATOMIC_INITIALIZER(false);
+Q_CONSTINIT static QBasicAtomicInt fetchedRoot = Q_BASIC_ATOMIC_INITIALIZER(false);
Q_AUTOTEST_EXPORT void qt_test_resetFetchedRoot()
{
fetchedRoot.storeRelaxed(false);
@@ -57,11 +59,40 @@ QFileInfoGatherer::QFileInfoGatherer(QObject *parent)
*/
QFileInfoGatherer::~QFileInfoGatherer()
{
- abort.storeRelaxed(true);
+ requestAbort();
+ wait();
+}
+
+bool QFileInfoGatherer::event(QEvent *event)
+{
+ if (event->type() == QEvent::DeferredDelete && isRunning()) {
+ // We have been asked to shut down later but were blocked,
+ // so the owning QFileSystemModel proceeded with its shut-down
+ // and deferred the destruction of the gatherer.
+ // If we are still blocked now, then we have three bad options:
+ // terminate, wait forever (preventing the process from shutting down),
+ // or accept a memory leak.
+ requestAbort();
+ if (!wait(5000)) {
+ // If the application is shutting down, then we terminate.
+ // Otherwise assume that sooner or later the thread will finish,
+ // and we delete it then.
+ if (QCoreApplication::closingDown())
+ terminate();
+ else
+ connect(this, &QThread::finished, this, [this]{ delete this; });
+ return true;
+ }
+ }
+
+ return QThread::event(event);
+}
+
+void QFileInfoGatherer::requestAbort()
+{
+ requestInterruption();
QMutexLocker locker(&mutex);
condition.wakeAll();
- locker.unlock();
- wait();
}
void QFileInfoGatherer::setResolveSymlinks(bool enable)
@@ -83,7 +114,7 @@ void QFileInfoGatherer::driveRemoved()
const QFileInfoList driveInfoList = QDir::drives();
for (const QFileInfo &fi : driveInfoList)
drives.append(translateDriveName(fi));
- newListOfFiles(QString(), drives);
+ emit newListOfFiles(QString(), drives);
}
bool QFileInfoGatherer::resolveSymlinks() const
@@ -114,16 +145,19 @@ void QFileInfoGatherer::fetchExtendedInformation(const QString &path, const QStr
{
QMutexLocker locker(&mutex);
// See if we already have this dir/file in our queue
- int loc = this->path.lastIndexOf(path);
- while (loc > 0) {
- if (this->files.at(loc) == files) {
+ qsizetype loc = 0;
+ while ((loc = this->path.lastIndexOf(path, loc - 1)) != -1) {
+ if (this->files.at(loc) == files)
return;
- }
- loc = this->path.lastIndexOf(path, loc - 1);
}
+
+#if QT_CONFIG(thread)
this->path.push(path);
this->files.push(files);
condition.wakeAll();
+#else // !QT_CONFIG(thread)
+ getFileInfos(path, files);
+#endif // QT_CONFIG(thread)
#if QT_CONFIG(filesystemwatcher)
if (files.isEmpty()
@@ -143,7 +177,7 @@ void QFileInfoGatherer::fetchExtendedInformation(const QString &path, const QStr
void QFileInfoGatherer::updateFile(const QString &filePath)
{
QString dir = filePath.mid(0, filePath.lastIndexOf(u'/'));
- QString fileName = filePath.mid(dir.length() + 1);
+ QString fileName = filePath.mid(dir.size() + 1);
fetchExtendedInformation(dir, QStringList(fileName));
}
@@ -216,16 +250,23 @@ bool QFileInfoGatherer::isWatching() const
return result;
}
+/*! \internal
+
+ If \a v is \c false, the QFileSystemWatcher used internally will be deleted
+ and subsequent calls to watchPaths() will do nothing.
+
+ If \a v is \c true, subsequent calls to watchPaths() will add those paths to
+ the filesystem watcher; watchPaths() will initialize a QFileSystemWatcher if
+ one hasn't already been initialized.
+*/
void QFileInfoGatherer::setWatching(bool v)
{
#if QT_CONFIG(filesystemwatcher)
QMutexLocker locker(&mutex);
if (v != m_watching) {
- if (!v) {
- delete m_watcher;
- m_watcher = nullptr;
- }
m_watching = v;
+ if (!m_watching)
+ delete std::exchange(m_watcher, nullptr);
}
#else
Q_UNUSED(v);
@@ -277,17 +318,24 @@ void QFileInfoGatherer::list(const QString &directoryPath)
void QFileInfoGatherer::run()
{
forever {
+ // Disallow termination while we are holding a mutex or can be
+ // woken up cleanly.
+ setTerminationEnabled(false);
QMutexLocker locker(&mutex);
- while (!abort.loadRelaxed() && path.isEmpty())
+ while (!isInterruptionRequested() && path.isEmpty())
condition.wait(&mutex);
- if (abort.loadRelaxed())
+ if (isInterruptionRequested())
return;
- const QString thisPath = qAsConst(path).front();
+ const QString thisPath = std::as_const(path).front();
path.pop_front();
- const QStringList thisList = qAsConst(files).front();
+ const QStringList thisList = std::as_const(files).front();
files.pop_front();
locker.unlock();
+ // Some of the system APIs we call when gathering file infomration
+ // might hang (e.g. waiting for network), so we explicitly allow
+ // termination now.
+ setTerminationEnabled(true);
getFileInfos(thisPath, thisList);
}
}
@@ -295,8 +343,12 @@ void QFileInfoGatherer::run()
QExtendedInformation QFileInfoGatherer::getInfo(const QFileInfo &fileInfo) const
{
QExtendedInformation info(fileInfo);
- info.icon = m_iconProvider->icon(fileInfo);
- info.displayType = m_iconProvider->type(fileInfo);
+ if (m_iconProvider) {
+ info.icon = m_iconProvider->icon(fileInfo);
+ info.displayType = m_iconProvider->type(fileInfo);
+ } else {
+ info.displayType = QAbstractFileIconProviderPrivate::getFileType(fileInfo);
+ }
#if QT_CONFIG(filesystemwatcher)
// ### Not ready to listen all modifications by default
static const bool watchFiles = qEnvironmentVariableIsSet("QT_FILESYSTEMMODEL_WATCH_FILES");
@@ -336,21 +388,23 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil
#ifdef QT_BUILD_INTERNAL
fetchedRoot.storeRelaxed(true);
#endif
- QFileInfoList infoList;
+ QList<std::pair<QString, QFileInfo>> updatedFiles;
+ auto addToUpdatedFiles = [&updatedFiles](QFileInfo &&fileInfo) {
+ fileInfo.stat();
+ updatedFiles.emplace_back(std::pair{translateDriveName(fileInfo), fileInfo});
+ };
+
if (files.isEmpty()) {
- infoList = QDir::drives();
+ // QDir::drives() calls QFSFileEngine::drives() which creates the QFileInfoList on
+ // the stack and return it, so this list is not shared, so no detaching.
+ QFileInfoList infoList = QDir::drives();
+ updatedFiles.reserve(infoList.size());
+ for (auto rit = infoList.rbegin(), rend = infoList.rend(); rit != rend; ++rit)
+ addToUpdatedFiles(std::move(*rit));
} else {
- infoList.reserve(files.count());
- for (const auto &file : files)
- infoList << QFileInfo(file);
- }
- QList<QPair<QString, QFileInfo>> updatedFiles;
- updatedFiles.reserve(infoList.count());
- for (int i = infoList.count() - 1; i >= 0; --i) {
- QFileInfo driveInfo = infoList.at(i);
- driveInfo.stat();
- QString driveName = translateDriveName(driveInfo);
- updatedFiles.append(QPair<QString,QFileInfo>(driveName, driveInfo));
+ updatedFiles.reserve(files.size());
+ for (auto rit = files.crbegin(), rend = files.crend(); rit != rend; ++rit)
+ addToUpdatedFiles(QFileInfo(*rit));
}
emit updates(path, updatedFiles);
return;
@@ -360,14 +414,16 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil
base.start();
QFileInfo fileInfo;
bool firstTime = true;
- QList<QPair<QString, QFileInfo>> updatedFiles;
+ QList<std::pair<QString, QFileInfo>> updatedFiles;
QStringList filesToCheck = files;
QStringList allFiles;
if (files.isEmpty()) {
- QDirIterator dirIt(path, QDir::AllEntries | QDir::System | QDir::Hidden);
- while (!abort.loadRelaxed() && dirIt.hasNext()) {
- fileInfo = dirIt.nextFileInfo();
+ constexpr auto dirFilters = QDir::AllEntries | QDir::System | QDir::Hidden;
+ for (const auto &dirEntry : QDirListing(path, dirFilters)) {
+ if (isInterruptionRequested())
+ break;
+ fileInfo = dirEntry.fileInfo();
fileInfo.stat();
allFiles.append(fileInfo.fileName());
fetch(fileInfo, base, firstTime, updatedFiles, path);
@@ -377,7 +433,7 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil
emit newListOfFiles(path, allFiles);
QStringList::const_iterator filesIt = filesToCheck.constBegin();
- while (!abort.loadRelaxed() && filesIt != filesToCheck.constEnd()) {
+ while (!isInterruptionRequested() && filesIt != filesToCheck.constEnd()) {
fileInfo.setFile(path + QDir::separator() + *filesIt);
++filesIt;
fileInfo.stat();
@@ -389,12 +445,12 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil
}
void QFileInfoGatherer::fetch(const QFileInfo &fileInfo, QElapsedTimer &base, bool &firstTime,
- QList<QPair<QString, QFileInfo>> &updatedFiles, const QString &path)
+ QList<std::pair<QString, QFileInfo>> &updatedFiles, const QString &path)
{
- updatedFiles.append(QPair<QString, QFileInfo>(fileInfo.fileName(), fileInfo));
+ updatedFiles.emplace_back(std::pair(fileInfo.fileName(), fileInfo));
QElapsedTimer current;
current.start();
- if ((firstTime && updatedFiles.count() > 100) || base.msecsTo(current) > 1000) {
+ if ((firstTime && updatedFiles.size() > 100) || base.msecsTo(current) > 1000) {
emit updates(path, updatedFiles);
updatedFiles.clear();
base = current;
diff --git a/src/gui/itemmodels/qfileinfogatherer_p.h b/src/gui/itemmodels/qfileinfogatherer_p.h
index c8a56a333f..3d5f59c22e 100644
--- a/src/gui/itemmodels/qfileinfogatherer_p.h
+++ b/src/gui/itemmodels/qfileinfogatherer_p.h
@@ -24,7 +24,6 @@
#include <qfilesystemwatcher.h>
#endif
#include <qabstractfileiconprovider.h>
-#include <qpair.h>
#include <qstack.h>
#include <qdatetime.h>
#include <qdir.h>
@@ -32,6 +31,8 @@
#include <private/qfilesystemengine_p.h>
+#include <utility>
+
QT_REQUIRE_CONFIG(filesystemmodel);
QT_BEGIN_NAMESPACE
@@ -51,7 +52,7 @@ public:
return mFileInfo == fileInfo.mFileInfo
&& displayType == fileInfo.displayType
&& permissions() == fileInfo.permissions()
- && lastModified() == fileInfo.lastModified();
+ && lastModified(QTimeZone::UTC) == fileInfo.lastModified(QTimeZone::UTC);
}
#ifndef QT_NO_FSFILEENGINE
@@ -95,8 +96,8 @@ public:
return mFileInfo;
}
- QDateTime lastModified() const {
- return mFileInfo.lastModified();
+ QDateTime lastModified(const QTimeZone &tz) const {
+ return mFileInfo.lastModified(tz);
}
qint64 size() const {
@@ -124,7 +125,7 @@ class Q_GUI_EXPORT QFileInfoGatherer : public QThread
Q_OBJECT
Q_SIGNALS:
- void updates(const QString &directory, const QList<QPair<QString, QFileInfo>> &updates);
+ void updates(const QString &directory, const QList<std::pair<QString, QFileInfo>> &updates);
void newListOfFiles(const QString &directory, const QStringList &listOfFiles) const;
void nameResolved(const QString &fileName, const QString &resolvedName) const;
void directoryLoaded(const QString &path);
@@ -148,6 +149,8 @@ public:
QAbstractFileIconProvider *iconProvider() const;
bool resolveSymlinks() const;
+ void requestAbort();
+
public Q_SLOTS:
void list(const QString &directoryPath);
void fetchExtendedInformation(const QString &path, const QStringList &files);
@@ -159,12 +162,15 @@ private Q_SLOTS:
void driveAdded();
void driveRemoved();
+protected:
+ bool event(QEvent *event) override;
+
private:
void run() override;
// called by run():
void getFileInfos(const QString &path, const QStringList &files);
void fetch(const QFileInfo &info, QElapsedTimer &base, bool &firstTime,
- QList<QPair<QString, QFileInfo>> &updatedFiles, const QString &path);
+ QList<std::pair<QString, QFileInfo>> &updatedFiles, const QString &path);
private:
void createWatcher();
@@ -175,7 +181,6 @@ private:
QStack<QString> path;
QStack<QStringList> files;
// end protected by mutex
- QAtomicInt abort;
#if QT_CONFIG(filesystemwatcher)
QFileSystemWatcher *m_watcher = nullptr;
diff --git a/src/gui/itemmodels/qfilesystemmodel.cpp b/src/gui/itemmodels/qfilesystemmodel.cpp
index f0dcb6d801..290891322f 100644
--- a/src/gui/itemmodels/qfilesystemmodel.cpp
+++ b/src/gui/itemmodels/qfilesystemmodel.cpp
@@ -31,6 +31,7 @@ using namespace Qt::StringLiterals;
\value FilePathRole
\value FileNameRole
\value FilePermissions
+ \value FileInfoRole The QFileInfo object for the index
*/
/*!
@@ -143,7 +144,7 @@ QFileInfo QFileSystemModel::fileInfo(const QModelIndex &index) const
\fn void QFileSystemModel::fileRenamed(const QString &path, const QString &oldName, const QString &newName)
This signal is emitted whenever a file with the \a oldName is successfully
- renamed to \a newName. The file is located in in the directory \a path.
+ renamed to \a newName. The file is located in the directory \a path.
*/
/*!
@@ -240,7 +241,7 @@ QModelIndex QFileSystemModel::index(int row, int column, const QModelIndex &pare
*/
QModelIndex QFileSystemModel::sibling(int row, int column, const QModelIndex &idx) const
{
- if (row == idx.row() && column < QFileSystemModelPrivate::NumColumns) {
+ if (row == idx.row() && column < columnCount(idx.parent())) {
// cheap sibling operation: just adjust the column:
return createIndex(row, column, idx.internalPointer());
} else {
@@ -394,12 +395,12 @@ QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QS
QFileSystemModelPrivate::QFileSystemNode *parent = node(index);
- for (int i = 0; i < pathElements.count(); ++i) {
+ for (int i = 0; i < pathElements.size(); ++i) {
QString element = pathElements.at(i);
if (i != 0)
elementPath.append(separator);
elementPath.append(element);
- if (i == pathElements.count() - 1)
+ if (i == pathElements.size() - 1)
elementPath.append(trailingSeparator);
#ifdef Q_OS_WIN
// On Windows, "filename " and "filename" are equivalent and
@@ -420,7 +421,7 @@ QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QS
// we couldn't find the path element, we create a new node since we
// _know_ that the path is valid
if (alreadyExisted) {
- if ((parent->children.count() == 0)
+ if ((parent->children.size() == 0)
|| (parent->caseSensitive()
&& parent->children.value(element)->fileName != element)
|| (!parent->caseSensitive()
@@ -438,7 +439,7 @@ QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QS
QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this);
node = p->addNode(parent, element,info);
#if QT_CONFIG(filesystemwatcher)
- node->populate(fileInfoGatherer.getInfo(info));
+ node->populate(fileInfoGatherer->getInfo(info));
#endif
} else {
node = parent->children.value(element);
@@ -476,10 +477,10 @@ void QFileSystemModel::timerEvent(QTimerEvent *event)
if (event->timerId() == d->fetchingTimer.timerId()) {
d->fetchingTimer.stop();
#if QT_CONFIG(filesystemwatcher)
- for (int i = 0; i < d->toFetch.count(); ++i) {
+ for (int i = 0; i < d->toFetch.size(); ++i) {
const QFileSystemModelPrivate::QFileSystemNode *node = d->toFetch.at(i).node;
if (!node->hasInformation()) {
- d->fileInfoGatherer.fetchExtendedInformation(d->toFetch.at(i).dir,
+ d->fileInfoGatherer->fetchExtendedInformation(d->toFetch.at(i).dir,
QStringList(d->toFetch.at(i).file));
} else {
// qDebug("yah!, you saved a little gerbil soul");
@@ -529,14 +530,38 @@ QString QFileSystemModel::type(const QModelIndex &index) const
}
/*!
- Returns the date and time when \a index was last modified.
+ Returns the date and time (in local time) when \a index was last modified.
+
+ This is an overloaded function, equivalent to calling:
+ \code
+ lastModified(index, QTimeZone::LocalTime);
+ \endcode
+
+ If \a index is invalid, a default constructed QDateTime is returned.
*/
QDateTime QFileSystemModel::lastModified(const QModelIndex &index) const
{
+ return lastModified(index, QTimeZone::LocalTime);
+}
+
+/*!
+ \since 6.6
+ Returns the date and time, in the time zone \a tz, when
+ \a index was last modified.
+
+ Typical arguments for \a tz are \c QTimeZone::UTC or \c QTimeZone::LocalTime.
+ UTC does not require any conversion from the time returned by the native file
+ system API, therefore getting the time in UTC is potentially faster. LocalTime
+ is typically chosen if the time is shown to the user.
+
+ If \a index is invalid, a default constructed QDateTime is returned.
+ */
+QDateTime QFileSystemModel::lastModified(const QModelIndex &index, const QTimeZone &tz) const
+{
Q_D(const QFileSystemModel);
if (!index.isValid())
return QDateTime();
- return d->node(index)->lastModified();
+ return d->node(index)->lastModified(tz);
}
/*!
@@ -626,7 +651,7 @@ void QFileSystemModel::fetchMore(const QModelIndex &parent)
return;
indexNode->populatedChildren = true;
#if QT_CONFIG(filesystemwatcher)
- d->fileInfoGatherer.list(filePath(parent));
+ d->fileInfoGatherer->list(filePath(parent));
#endif
}
@@ -640,10 +665,10 @@ int QFileSystemModel::rowCount(const QModelIndex &parent) const
return 0;
if (!parent.isValid())
- return d->root.visibleChildren.count();
+ return d->root.visibleChildren.size();
const QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent);
- return parentNode->visibleChildren.count();
+ return parentNode->visibleChildren.size();
}
/*!
@@ -669,7 +694,9 @@ QVariant QFileSystemModel::myComputer(int role) const
return QFileSystemModelPrivate::myComputer();
#if QT_CONFIG(filesystemwatcher)
case Qt::DecorationRole:
- return d->fileInfoGatherer.iconProvider()->icon(QAbstractFileIconProvider::Computer);
+ if (auto *provider = d->fileInfoGatherer->iconProvider())
+ return provider->icon(QAbstractFileIconProvider::Computer);
+ break;
#endif
}
return QVariant();
@@ -686,15 +713,15 @@ QVariant QFileSystemModel::data(const QModelIndex &index, int role) const
switch (role) {
case Qt::EditRole:
- if (index.column() == 0)
+ if (index.column() == QFileSystemModelPrivate::NameColumn)
return d->name(index);
Q_FALLTHROUGH();
case Qt::DisplayRole:
switch (index.column()) {
- case 0: return d->displayName(index);
- case 1: return d->size(index);
- case 2: return d->type(index);
- case 3: return d->time(index);
+ case QFileSystemModelPrivate::NameColumn: return d->displayName(index);
+ case QFileSystemModelPrivate::SizeColumn: return d->size(index);
+ case QFileSystemModelPrivate::TypeColumn: return d->type(index);
+ case QFileSystemModelPrivate::TimeColumn: return d->time(index);
default:
qWarning("data: invalid display value column %d", index.column());
break;
@@ -704,22 +731,23 @@ QVariant QFileSystemModel::data(const QModelIndex &index, int role) const
return filePath(index);
case FileNameRole:
return d->name(index);
+ case FileInfoRole:
+ return QVariant::fromValue(fileInfo(index));
case Qt::DecorationRole:
- if (index.column() == 0) {
+ if (index.column() == QFileSystemModelPrivate::NameColumn) {
QIcon icon = d->icon(index);
#if QT_CONFIG(filesystemwatcher)
if (icon.isNull()) {
- if (d->node(index)->isDir())
- icon = d->fileInfoGatherer.iconProvider()->icon(QAbstractFileIconProvider::Folder);
- else
- icon = d->fileInfoGatherer.iconProvider()->icon(QAbstractFileIconProvider::File);
+ using P = QAbstractFileIconProvider;
+ if (auto *provider = d->fileInfoGatherer->iconProvider())
+ icon = provider->icon(d->node(index)->isDir() ? P::Folder: P::File);
}
#endif // filesystemwatcher
return icon;
}
break;
case Qt::TextAlignmentRole:
- if (index.column() == 1)
+ if (index.column() == QFileSystemModelPrivate::SizeColumn)
return QVariant(Qt::AlignTrailing | Qt::AlignVCenter);
break;
case FilePermissions:
@@ -765,7 +793,7 @@ QString QFileSystemModelPrivate::time(const QModelIndex &index) const
if (!index.isValid())
return QString();
#if QT_CONFIG(datestring)
- return QLocale::system().toString(node(index)->lastModified(), QLocale::ShortFormat);
+ return QLocale::system().toString(node(index)->lastModified(QTimeZone::LocalTime), QLocale::ShortFormat);
#else
Q_UNUSED(index);
return QString();
@@ -792,7 +820,7 @@ QString QFileSystemModelPrivate::name(const QModelIndex &index) const
QFileSystemNode *dirNode = node(index);
if (
#if QT_CONFIG(filesystemwatcher)
- fileInfoGatherer.resolveSymlinks() &&
+ fileInfoGatherer->resolveSymlinks() &&
#endif
!resolvedSymLinks.isEmpty() && dirNode->isSymLink(/* ignoreNtfsSymLinks = */ true)) {
QString fullPath = QDir::fromNativeSeparators(filePath(index));
@@ -877,7 +905,7 @@ bool QFileSystemModel::setData(const QModelIndex &idx, const QVariant &value, in
nodeToRename->fileName = newName;
nodeToRename->parent = parentNode;
#if QT_CONFIG(filesystemwatcher)
- nodeToRename->populate(d->fileInfoGatherer.getInfo(QFileInfo(parentPath, newName)));
+ nodeToRename->populate(d->fileInfoGatherer->getInfo(QFileInfo(parentPath, newName)));
#endif
nodeToRename->isVisible = true;
parentNode->children[newName] = nodeToRename.release();
@@ -913,23 +941,27 @@ QVariant QFileSystemModel::headerData(int section, Qt::Orientation orientation,
QString returnValue;
switch (section) {
- case 0: returnValue = tr("Name");
- break;
- case 1: returnValue = tr("Size");
- break;
- case 2: returnValue =
+ case QFileSystemModelPrivate::NameColumn:
+ returnValue = tr("Name");
+ break;
+ case QFileSystemModelPrivate::SizeColumn:
+ returnValue = tr("Size");
+ break;
+ case QFileSystemModelPrivate::TypeColumn:
+ returnValue =
#ifdef Q_OS_MAC
- tr("Kind", "Match OS X Finder");
+ tr("Kind", "Match OS X Finder");
#else
- tr("Type", "All other platforms");
+ tr("Type", "All other platforms");
#endif
- break;
+ break;
// Windows - Type
// OS X - Kind
// Konqueror - File Type
// Nautilus - Type
- case 3: returnValue = tr("Date Modified");
- break;
+ case QFileSystemModelPrivate::TimeColumn:
+ returnValue = tr("Date Modified");
+ break;
default: return QVariant();
}
return returnValue;
@@ -969,7 +1001,7 @@ Qt::ItemFlags QFileSystemModel::flags(const QModelIndex &index) const
/*!
\internal
*/
-void QFileSystemModelPrivate::_q_performDelayedSort()
+void QFileSystemModelPrivate::performDelayedSort()
{
Q_Q(QFileSystemModel);
q->sort(sortColumn, sortOrder);
@@ -993,7 +1025,7 @@ public:
const QFileSystemModelPrivate::QFileSystemNode *r) const
{
switch (sortColumn) {
- case 0: {
+ case QFileSystemModelPrivate::NameColumn: {
#ifndef Q_OS_MAC
// place directories before files
bool left = l->isDir();
@@ -1003,7 +1035,7 @@ public:
#endif
return naturalCompare.compare(l->fileName, r->fileName) < 0;
}
- case 1:
+ case QFileSystemModelPrivate::SizeColumn:
{
// Directories go first
bool left = l->isDir();
@@ -1017,7 +1049,7 @@ public:
return sizeDifference < 0;
}
- case 2:
+ case QFileSystemModelPrivate::TypeColumn:
{
int compare = naturalCompare.compare(l->type(), r->type());
if (compare == 0)
@@ -1025,12 +1057,14 @@ public:
return compare < 0;
}
- case 3:
+ case QFileSystemModelPrivate::TimeColumn:
{
- if (l->lastModified() == r->lastModified())
+ const QDateTime left = l->lastModified(QTimeZone::UTC);
+ const QDateTime right = r->lastModified(QTimeZone::UTC);
+ if (left == right)
return naturalCompare.compare(l->fileName, r->fileName) < 0;
- return l->lastModified() < r->lastModified();
+ return left < right;
}
}
Q_ASSERT(false);
@@ -1058,7 +1092,7 @@ void QFileSystemModelPrivate::sortChildren(int column, const QModelIndex &parent
{
Q_Q(QFileSystemModel);
QFileSystemModelPrivate::QFileSystemNode *indexNode = node(parent);
- if (indexNode->children.count() == 0)
+ if (indexNode->children.size() == 0)
return;
QList<QFileSystemModelPrivate::QFileSystemNode *> values;
@@ -1076,11 +1110,10 @@ void QFileSystemModelPrivate::sortChildren(int column, const QModelIndex &parent
indexNode->visibleChildren.clear();
//No more dirty item we reset our internal dirty index
indexNode->dirtyChildrenIndex = -1;
- const int numValues = values.count();
- indexNode->visibleChildren.reserve(numValues);
- for (int i = 0; i < numValues; ++i) {
- indexNode->visibleChildren.append(values.at(i)->fileName);
- values.at(i)->isVisible = true;
+ indexNode->visibleChildren.reserve(values.size());
+ for (QFileSystemNode *node : std::as_const(values)) {
+ indexNode->visibleChildren.append(node->fileName);
+ node->isVisible = true;
}
if (!disableRecursiveSort) {
@@ -1106,10 +1139,8 @@ void QFileSystemModel::sort(int column, Qt::SortOrder order)
emit layoutAboutToBeChanged();
QModelIndexList oldList = persistentIndexList();
QList<QPair<QFileSystemModelPrivate::QFileSystemNode *, int>> oldNodes;
- const int nodeCount = oldList.count();
- oldNodes.reserve(nodeCount);
- for (int i = 0; i < nodeCount; ++i) {
- const QModelIndex &oldNode = oldList.at(i);
+ oldNodes.reserve(oldList.size());
+ for (const QModelIndex &oldNode : oldList) {
QPair<QFileSystemModelPrivate::QFileSystemNode*, int> pair(d->node(oldNode), oldNode.column());
oldNodes.append(pair);
}
@@ -1123,12 +1154,10 @@ void QFileSystemModel::sort(int column, Qt::SortOrder order)
d->sortOrder = order;
QModelIndexList newList;
- const int numOldNodes = oldNodes.size();
- newList.reserve(numOldNodes);
- for (int i = 0; i < numOldNodes; ++i) {
- const QPair<QFileSystemModelPrivate::QFileSystemNode*, int> &oldNode = oldNodes.at(i);
- newList.append(d->index(oldNode.first, oldNode.second));
- }
+ newList.reserve(oldNodes.size());
+ for (const auto &[node, col]: std::as_const(oldNodes))
+ newList.append(d->index(node, col));
+
changePersistentIndexList(oldList, newList);
emit layoutChanged();
}
@@ -1155,7 +1184,7 @@ QMimeData *QFileSystemModel::mimeData(const QModelIndexList &indexes) const
QList<QUrl> urls;
QList<QModelIndex>::const_iterator it = indexes.begin();
for (; it != indexes.end(); ++it)
- if ((*it).column() == 0)
+ if ((*it).column() == QFileSystemModelPrivate::NameColumn)
urls << QUrl::fromLocalFile(filePath(*it));
QMimeData *data = new QMimeData();
data->setUrls(urls);
@@ -1229,6 +1258,7 @@ QHash<int, QByteArray> QFileSystemModel::roleNames() const
ret.insert(QFileSystemModel::FilePathRole, QByteArrayLiteral("filePath"));
ret.insert(QFileSystemModel::FileNameRole, QByteArrayLiteral("fileName"));
ret.insert(QFileSystemModel::FilePermissions, QByteArrayLiteral("filePermissions"));
+ ret.insert(QFileSystemModel::FileInfoRole, QByteArrayLiteral("fileInfo"));
return ret;
}
@@ -1301,7 +1331,7 @@ void QFileSystemModel::setOptions(Options options)
#if QT_CONFIG(filesystemwatcher)
Q_D(QFileSystemModel);
if (changed.testFlag(DontWatchForChanges))
- d->fileInfoGatherer.setWatching(!options.testFlag(DontWatchForChanges));
+ d->fileInfoGatherer->setWatching(!options.testFlag(DontWatchForChanges));
#endif
if (changed.testFlag(DontUseCustomDirectoryIcons)) {
@@ -1322,7 +1352,7 @@ QFileSystemModel::Options QFileSystemModel::options() const
result.setFlag(DontResolveSymlinks, !resolveSymlinks());
#if QT_CONFIG(filesystemwatcher)
Q_D(const QFileSystemModel);
- result.setFlag(DontWatchForChanges, !d->fileInfoGatherer.isWatching());
+ result.setFlag(DontWatchForChanges, !d->fileInfoGatherer->isWatching());
#else
result.setFlag(DontWatchForChanges);
#endif
@@ -1344,7 +1374,7 @@ QString QFileSystemModel::filePath(const QModelIndex &index) const
QFileSystemModelPrivate::QFileSystemNode *dirNode = d->node(index);
if (dirNode->isSymLink()
#if QT_CONFIG(filesystemwatcher)
- && d->fileInfoGatherer.resolveSymlinks()
+ && d->fileInfoGatherer->resolveSymlinks()
#endif
&& d->resolvedSymLinks.contains(fullPath)
&& dirNode->isDir()) {
@@ -1380,7 +1410,7 @@ QString QFileSystemModelPrivate::filePath(const QModelIndex &index) const
}
QString fullPath = QDir::fromNativeSeparators(path.join(QDir::separator()));
#if !defined(Q_OS_WIN)
- if ((fullPath.length() > 2) && fullPath[0] == u'/' && fullPath[1] == u'/')
+ if ((fullPath.size() > 2) && fullPath[0] == u'/' && fullPath[1] == u'/')
fullPath = fullPath.mid(1);
#else
if (fullPath.length() == 2 && fullPath.endsWith(u':'))
@@ -1406,7 +1436,7 @@ QModelIndex QFileSystemModel::mkdir(const QModelIndex &parent, const QString &na
Q_ASSERT(parentNode->children.contains(name));
QFileSystemModelPrivate::QFileSystemNode *node = parentNode->children[name];
#if QT_CONFIG(filesystemwatcher)
- node->populate(d->fileInfoGatherer.getInfo(QFileInfo(dir.absolutePath() + QDir::separator() + name)));
+ node->populate(d->fileInfoGatherer->getInfo(QFileInfo(dir.absolutePath() + QDir::separator() + name)));
#endif
d->addVisibleFiles(parentNode, QStringList(name));
return d->index(node);
@@ -1474,7 +1504,7 @@ QModelIndex QFileSystemModel::setRootPath(const QString &newPath)
if (!rootPath().isEmpty() && rootPath() != "."_L1) {
//This remove the watcher for the old rootPath
#if QT_CONFIG(filesystemwatcher)
- d->fileInfoGatherer.removePath(rootPath());
+ d->fileInfoGatherer->removePath(rootPath());
#endif
//This line "marks" the node as dirty, so the next fetchMore
//call on the path will ask the gatherer to install a watcher again
@@ -1530,7 +1560,7 @@ void QFileSystemModel::setIconProvider(QAbstractFileIconProvider *provider)
{
Q_D(QFileSystemModel);
#if QT_CONFIG(filesystemwatcher)
- d->fileInfoGatherer.setIconProvider(provider);
+ d->fileInfoGatherer->setIconProvider(provider);
#endif
d->root.updateIcon(provider, QString());
}
@@ -1542,9 +1572,9 @@ QAbstractFileIconProvider *QFileSystemModel::iconProvider() const
{
#if QT_CONFIG(filesystemwatcher)
Q_D(const QFileSystemModel);
- return d->fileInfoGatherer.iconProvider();
+ return d->fileInfoGatherer->iconProvider();
#else
- return 0;
+ return nullptr;
#endif
}
@@ -1598,7 +1628,7 @@ void QFileSystemModel::setResolveSymlinks(bool enable)
{
#if QT_CONFIG(filesystemwatcher)
Q_D(QFileSystemModel);
- d->fileInfoGatherer.setResolveSymlinks(enable);
+ d->fileInfoGatherer->setResolveSymlinks(enable);
#else
Q_UNUSED(enable);
#endif
@@ -1608,7 +1638,7 @@ bool QFileSystemModel::resolveSymlinks() const
{
#if QT_CONFIG(filesystemwatcher)
Q_D(const QFileSystemModel);
- return d->fileInfoGatherer.resolveSymlinks();
+ return d->fileInfoGatherer->resolveSymlinks();
#else
return false;
#endif
@@ -1713,7 +1743,7 @@ bool QFileSystemModel::event(QEvent *event)
#if QT_CONFIG(filesystemwatcher)
Q_D(QFileSystemModel);
if (event->type() == QEvent::LanguageChange) {
- d->root.retranslateStrings(d->fileInfoGatherer.iconProvider(), QString());
+ d->root.retranslateStrings(d->fileInfoGatherer->iconProvider(), QString());
return true;
}
#endif
@@ -1727,7 +1757,7 @@ bool QFileSystemModel::rmdir(const QModelIndex &aindex)
#if QT_CONFIG(filesystemwatcher)
if (success) {
QFileSystemModelPrivate * d = const_cast<QFileSystemModelPrivate*>(d_func());
- d->fileInfoGatherer.removePath(path);
+ d->fileInfoGatherer->removePath(path);
}
#endif
return success;
@@ -1739,10 +1769,10 @@ bool QFileSystemModel::rmdir(const QModelIndex &aindex)
Performed quick listing and see if any files have been added or removed,
then fetch more information on visible files.
*/
-void QFileSystemModelPrivate::_q_directoryChanged(const QString &directory, const QStringList &files)
+void QFileSystemModelPrivate::directoryChanged(const QString &directory, const QStringList &files)
{
QFileSystemModelPrivate::QFileSystemNode *parentNode = node(directory, false);
- if (parentNode->children.count() == 0)
+ if (parentNode->children.size() == 0)
return;
QStringList toRemove;
QStringList newFiles = files;
@@ -1752,7 +1782,7 @@ void QFileSystemModelPrivate::_q_directoryChanged(const QString &directory, cons
if ((iterator == newFiles.end()) || (i.value()->fileName < *iterator))
toRemove.append(i.value()->fileName);
}
- for (int i = 0 ; i < toRemove.count() ; ++i )
+ for (int i = 0 ; i < toRemove.size() ; ++i )
removeNode(parentNode, toRemove[i]);
}
@@ -1844,11 +1874,11 @@ void QFileSystemModelPrivate::addVisibleFiles(QFileSystemNode *parentNode, const
QModelIndex parent = index(parentNode);
bool indexHidden = isHiddenByFilter(parentNode, parent);
if (!indexHidden) {
- q->beginInsertRows(parent, parentNode->visibleChildren.count() , parentNode->visibleChildren.count() + newFiles.count() - 1);
+ q->beginInsertRows(parent, parentNode->visibleChildren.size() , parentNode->visibleChildren.size() + newFiles.size() - 1);
}
if (parentNode->dirtyChildrenIndex == -1)
- parentNode->dirtyChildrenIndex = parentNode->visibleChildren.count();
+ parentNode->dirtyChildrenIndex = parentNode->visibleChildren.size();
for (const auto &newFile : newFiles) {
parentNode->visibleChildren.append(newFile);
@@ -1887,8 +1917,8 @@ void QFileSystemModelPrivate::removeVisibleFile(QFileSystemNode *parentNode, int
The thread has received new information about files,
update and emit dataChanged if it has actually changed.
*/
-void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path,
- const QList<QPair<QString, QFileInfo>> &updates)
+void QFileSystemModelPrivate::fileSystemChanged(const QString &path,
+ const QList<std::pair<QString, QFileInfo>> &updates)
{
#if QT_CONFIG(filesystemwatcher)
Q_Q(QFileSystemModel);
@@ -1899,7 +1929,7 @@ void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path,
for (const auto &update : updates) {
QString fileName = update.first;
Q_ASSERT(!fileName.isEmpty());
- QExtendedInformation info = fileInfoGatherer.getInfo(update.second);
+ QExtendedInformation info = fileInfoGatherer->getInfo(update.second);
bool previouslyHere = parentNode->children.contains(fileName);
if (!previouslyHere) {
addNode(parentNode, fileName, info.fileInfo());
@@ -1944,7 +1974,7 @@ void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path,
std::sort(rowsToUpdate.begin(), rowsToUpdate.end());
QString min;
QString max;
- for (const QString &value : qAsConst(rowsToUpdate)) {
+ for (const QString &value : std::as_const(rowsToUpdate)) {
//##TODO is there a way to bundle signals with QString as the content of the list?
/*if (min.isEmpty()) {
min = value;
@@ -1962,23 +1992,30 @@ void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path,
int visibleMin = parentNode->visibleLocation(min);
int visibleMax = parentNode->visibleLocation(max);
if (visibleMin >= 0
- && visibleMin < parentNode->visibleChildren.count()
+ && visibleMin < parentNode->visibleChildren.size()
&& parentNode->visibleChildren.at(visibleMin) == min
&& visibleMax >= 0) {
- QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMin), 0, parentIndex);
- QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMax), 3, parentIndex);
- emit q->dataChanged(bottom, top);
+ // don't use NumColumns here, a subclass might override columnCount
+ const int lastColumn = q->columnCount(parentIndex) - 1;
+ const QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMin),
+ QFileSystemModelPrivate::NameColumn, parentIndex);
+ const QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMax),
+ lastColumn, parentIndex);
+ // We document that emitting dataChanged with indexes that don't have the
+ // same parent is undefined behavior.
+ Q_ASSERT(bottom.parent() == top.parent());
+ emit q->dataChanged(top, bottom);
}
/*min = QString();
max = QString();*/
}
- if (newFiles.count() > 0) {
+ if (newFiles.size() > 0) {
addVisibleFiles(parentNode, newFiles);
}
- if (newFiles.count() > 0 || (sortColumn != 0 && rowsToUpdate.count() > 0)) {
+ if (newFiles.size() > 0 || (sortColumn != 0 && rowsToUpdate.size() > 0)) {
forceSort = true;
delayedSort();
}
@@ -1991,7 +2028,7 @@ void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path,
/*!
\internal
*/
-void QFileSystemModelPrivate::_q_resolvedName(const QString &fileName, const QString &resolvedName)
+void QFileSystemModelPrivate::resolvedName(const QString &fileName, const QString &resolvedName)
{
resolvedSymLinks[fileName] = resolvedName;
}
@@ -2023,22 +2060,41 @@ QStringList QFileSystemModelPrivate::unwatchPathsAt(const QModelIndex &index)
return false;
};
- const QStringList &watchedFiles = fileInfoGatherer.watchedFiles();
+ const QStringList &watchedFiles = fileInfoGatherer->watchedFiles();
std::copy_if(watchedFiles.cbegin(), watchedFiles.cend(),
std::back_inserter(result), filter);
- const QStringList &watchedDirectories = fileInfoGatherer.watchedDirectories();
+ const QStringList &watchedDirectories = fileInfoGatherer->watchedDirectories();
std::copy_if(watchedDirectories.cbegin(), watchedDirectories.cend(),
std::back_inserter(result), filter);
- fileInfoGatherer.unwatchPaths(result);
+ fileInfoGatherer->unwatchPaths(result);
return result;
}
#endif // filesystemwatcher && Q_OS_WIN
-QFileSystemModelPrivate::QFileSystemModelPrivate() = default;
+QFileSystemModelPrivate::QFileSystemModelPrivate()
+#if QT_CONFIG(filesystemwatcher)
+ : fileInfoGatherer(new QFileInfoGatherer)
+#endif // filesystemwatcher
+{
+}
-QFileSystemModelPrivate::~QFileSystemModelPrivate() = default;
+QFileSystemModelPrivate::~QFileSystemModelPrivate()
+{
+#if QT_CONFIG(filesystemwatcher)
+ fileInfoGatherer->requestAbort();
+ if (!fileInfoGatherer->wait(1000)) {
+ // If the thread hangs, perhaps because the network was disconnected
+ // while the gatherer was stat'ing a remote file, then don't block
+ // shutting down the model (which might block a file dialog and the
+ // main thread). Schedule the gatherer for later deletion; it's
+ // destructor will wait for the thread to finish.
+ auto *rawGatherer = fileInfoGatherer.release();
+ rawGatherer->deleteLater();
+ }
+#endif // filesystemwatcher
+}
/*!
\internal
@@ -2049,18 +2105,20 @@ void QFileSystemModelPrivate::init()
delayedSortTimer.setSingleShot(true);
- qRegisterMetaType<QList<QPair<QString, QFileInfo>>>();
+ qRegisterMetaType<QList<std::pair<QString, QFileInfo>>>();
#if QT_CONFIG(filesystemwatcher)
- q->connect(&fileInfoGatherer, SIGNAL(newListOfFiles(QString,QStringList)),
- q, SLOT(_q_directoryChanged(QString,QStringList)));
- q->connect(&fileInfoGatherer, SIGNAL(updates(QString, QList<QPair<QString, QFileInfo>>)), q,
- SLOT(_q_fileSystemChanged(QString, QList<QPair<QString, QFileInfo>>)));
- q->connect(&fileInfoGatherer, SIGNAL(nameResolved(QString,QString)),
- q, SLOT(_q_resolvedName(QString,QString)));
- q->connect(&fileInfoGatherer, SIGNAL(directoryLoaded(QString)),
- q, SIGNAL(directoryLoaded(QString)));
+ QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::newListOfFiles,
+ this, &QFileSystemModelPrivate::directoryChanged);
+ QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::updates,
+ this, &QFileSystemModelPrivate::fileSystemChanged);
+ QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::nameResolved,
+ this, &QFileSystemModelPrivate::resolvedName);
+ q->connect(fileInfoGatherer.get(), &QFileInfoGatherer::directoryLoaded,
+ q, &QFileSystemModel::directoryLoaded);
#endif // filesystemwatcher
- q->connect(&delayedSortTimer, SIGNAL(timeout()), q, SLOT(_q_performDelayedSort()), Qt::QueuedConnection);
+ QObjectPrivate::connect(&delayedSortTimer, &QTimer::timeout,
+ this, &QFileSystemModelPrivate::performDelayedSort,
+ Qt::QueuedConnection);
}
/*!
@@ -2073,8 +2131,14 @@ void QFileSystemModelPrivate::init()
*/
bool QFileSystemModelPrivate::filtersAcceptsNode(const QFileSystemNode *node) const
{
+ // When the model is set to only show files, then a node representing a dir
+ // should be hidden regardless of bypassFilters.
+ // QTBUG-74471
+ const bool hideDirs = (filters & (QDir::Dirs | QDir::AllDirs)) == 0;
+ const bool shouldHideDirNode = hideDirs && node->isDir();
+
// always accept drives
- if (node->parent == &root || bypassFilters.contains(node))
+ if (node->parent == &root || (!shouldHideDirNode && bypassFilters.contains(node)))
return true;
// If we don't know anything yet don't accept it
@@ -2083,7 +2147,6 @@ bool QFileSystemModelPrivate::filtersAcceptsNode(const QFileSystemNode *node) co
const bool filterPermissions = ((filters & QDir::PermissionMask)
&& (filters & QDir::PermissionMask) != QDir::PermissionMask);
- const bool hideDirs = !(filters & (QDir::Dirs | QDir::AllDirs));
const bool hideFiles = !(filters & QDir::Files);
const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable));
const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable));
diff --git a/src/gui/itemmodels/qfilesystemmodel.h b/src/gui/itemmodels/qfilesystemmodel.h
index d7bdf82589..1fd1041f15 100644
--- a/src/gui/itemmodels/qfilesystemmodel.h
+++ b/src/gui/itemmodels/qfilesystemmodel.h
@@ -9,7 +9,6 @@
#include <QtCore/qpair.h>
#include <QtCore/qdir.h>
#include <QtGui/qicon.h>
-#include <QtCore/qdiriterator.h>
QT_REQUIRE_CONFIG(filesystemmodel);
@@ -33,11 +32,13 @@ Q_SIGNALS:
void directoryLoaded(const QString &path);
public:
+ // ### Qt 7: renumber these values to be before Qt::UserRole comment.
enum Roles {
FileIconRole = Qt::DecorationRole,
+ FileInfoRole = Qt::UserRole - 1,
FilePathRole = Qt::UserRole + 1,
FileNameRole = Qt::UserRole + 2,
- FilePermissions = Qt::UserRole + 3
+ FilePermissions = Qt::UserRole + 3,
};
enum Option
@@ -113,7 +114,9 @@ public:
bool isDir(const QModelIndex &index) const;
qint64 size(const QModelIndex &index) const;
QString type(const QModelIndex &index) const;
+
QDateTime lastModified(const QModelIndex &index) const;
+ QDateTime lastModified(const QModelIndex &index, const QTimeZone &tz) const;
QModelIndex mkdir(const QModelIndex &parent, const QString &name);
bool rmdir(const QModelIndex &index);
@@ -132,13 +135,6 @@ private:
Q_DECLARE_PRIVATE(QFileSystemModel)
Q_DISABLE_COPY(QFileSystemModel)
- Q_PRIVATE_SLOT(d_func(), void _q_directoryChanged(const QString &directory, const QStringList &list))
- Q_PRIVATE_SLOT(d_func(), void _q_performDelayedSort())
- Q_PRIVATE_SLOT(d_func(),
- void _q_fileSystemChanged(const QString &path,
- const QList<QPair<QString, QFileInfo>> &))
- Q_PRIVATE_SLOT(d_func(), void _q_resolvedName(const QString &fileName, const QString &resolvedName))
-
friend class QFileDialogPrivate;
};
diff --git a/src/gui/itemmodels/qfilesystemmodel_p.h b/src/gui/itemmodels/qfilesystemmodel_p.h
index 3cb08d1308..e01b0d56e6 100644
--- a/src/gui/itemmodels/qfilesystemmodel_p.h
+++ b/src/gui/itemmodels/qfilesystemmodel_p.h
@@ -50,7 +50,10 @@ public:
Q_DECLARE_TYPEINFO(QFileSystemModelNodePathKey, Q_RELOCATABLE_TYPE);
-inline size_t qHash(const QFileSystemModelNodePathKey &key) { return qHash(key.toCaseFolded()); }
+inline size_t qHash(const QFileSystemModelNodePathKey &key, size_t seed = 0)
+{
+ return qHash(key.toCaseFolded(), seed);
+}
#else // Q_OS_WIN
typedef QString QFileSystemModelNodePathKey;
#endif
@@ -60,7 +63,13 @@ class Q_GUI_EXPORT QFileSystemModelPrivate : public QAbstractItemModelPrivate
Q_DECLARE_PUBLIC(QFileSystemModel)
public:
- enum { NumColumns = 4 };
+ enum {
+ NameColumn,
+ SizeColumn,
+ TypeColumn,
+ TimeColumn,
+ NumColumns = 4
+ };
class QFileSystemNode
{
@@ -81,7 +90,7 @@ public:
inline qint64 size() const { if (info && !info->isDir()) return info->size(); return 0; }
inline QString type() const { if (info) return info->displayType; return QLatin1StringView(""); }
- inline QDateTime lastModified() const { if (info) return info->lastModified(); return QDateTime(); }
+ inline QDateTime lastModified(const QTimeZone &tz) const { return info ? info->lastModified(tz) : QDateTime(); }
inline QFile::Permissions permissions() const { if (info) return info->permissions(); return { }; }
inline bool isReadable() const { return ((permissions() & QFile::ReadUser) != 0); }
inline bool isWritable() const { return ((permissions() & QFile::WriteUser) != 0); }
@@ -89,7 +98,7 @@ public:
inline bool isDir() const {
if (info)
return info->isDir();
- if (children.count() > 0)
+ if (children.size() > 0)
return true;
return false;
}
@@ -141,9 +150,13 @@ public:
return visibleChildren.indexOf(childName);
}
void updateIcon(QAbstractFileIconProvider *iconProvider, const QString &path) {
+ if (!iconProvider)
+ return;
+
if (info)
info->icon = iconProvider->icon(QFileInfo(path));
- for (QFileSystemNode *child : qAsConst(children)) {
+
+ for (QFileSystemNode *child : std::as_const(children)) {
//On windows the root (My computer) has no path so we don't want to add a / for nothing (e.g. /C:/)
if (!path.isEmpty()) {
if (path.endsWith(u'/'))
@@ -156,9 +169,12 @@ public:
}
void retranslateStrings(QAbstractFileIconProvider *iconProvider, const QString &path) {
+ if (!iconProvider)
+ return;
+
if (info)
info->displayType = iconProvider->type(QFileInfo(path));
- for (QFileSystemNode *child : qAsConst(children)) {
+ for (QFileSystemNode *child : std::as_const(children)) {
//On windows the root (My computer) has no path so we don't want to add a / for nothing (e.g. /C:/)
if (!path.isEmpty()) {
if (path.endsWith(u'/'))
@@ -206,7 +222,7 @@ public:
inline int translateVisibleLocation(QFileSystemNode *parent, int row) const {
if (sortOrder != Qt::AscendingOrder) {
if (parent->dirtyChildrenIndex == -1)
- return parent->visibleChildren.count() - row - 1;
+ return parent->visibleChildren.size() - row - 1;
if (row < parent->dirtyChildrenIndex)
return parent->dirtyChildrenIndex - row - 1;
@@ -241,18 +257,18 @@ public:
QString type(const QModelIndex &index) const;
QString time(const QModelIndex &index) const;
- void _q_directoryChanged(const QString &directory, const QStringList &list);
- void _q_performDelayedSort();
- void _q_fileSystemChanged(const QString &path, const QList<QPair<QString, QFileInfo>> &);
- void _q_resolvedName(const QString &fileName, const QString &resolvedName);
+ void directoryChanged(const QString &directory, const QStringList &list);
+ void performDelayedSort();
+ void fileSystemChanged(const QString &path, const QList<std::pair<QString, QFileInfo>> &);
+ void resolvedName(const QString &fileName, const QString &resolvedName);
QDir rootDir;
#if QT_CONFIG(filesystemwatcher)
# ifdef Q_OS_WIN
QStringList unwatchPathsAt(const QModelIndex &);
- void watchPaths(const QStringList &paths) { fileInfoGatherer.watchPaths(paths); }
+ void watchPaths(const QStringList &paths) { fileInfoGatherer->watchPaths(paths); }
# endif // Q_OS_WIN
- QFileInfoGatherer fileInfoGatherer;
+ std::unique_ptr<QFileInfoGatherer> fileInfoGatherer;
#endif // filesystemwatcher
QTimer delayedSortTimer;
QHash<const QFileSystemNode*, bool> bypassFilters;
diff --git a/src/gui/itemmodels/qstandarditemmodel.cpp b/src/gui/itemmodels/qstandarditemmodel.cpp
index bfb5e8e134..8b3e381431 100644
--- a/src/gui/itemmodels/qstandarditemmodel.cpp
+++ b/src/gui/itemmodels/qstandarditemmodel.cpp
@@ -19,6 +19,11 @@
QT_BEGIN_NAMESPACE
+// Used internally to store the flags
+namespace {
+constexpr auto DataFlagsRole = Qt::ItemDataRole(Qt::UserRole - 1);
+}
+
static inline QString qStandardItemModelDataListMimeType()
{
return QStringLiteral("application/x-qstandarditemmodeldatalist");
@@ -275,14 +280,12 @@ void QStandardItemPrivate::setItemData(const QMap<int, QVariant> &roles)
/*!
\internal
*/
-const QMap<int, QVariant> QStandardItemPrivate::itemData() const
+QMap<int, QVariant> QStandardItemPrivate::itemData() const
{
QMap<int, QVariant> result;
- QList<QStandardItemData>::const_iterator it;
- for (it = values.cbegin(); it != values.cend(); ++it){
- // Qt::UserRole - 1 is used internally to store the flags
- if (it->role != Qt::UserRole - 1)
- result.insert(it->role, it->value);
+ for (const auto &data : values) {
+ if (data.role != DataFlagsRole)
+ result.insert(data.role, data.value);
}
return result;
}
@@ -319,11 +322,11 @@ void QStandardItemPrivate::sortChildren(int column, Qt::SortOrder order)
}
QModelIndexList changedPersistentIndexesFrom, changedPersistentIndexesTo;
- QList<QStandardItem*> sorted_children(children.count());
+ QList<QStandardItem*> sorted_children(children.size());
for (int i = 0; i < rowCount(); ++i) {
- int r = (i < sortable.count()
+ int r = (i < sortable.size()
? sortable.at(i).second
- : unsortable.at(i - sortable.count()));
+ : unsortable.at(i - sortable.size()));
for (int c = 0; c < columnCount(); ++c) {
QStandardItem *itm = q->child(r, c);
sorted_children[childIndex(i, c)] = itm;
@@ -371,7 +374,7 @@ void QStandardItemPrivate::setModel(QStandardItemModel *mod)
}
itm->d_func()->model = mod;
const QList<QStandardItem*> &childList = itm->d_func()->children;
- for (int i = 0; i < childList.count(); ++i) {
+ for (int i = 0; i < childList.size(); ++i) {
QStandardItem *chi = childList.at(i);
if (chi)
stack.push(chi);
@@ -432,7 +435,7 @@ bool QStandardItemPrivate::insertRows(int row, const QList<QStandardItem*> &item
Q_Q(QStandardItem);
if ((row < 0) || (row > rowCount()) || items.isEmpty())
return false;
- int count = items.count();
+ int count = items.size();
if (model)
model->d_func()->rowsAboutToBeInserted(q, row, row + count - 1);
if (rowCount() == 0) {
@@ -446,7 +449,7 @@ bool QStandardItemPrivate::insertRows(int row, const QList<QStandardItem*> &item
if (index != -1)
children.insert(index, columnCount() * count, nullptr);
}
- for (int i = 0; i < items.count(); ++i) {
+ for (int i = 0; i < items.size(); ++i) {
QStandardItem *item = items.at(i);
item->d_func()->model = model;
item->d_func()->parent = q;
@@ -478,7 +481,7 @@ bool QStandardItemPrivate::insertRows(int row, int count, const QList<QStandardI
}
if (!items.isEmpty()) {
int index = childIndex(row, 0);
- int limit = qMin(items.count(), columnCount() * count);
+ int limit = qMin(items.size(), columnCount() * count);
for (int i = 0; i < limit; ++i) {
QStandardItem *item = items.at(i);
if (item) {
@@ -523,7 +526,7 @@ bool QStandardItemPrivate::insertColumns(int column, int count, const QList<QSta
}
}
if (!items.isEmpty()) {
- int limit = qMin(items.count(), rowCount() * count);
+ int limit = qMin(items.size(), rowCount() * count);
for (int i = 0; i < limit; ++i) {
QStandardItem *item = items.at(i);
if (item) {
@@ -840,7 +843,7 @@ QStandardItem &QStandardItem::operator=(const QStandardItem &other)
QStandardItem::~QStandardItem()
{
Q_D(QStandardItem);
- for (QStandardItem *child : qAsConst(d->children)) {
+ for (QStandardItem *child : std::as_const(d->children)) {
if (child)
child->d_func()->setModel(nullptr);
delete child;
@@ -869,9 +872,15 @@ QStandardItem *QStandardItem::parent() const
Sets the item's data for the given \a role to the specified \a value.
If you subclass QStandardItem and reimplement this function, your
- reimplementation should call emitDataChanged() if you do not call
- the base implementation of setData(). This will ensure that e.g.
- views using the model are notified of the changes.
+ reimplementation should:
+ \list
+ \li call emitDataChanged() if you do not call the base implementation of
+ setData(). This will ensure that e.g. views using the model are notified
+ of the changes
+ \li call the base implementation for roles you don't handle, otherwise
+ setting flags, e.g. by calling setFlags(), setCheckable(), setEditable()
+ etc., will not work.
+ \endlist
\note The default implementation treats Qt::EditRole and Qt::DisplayRole
as referring to the same data.
@@ -925,6 +934,11 @@ void QStandardItem::clearData()
Returns the item's data for the given \a role, or an invalid
QVariant if there is no data for the role.
+ If you reimplement this function, your reimplementation should call
+ the base implementation for roles you don't handle, otherwise getting
+ flags, e.g. by calling flags(), isCheckable(), isEditable() etc.,
+ will not work.
+
\note The default implementation treats Qt::EditRole and Qt::DisplayRole
as referring to the same data.
*/
@@ -984,7 +998,7 @@ void QStandardItem::emitDataChanged()
*/
void QStandardItem::setFlags(Qt::ItemFlags flags)
{
- setData((int)flags, Qt::UserRole - 1);
+ setData((int)flags, DataFlagsRole);
}
/*!
@@ -999,7 +1013,7 @@ void QStandardItem::setFlags(Qt::ItemFlags flags)
*/
Qt::ItemFlags QStandardItem::flags() const
{
- QVariant v = data(Qt::UserRole - 1);
+ QVariant v = data(DataFlagsRole);
if (!v.isValid())
return (Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsEditable
|Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled);
@@ -1588,8 +1602,8 @@ void QStandardItem::insertRow(int row, const QList<QStandardItem*> &items)
Q_D(QStandardItem);
if (row < 0)
return;
- if (columnCount() < items.count())
- setColumnCount(items.count());
+ if (columnCount() < items.size())
+ setColumnCount(items.size());
d->insertRows(row, 1, items);
}
@@ -1617,8 +1631,8 @@ void QStandardItem::insertColumn(int column, const QList<QStandardItem*> &items)
Q_D(QStandardItem);
if (column < 0)
return;
- if (rowCount() < items.count())
- setRowCount(items.count());
+ if (rowCount() < items.size())
+ setRowCount(items.size());
d->insertColumns(column, 1, items);
}
@@ -1859,28 +1873,30 @@ QStandardItem *QStandardItem::takeChild(int row, int column)
if (index != -1) {
QModelIndex changedIdx;
item = d->children.at(index);
- if (item && d->model) {
+ if (item) {
QStandardItemPrivate *const item_d = item->d_func();
- const int savedRows = item_d->rows;
- const int savedCols = item_d->columns;
- const QVector<QStandardItem*> savedChildren = item_d->children;
- if (savedRows > 0) {
- d->model->d_func()->rowsAboutToBeRemoved(item, 0, savedRows - 1);
- item_d->rows = 0;
- item_d->children = QVector<QStandardItem*>(); //slightly faster than clear
- d->model->d_func()->rowsRemoved(item, 0, savedRows);
- }
- if (savedCols > 0) {
- d->model->d_func()->columnsAboutToBeRemoved(item, 0, savedCols - 1);
- item_d->columns = 0;
- if (!item_d->children.isEmpty())
+ if (d->model) {
+ QStandardItemModelPrivate *const model_d = d->model->d_func();
+ const int savedRows = item_d->rows;
+ const int savedCols = item_d->columns;
+ const QVector<QStandardItem*> savedChildren = item_d->children;
+ if (savedRows > 0) {
+ model_d->rowsAboutToBeRemoved(item, 0, savedRows - 1);
+ item_d->rows = 0;
+ item_d->children = QVector<QStandardItem*>(); //slightly faster than clear
+ model_d->rowsRemoved(item, 0, savedRows);
+ }
+ if (savedCols > 0) {
+ model_d->columnsAboutToBeRemoved(item, 0, savedCols - 1);
+ item_d->columns = 0;
item_d->children = QVector<QStandardItem*>(); //slightly faster than clear
- d->model->d_func()->columnsRemoved(item, 0, savedCols);
+ model_d->columnsRemoved(item, 0, savedCols);
+ }
+ item_d->rows = savedRows;
+ item_d->columns = savedCols;
+ item_d->children = savedChildren;
+ changedIdx = d->model->indexFromItem(item);
}
- item_d->rows = savedRows;
- item_d->columns = savedCols;
- item_d->children = savedChildren;
- changedIdx = d->model->indexFromItem(item);
item_d->setParentAndModel(nullptr, nullptr);
}
d->children.replace(index, nullptr);
@@ -2529,9 +2545,9 @@ QStandardItem *QStandardItemModel::verticalHeaderItem(int row) const
void QStandardItemModel::setHorizontalHeaderLabels(const QStringList &labels)
{
Q_D(QStandardItemModel);
- if (columnCount() < labels.count())
- setColumnCount(labels.count());
- for (int i = 0; i < labels.count(); ++i) {
+ if (columnCount() < labels.size())
+ setColumnCount(labels.size());
+ for (int i = 0; i < labels.size(); ++i) {
QStandardItem *item = horizontalHeaderItem(i);
if (!item) {
item = d->createItem();
@@ -2552,9 +2568,9 @@ void QStandardItemModel::setHorizontalHeaderLabels(const QStringList &labels)
void QStandardItemModel::setVerticalHeaderLabels(const QStringList &labels)
{
Q_D(QStandardItemModel);
- if (rowCount() < labels.count())
- setRowCount(labels.count());
- for (int i = 0; i < labels.count(); ++i) {
+ if (rowCount() < labels.size())
+ setRowCount(labels.size());
+ for (int i = 0; i < labels.size(); ++i) {
QStandardItem *item = verticalHeaderItem(i);
if (!item) {
item = d->createItem();
@@ -3100,21 +3116,21 @@ QStringList QStandardItemModel::mimeTypes() const
*/
QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const
{
- QMimeData *data = QAbstractItemModel::mimeData(indexes);
+ std::unique_ptr<QMimeData> data(QAbstractItemModel::mimeData(indexes));
if (!data)
return nullptr;
const QString format = qStandardItemModelDataListMimeType();
if (!mimeTypes().contains(format))
- return data;
+ return data.release();
QByteArray encoded;
QDataStream stream(&encoded, QIODevice::WriteOnly);
QSet<QStandardItem*> itemsSet;
QStack<QStandardItem*> stack;
- itemsSet.reserve(indexes.count());
- stack.reserve(indexes.count());
- for (int i = 0; i < indexes.count(); ++i) {
+ itemsSet.reserve(indexes.size());
+ stack.reserve(indexes.size());
+ for (int i = 0; i < indexes.size(); ++i) {
if (QStandardItem *item = itemFromIndex(indexes.at(i))) {
itemsSet << item;
stack.push(item);
@@ -3133,7 +3149,7 @@ QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const
continue;
const QList<QStandardItem*> &childList = itm->d_func()->children;
- for (int i = 0; i < childList.count(); ++i) {
+ for (int i = 0; i < childList.size(); ++i) {
QStandardItem *chi = childList.at(i);
if (chi) {
itemsSet.remove(chi);
@@ -3143,8 +3159,8 @@ QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const
}
}
- stack.reserve(itemsSet.count());
- for (QStandardItem *item : qAsConst(itemsSet))
+ stack.reserve(itemsSet.size());
+ for (QStandardItem *item : std::as_const(itemsSet))
stack.push(item);
//stream everything recursively
@@ -3153,12 +3169,12 @@ QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const
if (itemsSet.contains(item)) //if the item is selection 'top-level', stream its position
stream << item->row() << item->column();
- stream << *item << item->columnCount() << int(item->d_ptr->children.count());
+ stream << *item << item->columnCount() << int(item->d_ptr->children.size());
stack += item->d_ptr->children;
}
data->setData(format, encoded);
- return data;
+ return data.release();
}
@@ -3242,15 +3258,15 @@ bool QStandardItemModel::dropMimeData(const QMimeData *data, Qt::DropAction acti
// Compute the number of continuous rows upon insertion and modify the rows to match
QList<int> rowsToInsert(bottom + 1);
- for (int i = 0; i < rows.count(); ++i)
+ for (int i = 0; i < rows.size(); ++i)
rowsToInsert[rows.at(i)] = 1;
- for (int i = 0; i < rowsToInsert.count(); ++i) {
+ for (int i = 0; i < rowsToInsert.size(); ++i) {
if (rowsToInsert.at(i) == 1){
rowsToInsert[i] = dragRowCount;
++dragRowCount;
}
}
- for (int i = 0; i < rows.count(); ++i)
+ for (int i = 0; i < rows.size(); ++i)
rows[i] = top + rowsToInsert.at(rows.at(i));
QBitArray isWrittenTo(dragRowCount * dragColumnCount);
diff --git a/src/gui/itemmodels/qstandarditemmodel_p.h b/src/gui/itemmodels/qstandarditemmodel_p.h
index 27e108434d..a0c3f8a161 100644
--- a/src/gui/itemmodels/qstandarditemmodel_p.h
+++ b/src/gui/itemmodels/qstandarditemmodel_p.h
@@ -15,6 +15,8 @@
// We mean it.
//
+#include <QtGui/qstandarditemmodel.h>
+
#include <QtGui/private/qtguiglobal_p.h>
#include "private/qabstractitemmodel_p.h"
@@ -32,8 +34,10 @@ class QStandardItemData
{
public:
inline QStandardItemData() : role(-1) {}
- inline QStandardItemData(int r, const QVariant &v) : role(r), value(v) {}
- inline QStandardItemData(const std::pair<const int&, const QVariant&> &p) : role(p.first), value(p.second) {}
+ inline QStandardItemData(int r, const QVariant &v) :
+ role(r == Qt::EditRole ? Qt::DisplayRole : r), value(v) {}
+ inline QStandardItemData(const std::pair<const int&, const QVariant&> &p) :
+ role(p.first == Qt::EditRole ? Qt::DisplayRole : p.first), value(p.second) {}
int role;
QVariant value;
inline bool operator==(const QStandardItemData &other) const { return role == other.role && value == other.value; }
@@ -144,7 +148,7 @@ public:
void changeFlags(bool enable, Qt::ItemFlags f);
void setItemData(const QMap<int, QVariant> &roles);
- const QMap<int, QVariant> itemData() const;
+ QMap<int, QVariant> itemData() const;
bool insertRows(int row, int count, const QList<QStandardItem*> &items);
bool insertRows(int row, const QList<QStandardItem*> &items);
diff --git a/src/gui/kernel/qaction.cpp b/src/gui/kernel/qaction.cpp
index 637ec37ca5..c67cfd53b1 100644
--- a/src/gui/kernel/qaction.cpp
+++ b/src/gui/kernel/qaction.cpp
@@ -81,26 +81,26 @@ void QActionPrivate::sendDataChanged()
void QActionPrivate::redoGrab(QShortcutMap &map)
{
Q_Q(QAction);
- for (int id : qAsConst(shortcutIds)) {
+ for (int id : std::as_const(shortcutIds)) {
if (id)
map.removeShortcut(id, q);
}
shortcutIds.clear();
- for (const QKeySequence &shortcut : qAsConst(shortcuts)) {
+ for (const QKeySequence &shortcut : std::as_const(shortcuts)) {
if (!shortcut.isEmpty())
shortcutIds.append(map.addShortcut(q, shortcut, shortcutContext, contextMatcher()));
else
shortcutIds.append(0);
}
if (!enabled) {
- for (int id : qAsConst(shortcutIds)) {
+ for (int id : std::as_const(shortcutIds)) {
if (id)
map.setShortcutEnabled(false, id, q);
}
}
if (!autorepeat) {
- for (int id : qAsConst(shortcutIds)) {
+ for (int id : std::as_const(shortcutIds)) {
if (id)
map.setShortcutAutoRepeat(false, id, q);
}
@@ -110,7 +110,7 @@ void QActionPrivate::redoGrab(QShortcutMap &map)
void QActionPrivate::setShortcutEnabled(bool enable, QShortcutMap &map)
{
Q_Q(QAction);
- for (int id : qAsConst(shortcutIds)) {
+ for (int id : std::as_const(shortcutIds)) {
if (id)
map.setShortcutEnabled(enable, id, q);
}
@@ -150,17 +150,18 @@ QObject *QActionPrivate::menu() const
user interface used, it is useful to represent each command as
an \e action.
- Actions can be added to menus and toolbars, and will
- automatically keep them in sync. For example, in a word processor,
- if the user presses a Bold toolbar button, the Bold menu item
+ Actions can be added to user interface elements such as menus and toolbars,
+ and will automatically keep the UI in sync. For example, in a word
+ processor, if the user presses a Bold toolbar button, the Bold menu item
will automatically be checked.
- A QAction may contain an icon, menu text, a shortcut, status text,
- "What's This?" text, and a tooltip. Most of these can be set in
- the constructor. They can also be set independently with
- setIcon(), setText(), setIconText(), setShortcut(),
- setStatusTip(), setWhatsThis(), and setToolTip(). For menu items,
- it is possible to set an individual font with setFont().
+ A QAction may contain an icon, descriptive text, icon text, a keyboard
+ shortcut, status text, "What's This?" text, and a tooltip. All properties
+ can be set independently with setIcon(), setText(), setIconText(),
+ setShortcut(), setStatusTip(), setWhatsThis(), and setToolTip(). Icon and
+ text, as the two most important properties, can also be set in the
+ constructor. It's possible to set an individual font with setFont(), which
+ e.g. menus respect when displaying the action as a menu item.
We recommend that actions are created as children of the window
they are used in. In most cases actions will be children of
@@ -170,9 +171,7 @@ QObject *QActionPrivate::menu() const
Once a QAction has been created, it should be added to the relevant
menu and toolbar, then connected to the slot which will perform
- the action. For example:
-
- \snippet ../widgets/mainwindows/application/mainwindow.cpp 19
+ the action.
Actions are added to widgets using QWidget::addAction() or
QGraphicsWidget::addAction(). Note that an action must be added to a
@@ -185,7 +184,7 @@ QObject *QActionPrivate::menu() const
use as menu items.
- \sa QMenu, QToolBar, {Qt Widgets - Application Example}
+ \sa QMenu, QToolBar
*/
/*!
@@ -238,13 +237,11 @@ QAction::QAction(QObject *parent)
parent is an action group the action will be automatically
inserted into the group.
- The action uses a stripped version of \a text (e.g. "\&Menu
- Option..." becomes "Menu Option") as descriptive text for
- tool buttons. You can override this by setting a specific
- description with setText(). The same text will be used for
- tooltips unless you specify a different text using
- setToolTip().
+ A stripped version of \a text (for example, "\&Menu Option..." becomes
+ "Menu Option") will be used for tooltips and icon text unless you specify a
+ different text using setToolTip() or setIconText(), respectively.
+ \sa text
*/
QAction::QAction(const QString &text, QObject *parent)
: QAction(parent)
@@ -258,12 +255,11 @@ QAction::QAction(const QString &text, QObject *parent)
parent. If \a parent is an action group the action will be
automatically inserted into the group.
- The action uses a stripped version of \a text (e.g. "\&Menu
- Option..." becomes "Menu Option") as descriptive text for
- tool buttons. You can override this by setting a specific
- description with setText(). The same text will be used for
- tooltips unless you specify a different text using
- setToolTip().
+ A stripped version of \a text (for example, "\&Menu Option..." becomes
+ "Menu Option") will be used for tooltips and icon text unless you specify a
+ different text using setToolTip() or setIconText(), respectively.
+
+ \sa text, icon
*/
QAction::QAction(const QIcon &icon, const QString &text, QObject *parent)
: QAction(text, parent)
@@ -458,7 +454,7 @@ QAction::~QAction()
d->group->removeAction(this);
#if QT_CONFIG(shortcut)
if (qApp) {
- for (int id : qAsConst(d->shortcutIds)) {
+ for (int id : std::as_const(d->shortcutIds)) {
if (id)
QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(id, this);
}
@@ -514,14 +510,14 @@ QList<QObject*> QAction::associatedObjects() const
/*!
\fn QWidget *QAction::parentWidget() const
- \deprecated Use parent() with qobject_cast() instead.
+ \deprecated [6.0] Use parent() with qobject_cast() instead.
Returns the parent widget.
*/
/*!
\fn QList<QWidget*> QAction::associatedWidgets() const
- \deprecated Use associatedObjects() with qobject_cast() instead.
+ \deprecated [6.0] Use associatedObjects() with qobject_cast() instead.
Returns a list of widgets this action has been added to.
@@ -530,7 +526,7 @@ QList<QObject*> QAction::associatedObjects() const
/*!
\fn QList<QWidget*> QAction::associatedGraphicsWidgets() const
- \deprecated Use associatedObjects() with qobject_cast() instead.
+ \deprecated [6.0] Use associatedObjects() with qobject_cast() instead.
Returns a list of graphics widgets this action has been added to.
@@ -602,6 +598,14 @@ bool QAction::isSeparator() const
by using setText(), the action's description icon text will be
used as text. There is no default text.
+ Certain UI elements, such as menus or buttons, can use '&' in front of a
+ character to automatically create a mnemonic (a shortcut) for that
+ character. For example, "&File" for a menu will create the shortcut
+ \uicontrol Alt+F, which will open the File menu. "E&xit" will create the
+ shortcut \uicontrol Alt+X for a button, or in a menu allow navigating to
+ the menu item by pressing "x". (use '&&' to display an actual ampersand).
+ The widget might consume and perform an action on a given shortcut.
+
\sa iconText
*/
void QAction::setText(const QString &text)
@@ -1024,7 +1028,7 @@ bool QAction::event(QEvent *e)
{
Q_D(QAction);
if (e->type() == QEvent::ActionChanged) {
- for (auto object : qAsConst(d->associatedObjects))
+ for (auto object : std::as_const(d->associatedObjects))
QCoreApplication::sendEvent(object, e);
}
@@ -1191,14 +1195,13 @@ QAction::MenuRole QAction::menuRole() const
/*!
\fn QMenu *QAction::menu() const
- \deprecated Use QMenu::menuForAction instead.
Returns the menu contained by this action.
In widget applications, actions that contain menus can be used to create menu
items with submenus, or inserted into toolbars to create buttons with popup menus.
- \sa QMenu::addAction()
+ \sa QMenu::addAction(), QMenu::menuInAction()
*/
QObject* QAction::menuObject() const
{
@@ -1208,7 +1211,6 @@ QObject* QAction::menuObject() const
/*!
\fn void QAction::setMenu(QMenu *menu)
- \deprecated
Sets the menu contained by this action to the specified \a menu.
*/
diff --git a/src/gui/kernel/qaction.h b/src/gui/kernel/qaction.h
index 54cfb8a50b..d805599138 100644
--- a/src/gui/kernel/qaction.h
+++ b/src/gui/kernel/qaction.h
@@ -20,10 +20,10 @@ QT_BEGIN_NAMESPACE
class QActionEvent;
class QActionGroup;
class QActionPrivate;
+class QMenu;
#if QT_DEPRECATED_SINCE(6,0)
class QWidget;
class QGraphicsWidget;
-class QMenu;
#endif
class Q_GUI_EXPORT QAction : public QObject
@@ -74,7 +74,7 @@ public:
QList<QObject *> associatedObjects() const;
#if QT_DEPRECATED_SINCE(6,0)
-#ifdef Q_CLANG_QDOC
+#ifdef Q_QDOC
QWidget *parentWidget() const;
QList<QWidget*> associatedWidgets() const;
QList<QGraphicsWidget*> associatedGraphicsWidgets() const;
@@ -84,6 +84,7 @@ public:
QWidget, QMenu, and QGraphicsWidget can be expected to be fully defined.
*/
template<typename T = QWidget*>
+ QT_DEPRECATED_VERSION_X_6_0("Use parent() with qobject_cast() instead")
T parentWidget() const
{
auto result = parent();
@@ -93,6 +94,7 @@ public:
}
template<typename T = QWidget*>
+ QT_DEPRECATED_VERSION_X_6_0("Use associatedObjects() with qobject_cast() instead")
QList<T> associatedWidgets() const
{
QList<T> result;
@@ -102,6 +104,7 @@ public:
return result;
}
template<typename T = QGraphicsWidget*>
+ QT_DEPRECATED_VERSION_X_6_0("Use associatedObjects() with qobject_cast() instead")
QList<T> associatedGraphicsWidgets() const
{
QList<T> result;
@@ -175,8 +178,7 @@ public:
void setMenuRole(MenuRole menuRole);
MenuRole menuRole() const;
-#if QT_DEPRECATED_SINCE(6,0)
-#ifdef Q_CLANG_QDOC
+#ifdef Q_QDOC
QMenu *menu() const;
void setMenu(QMenu *menu);
#else
@@ -191,7 +193,6 @@ public:
setMenuObject(m);
}
#endif
-#endif
void setIconVisibleInMenu(bool visible);
bool isIconVisibleInMenu() const;
diff --git a/src/gui/kernel/qaction_p.h b/src/gui/kernel/qaction_p.h
index 8aa75fdf89..e79cc26b4d 100644
--- a/src/gui/kernel/qaction_p.h
+++ b/src/gui/kernel/qaction_p.h
@@ -21,6 +21,8 @@
#if QT_CONFIG(shortcut)
# include <QtGui/private/qshortcutmap_p.h>
#endif
+
+#include <QtCore/qpointer.h>
#include "private/qobject_p.h"
QT_REQUIRE_CONFIG(action);
diff --git a/src/gui/kernel/qactiongroup.cpp b/src/gui/kernel/qactiongroup.cpp
index 69fc87a27f..7fb5b4ad46 100644
--- a/src/gui/kernel/qactiongroup.cpp
+++ b/src/gui/kernel/qactiongroup.cpp
@@ -277,7 +277,7 @@ void QActionGroup::setEnabled(bool b)
{
Q_D(QActionGroup);
d->enabled = b;
- for (auto action : qAsConst(d->actions)) {
+ for (auto action : std::as_const(d->actions)) {
action->d_func()->setEnabled(b, true);
}
}
@@ -311,7 +311,7 @@ void QActionGroup::setVisible(bool b)
{
Q_D(QActionGroup);
d->visible = b;
- for (auto action : qAsConst(d->actions)) {
+ for (auto action : std::as_const(d->actions)) {
if (!action->d_func()->forceInvisible)
action->d_func()->setVisible(b);
}
diff --git a/src/gui/kernel/qactiongroup_p.h b/src/gui/kernel/qactiongroup_p.h
index c7a058a6f2..ba939a2b10 100644
--- a/src/gui/kernel/qactiongroup_p.h
+++ b/src/gui/kernel/qactiongroup_p.h
@@ -21,6 +21,8 @@
#if QT_CONFIG(shortcut)
# include <QtGui/private/qshortcutmap_p.h>
#endif
+
+#include <QtCore/qpointer.h>
#include "private/qobject_p.h"
QT_REQUIRE_CONFIG(action);
diff --git a/src/gui/kernel/qclipboard.cpp b/src/gui/kernel/qclipboard.cpp
index 9a6ead05b6..6a36862a04 100644
--- a/src/gui/kernel/qclipboard.cpp
+++ b/src/gui/kernel/qclipboard.cpp
@@ -103,6 +103,10 @@ using namespace Qt::StringLiterals;
\endlist
+ \section1 Notes for Android Users
+
+ On Android only these mime types are supported: text/plain, text/html, and text/uri-list.
+
\sa QGuiApplication
*/
@@ -239,11 +243,12 @@ QString QClipboard::text(QString &subtype, Mode mode) const
if (formats.contains("text/plain"_L1))
subtype = "plain"_L1;
else {
- for (int i = 0; i < formats.size(); ++i)
- if (formats.at(i).startsWith("text/"_L1)) {
- subtype = formats.at(i).mid(5);
+ for (const auto &format : formats) {
+ if (format.startsWith("text/"_L1)) {
+ subtype = format.sliced(5);
break;
}
+ }
if (subtype.isEmpty())
return QString();
}
diff --git a/src/gui/kernel/qcursor.cpp b/src/gui/kernel/qcursor.cpp
index 7f052fd5bd..8e7747559a 100644
--- a/src/gui/kernel/qcursor.cpp
+++ b/src/gui/kernel/qcursor.cpp
@@ -290,7 +290,7 @@ QDataStream &operator<<(QDataStream &s, const QCursor &c)
if (isPixmap)
s << c.pixmap();
else
- s << c.bitmap(Qt::ReturnByValue) << c.mask(Qt::ReturnByValue);
+ s << c.bitmap() << c.mask();
s << c.hotSpot();
}
return s;
@@ -439,8 +439,7 @@ QCursor::QCursor()
QCursor::QCursor(Qt::CursorShape shape)
: d(nullptr)
{
- if (!QCursorData::initialized)
- QCursorData::initialize();
+ QCursorData::initialize();
setShape(shape);
}
@@ -498,8 +497,7 @@ bool operator==(const QCursor &lhs, const QCursor &rhs) noexcept
*/
Qt::CursorShape QCursor::shape() const
{
- if (!QCursorData::initialized)
- QCursorData::initialize();
+ QCursorData::initialize();
return d->cshape;
}
@@ -512,8 +510,7 @@ Qt::CursorShape QCursor::shape() const
*/
void QCursor::setShape(Qt::CursorShape shape)
{
- if (!QCursorData::initialized)
- QCursorData::initialize();
+ QCursorData::initialize();
QCursorData *c = uint(shape) <= Qt::LastCursor ? qt_cursorTable[shape] : nullptr;
if (!c)
c = qt_cursorTable[0];
@@ -547,8 +544,7 @@ void QCursor::setShape(Qt::CursorShape shape)
*/
QBitmap QCursor::bitmap() const
{
- if (!QCursorData::initialized)
- QCursorData::initialize();
+ QCursorData::initialize();
if (d->bm)
return *(d->bm);
return QBitmap();
@@ -574,8 +570,7 @@ QBitmap QCursor::bitmap() const
*/
QBitmap QCursor::mask() const
{
- if (!QCursorData::initialized)
- QCursorData::initialize();
+ QCursorData::initialize();
if (d->bmm)
return *(d->bmm);
return QBitmap();
@@ -588,8 +583,7 @@ QBitmap QCursor::mask() const
QPixmap QCursor::pixmap() const
{
- if (!QCursorData::initialized)
- QCursorData::initialize();
+ QCursorData::initialize();
return d->pixmap;
}
@@ -600,8 +594,7 @@ QPixmap QCursor::pixmap() const
QPoint QCursor::hotSpot() const
{
- if (!QCursorData::initialized)
- QCursorData::initialize();
+ QCursorData::initialize();
return QPoint(d->hx, d->hy);
}
@@ -611,8 +604,7 @@ QPoint QCursor::hotSpot() const
QCursor::QCursor(const QCursor &c)
{
- if (!QCursorData::initialized)
- QCursorData::initialize();
+ QCursorData::initialize();
d = c.d;
d->ref.ref();
}
@@ -635,8 +627,7 @@ QCursor::~QCursor()
QCursor &QCursor::operator=(const QCursor &c)
{
- if (!QCursorData::initialized)
- QCursorData::initialize();
+ QCursorData::initialize();
if (c.d)
c.d->ref.ref();
if (d && !d->ref.deref())
@@ -707,8 +698,7 @@ void QCursorData::initialize()
QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY, qreal devicePixelRatio)
{
- if (!QCursorData::initialized)
- QCursorData::initialize();
+ QCursorData::initialize();
if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) {
qWarning("QCursor: Cannot create bitmap cursor; invalid bitmap(s)");
QCursorData *c = qt_cursorTable[0];
diff --git a/src/gui/kernel/qcursor.h b/src/gui/kernel/qcursor.h
index 3c1b63277c..3aeef52094 100644
--- a/src/gui/kernel/qcursor.h
+++ b/src/gui/kernel/qcursor.h
@@ -51,7 +51,7 @@ public:
QCursor(const QCursor &cursor);
~QCursor();
QCursor &operator=(const QCursor &cursor);
- QCursor(QCursor &&other) noexcept : d(qExchange(other.d, nullptr)) {}
+ QCursor(QCursor &&other) noexcept : d(std::exchange(other.d, nullptr)) {}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QCursor)
void swap(QCursor &other) noexcept { qt_ptr_swap(d, other.d); }
@@ -62,7 +62,9 @@ public:
void setShape(Qt::CursorShape newShape);
#if QT_DEPRECATED_SINCE(6, 0)
+ QT_DEPRECATED_VERSION_X_6_0("Use the overload without argument instead.")
QBitmap bitmap(Qt::ReturnByValueConstant) const { return bitmap(); }
+ QT_DEPRECATED_VERSION_X_6_0("Use the overload without argument instead.")
QBitmap mask(Qt::ReturnByValueConstant) const { return mask(); }
#endif // QT_DEPRECATED_SINCE(6, 0)
QBitmap bitmap() const;
diff --git a/src/gui/kernel/qdnd_p.h b/src/gui/kernel/qdnd_p.h
index c863a63620..b5a2e015f4 100644
--- a/src/gui/kernel/qdnd_p.h
+++ b/src/gui/kernel/qdnd_p.h
@@ -27,6 +27,8 @@
#include "private/qobject_p.h"
#include "QtGui/qbackingstore.h"
+#include <QtCore/qpointer.h>
+
QT_REQUIRE_CONFIG(draganddrop);
QT_BEGIN_NAMESPACE
diff --git a/src/gui/kernel/qdrag.cpp b/src/gui/kernel/qdrag.cpp
index 10bf7f33f0..4e707e4915 100644
--- a/src/gui/kernel/qdrag.cpp
+++ b/src/gui/kernel/qdrag.cpp
@@ -9,6 +9,8 @@
#include <qpoint.h>
#include "qdnd_p.h"
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
/*!
@@ -64,7 +66,7 @@ QT_BEGIN_NAMESPACE
required.
\sa {Drag and Drop}, QClipboard, QMimeData, {Draggable Icons Example},
- {Draggable Text Example}, {Drop Site Example}, {Fridge Magnets Example}
+ {Draggable Text Example}, {Drop Site Example}
*/
/*!
diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp
index de55202d38..d8c11d72a6 100644
--- a/src/gui/kernel/qevent.cpp
+++ b/src/gui/kernel/qevent.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qevent.h"
+
#include "qcursor.h"
#include "private/qguiapplication_p.h"
#include "private/qinputdevice_p.h"
@@ -9,6 +10,7 @@
#include "qpa/qplatformintegration.h"
#include "private/qevent_p.h"
#include "private/qeventpoint_p.h"
+
#include "qfile.h"
#include "qhashfunctions.h"
#include "qmetaobject.h"
@@ -16,14 +18,35 @@
#include "qevent_p.h"
#include "qmath.h"
#include "qloggingcategory.h"
+#include "qpointer.h"
#if QT_CONFIG(draganddrop)
#include <qpa/qplatformdrag.h>
#include <private/qdnd_p.h>
#endif
+#if QT_CONFIG(shortcut)
+#include <private/qshortcut_p.h>
+#endif
+
#include <private/qdebug_p.h>
+#define Q_IMPL_POINTER_EVENT(Class) \
+ Class::Class(const Class &) = default; \
+ Class::~Class() = default; \
+ Class* Class::clone() const \
+ { \
+ auto c = new Class(*this); \
+ for (auto &point : c->m_points) \
+ QMutableEventPoint::detach(point); \
+ QEvent *e = c; \
+ /* check that covariant return is safe to add */ \
+ Q_ASSERT(reinterpret_cast<quintptr>(c) == reinterpret_cast<quintptr>(e)); \
+ return c; \
+ }
+
+
+
QT_BEGIN_NAMESPACE
static_assert(sizeof(QMutableTouchEvent) == sizeof(QTouchEvent));
@@ -55,7 +78,7 @@ QEnterEvent::QEnterEvent(const QPointF &localPos, const QPointF &scenePos, const
{
}
-Q_IMPL_EVENT_COMMON(QEnterEvent)
+Q_IMPL_POINTER_EVENT(QEnterEvent)
/*!
\fn QPoint QEnterEvent::globalPos() const
@@ -248,7 +271,7 @@ QPointerEvent::QPointerEvent(QEvent::Type type, QEvent::SinglePointEventTag, con
{
}
-Q_IMPL_EVENT_COMMON(QPointerEvent)
+Q_IMPL_POINTER_EVENT(QPointerEvent);
/*!
Returns the point whose \l {QEventPoint::id()}{id} matches the given \a id,
@@ -264,12 +287,13 @@ QEventPoint *QPointerEvent::pointById(int id)
}
/*!
- Returns \c true if every point in points() has an exclusiveGrabber().
+ Returns \c true if every point in points() has either an exclusiveGrabber()
+ or one or more passiveGrabbers().
*/
bool QPointerEvent::allPointsGrabbed() const
{
for (const auto &p : points()) {
- if (exclusiveGrabber(p) && passiveGrabbers(p).isEmpty())
+ if (!exclusiveGrabber(p) && passiveGrabbers(p).isEmpty())
return false;
}
return true;
@@ -550,7 +574,7 @@ QSinglePointEvent::QSinglePointEvent(QEvent::Type type, const QPointingDevice *d
m_points << point;
}
-Q_IMPL_EVENT_COMMON(QSinglePointEvent)
+Q_IMPL_POINTER_EVENT(QSinglePointEvent)
/*!
Returns \c true if this event represents a \l {button()}{button} being pressed.
@@ -739,9 +763,10 @@ QMouseEvent::QMouseEvent(QEvent::Type type, const QPointF &localPos, const QPoin
{
}
-Q_IMPL_EVENT_COMMON(QMouseEvent)
+Q_IMPL_POINTER_EVENT(QMouseEvent)
/*!
+ \fn Qt::MouseEventSource QMouseEvent::source() const
\since 5.3
\deprecated [6.0] Use pointingDevice() instead.
@@ -770,12 +795,13 @@ Q_IMPL_EVENT_COMMON(QMouseEvent)
decide how to react to this event. But it's even better to react to the
original event rather than handling only mouse events.
*/
-#if QT_DEPRECATED_SINCE(6, 0)
+// Note: the docs mention 6.0 as a deprecation version. That is correct and
+// intended, because we want our users to stop using it! Internally we will
+// deprecate it when we port our code away from using it.
Qt::MouseEventSource QMouseEvent::source() const
{
return Qt::MouseEventSource(m_source);
}
-#endif
/*!
\since 5.3
@@ -1022,15 +1048,15 @@ Qt::MouseEventFlags QMouseEvent::flags() const
The \a type parameter must be QEvent::HoverEnter,
QEvent::HoverLeave, or QEvent::HoverMove.
- The \a pos is the current mouse cursor's position relative to the
- receiving widget, \a oldPos is its previous such position, and
+ The \a scenePos is the current mouse cursor's position relative to the
+ receiving window or scene, \a oldPos is its previous such position, and
\a globalPos is the mouse position in absolute coordinates.
\a modifiers hold the state of all keyboard modifiers at the time
of the event.
*/
-QHoverEvent::QHoverEvent(Type type, const QPointF &pos, const QPointF &globalPos, const QPointF &oldPos,
+QHoverEvent::QHoverEvent(Type type, const QPointF &scenePos, const QPointF &globalPos, const QPointF &oldPos,
Qt::KeyboardModifiers modifiers, const QPointingDevice *device)
- : QSinglePointEvent(type, device, pos, pos, globalPos, Qt::NoButton, Qt::NoButton, modifiers), m_oldPos(oldPos)
+ : QSinglePointEvent(type, device, scenePos, scenePos, globalPos, Qt::NoButton, Qt::NoButton, modifiers), m_oldPos(oldPos)
{
}
@@ -1055,7 +1081,7 @@ QHoverEvent::QHoverEvent(Type type, const QPointF &pos, const QPointF &oldPos,
}
#endif
-Q_IMPL_EVENT_COMMON(QHoverEvent)
+Q_IMPL_POINTER_EVENT(QHoverEvent)
#if QT_CONFIG(wheelevent)
/*!
@@ -1180,7 +1206,7 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF &globalPos, QPoint pi
m_invertedScrolling = inverted;
}
-Q_IMPL_EVENT_COMMON(QWheelEvent)
+Q_IMPL_POINTER_EVENT(QWheelEvent)
/*!
Returns \c true if this event's phase() is Qt::ScrollBegin.
@@ -1417,12 +1443,13 @@ Q_IMPL_EVENT_COMMON(QKeyEvent)
Returns the Unicode text that this key generated.
- Return values when modifier keys such as
- Shift, Control, Alt, and Meta are pressed
- differ among platforms and could return an empty string.
+ The text is not limited to the printable range of Unicode
+ code points, and may include control characters or characters
+ from other Unicode categories, including QChar::Other_PrivateUse.
- \note \l key() will always return a valid value,
- independent of modifier keys.
+ The text may also be empty, for example when modifier keys such as
+ Shift, Control, Alt, and Meta are pressed (depending on the platform).
+ The key() function will always return a valid value.
\sa Qt::WA_KeyCompression
*/
@@ -1813,10 +1840,6 @@ Q_IMPL_EVENT_COMMON(QResizeEvent)
special handling, you should reimplement the event handler and
ignore() the event.
- The \l{mainwindows/application#close event handler}{closeEvent() in the
- Application example} shows a close event handler that
- asks whether to save a document before closing.
-
If you want the widget to be deleted when it is closed, create it
with the Qt::WA_DeleteOnClose flag. This is very useful for
independent top-level windows in a multi-window application.
@@ -2072,7 +2095,7 @@ QContextMenuEvent::QContextMenuEvent(Reason reason, const QPoint &pos)
has to be inserted to the widgets text directly before the preedit
string.
- If the commitString() should replace parts of the of the text in
+ If the commitString() should replace parts of the text in
the editor, replacementLength() will contain the number of
characters to be replaced. replacementStart() contains the position
at which characters are to be replaced relative from the start of
@@ -2238,7 +2261,7 @@ Q_IMPL_EVENT_COMMON(QInputMethodEvent)
result of the input operations and has to be inserted to the
widgets text directly before the preedit string.
- If the commit string should replace parts of the of the text in
+ If the commit string should replace parts of the text in
the editor, \a replaceLength specifies the number of
characters to be replaced. \a replaceFrom specifies the position
at which characters are to be replaced relative from the start of
@@ -2487,7 +2510,7 @@ QTabletEvent::QTabletEvent(Type type, const QPointingDevice *dev, const QPointF
QMutableEventPoint::setRotation(p, rotation);
}
-Q_IMPL_EVENT_COMMON(QTabletEvent)
+Q_IMPL_POINTER_EVENT(QTabletEvent)
/*!
\fn qreal QTabletEvent::tangentialPressure() const
@@ -2811,7 +2834,7 @@ QNativeGestureEvent::QNativeGestureEvent(Qt::NativeGestureType type, const QPoin
Q_ASSERT(fingerCount < 16); // we store it in 4 bits unsigned
}
-Q_IMPL_EVENT_COMMON(QNativeGestureEvent)
+Q_IMPL_POINTER_EVENT(QNativeGestureEvent)
/*!
\fn QNativeGestureEvent::gestureType() const
@@ -3071,10 +3094,24 @@ void QDropEvent::setDropAction(Qt::DropAction action)
*/
/*!
+ \fn QPointF QDropEvent::position() const
+ \since 6.0
+
+ Returns the position where the drop was made.
+*/
+
+/*!
\fn Qt::MouseButtons QDropEvent::mouseButtons() const
\deprecated [6.0] Use buttons() instead.
- Returns the mouse buttons that are pressed..
+ Returns the mouse buttons that are pressed.
+*/
+
+/*!
+ \fn Qt::MouseButtons QDropEvent::buttons() const
+ \since 6.0
+
+ Returns the mouse buttons that are pressed.
*/
/*!
@@ -3085,6 +3122,13 @@ void QDropEvent::setDropAction(Qt::DropAction action)
*/
/*!
+ \fn Qt::KeyboardModifiers QDropEvent::modifiers() const
+ \since 6.0
+
+ Returns the modifier keys that are pressed.
+*/
+
+/*!
\fn void QDropEvent::setDropAction(Qt::DropAction action)
Sets the \a action to be performed on the data by the target.
@@ -3549,10 +3593,15 @@ Q_IMPL_EVENT_COMMON(QShowEvent)
\snippet qfileopenevent/Info.plist Custom Info.plist
- The following implementation of a QApplication subclass prints the path to
- the file that was, for example, dropped on the Dock icon of the application.
+ The following implementation of a QApplication subclass shows how to handle
+ QFileOpenEvent to open the file that was, for example, dropped on the Dock
+ icon of the application.
\snippet qfileopenevent/main.cpp QApplication subclass
+
+ Note how \c{QFileOpenEvent::file()} is not guaranteed to be the name of a
+ local file that can be opened using QFile. The contents of the string depend
+ on the source application.
*/
/*!
@@ -3580,19 +3629,23 @@ Q_IMPL_EVENT_COMMON(QFileOpenEvent)
/*!
\fn QString QFileOpenEvent::file() const
- Returns the file that is being opened.
+ Returns the name of the file that the application should open.
+
+ This is not guaranteed to be the path to a local file.
*/
/*!
\fn QUrl QFileOpenEvent::url() const
- Returns the url that is being opened.
+ Returns the url that the application should open.
\since 4.6
*/
+#if QT_DEPRECATED_SINCE(6, 6)
/*!
\fn bool QFileOpenEvent::openFile(QFile &file, QIODevice::OpenMode flags) const
+ \deprecated [6.6] interpret the string returned by file()
Opens a QFile on the \a file referenced by this event in the mode specified
by \a flags. Returns \c true if successful; otherwise returns \c false.
@@ -3607,6 +3660,7 @@ bool QFileOpenEvent::openFile(QFile &file, QIODevice::OpenMode flags) const
file.setFileName(m_file);
return file.open(flags);
}
+#endif
#ifndef QT_NO_TOOLBAR
/*!
@@ -3658,6 +3712,8 @@ Q_IMPL_EVENT_COMMON(QToolBarChangeEvent)
Constructs a shortcut event for the given \a key press,
associated with the QShortcut ID \a id.
+ \deprecated use the other constructor
+
\a ambiguous specifies whether there is more than one QShortcut
for the same key sequence.
*/
@@ -3666,6 +3722,28 @@ QShortcutEvent::QShortcutEvent(const QKeySequence &key, int id, bool ambiguous)
{
}
+/*!
+ Constructs a shortcut event for the given \a key press,
+ associated with the QShortcut \a shortcut.
+ \since 6.5
+
+ \a ambiguous specifies whether there is more than one QShortcut
+ for the same key sequence.
+*/
+QShortcutEvent::QShortcutEvent(const QKeySequence &key, const QShortcut *shortcut, bool ambiguous)
+ : QEvent(Shortcut), m_sequence(key), m_shortcutId(0), m_ambiguous(ambiguous)
+{
+ if (shortcut) {
+ auto priv = static_cast<const QShortcutPrivate *>(QShortcutPrivate::get(shortcut));
+ auto index = priv->sc_sequences.indexOf(key);
+ if (index < 0) {
+ qWarning() << "Given QShortcut does not contain key-sequence " << key;
+ return;
+ }
+ m_shortcutId = priv->sc_ids[index];
+ }
+}
+
Q_IMPL_EVENT_COMMON(QShortcutEvent)
#endif // QT_CONFIG(shortcut)
@@ -3693,6 +3771,13 @@ static void formatUnicodeString(QDebug d, const QString &s)
d << Qt::dec << '"';
}
+static QDebug operator<<(QDebug dbg, const QInputMethodEvent::Attribute &attr)
+{
+ dbg << "[type= " << attr.type << ", start=" << attr.start << ", length=" << attr.length
+ << ", value=" << attr.value << ']';
+ return dbg;
+}
+
static inline void formatInputMethodEvent(QDebug d, const QInputMethodEvent *e)
{
d << "QInputMethodEvent(";
@@ -3708,15 +3793,15 @@ static inline void formatInputMethodEvent(QDebug d, const QInputMethodEvent *e)
d << ", replacementStart=" << e->replacementStart() << ", replacementLength="
<< e->replacementLength();
}
- if (const int attributeCount = e->attributes().size()) {
+ const auto attributes = e->attributes();
+ auto it = attributes.cbegin();
+ const auto end = attributes.cend();
+ if (it != end) {
d << ", attributes= {";
- for (int a = 0; a < attributeCount; ++a) {
- const QInputMethodEvent::Attribute &at = e->attributes().at(a);
- if (a)
- d << ',';
- d << "[type= " << at.type << ", start=" << at.start << ", length=" << at.length
- << ", value=" << at.value << ']';
- }
+ d << *it;
+ ++it;
+ for (; it != end; ++it)
+ d << ',' << *it;
d << '}';
}
d << ')';
@@ -3968,10 +4053,7 @@ QDebug operator<<(QDebug dbg, const QEvent *e)
bool isMouse = false;
switch (type) {
case QEvent::Expose:
-QT_WARNING_PUSH
-QT_WARNING_DISABLE_DEPRECATED
- dbg << "QExposeEvent(" << static_cast<const QExposeEvent *>(e)->region() << ')';
-QT_WARNING_POP
+ dbg << "QExposeEvent()";
break;
case QEvent::Paint:
dbg << "QPaintEvent(" << static_cast<const QPaintEvent *>(e)->region() << ')';
@@ -4046,6 +4128,10 @@ QT_WARNING_POP
dbg << ", text=" << ke->text();
if (ke->isAutoRepeat())
dbg << ", autorepeat, count=" << ke->count();
+ if (dbg.verbosity() > QDebug::DefaultVerbosity) {
+ dbg << ", nativeScanCode=" << ke->nativeScanCode();
+ dbg << ", nativeVirtualKey=" << ke->nativeVirtualKey();
+ }
dbg << ')';
}
break;
@@ -4212,6 +4298,8 @@ QT_WARNING_POP
/*!
\fn int QShortcutEvent::shortcutId() const
+ \deprecated
+
Returns the ID of the QShortcut object for which this event was
generated.
@@ -4390,8 +4478,6 @@ Q_IMPL_EVENT_COMMON(QWindowStateChangeEvent)
*/
/*!
- \deprecated [6.2] Use another constructor.
-
Constructs a QTouchEvent with the given \a eventType, \a device,
\a touchPoints, and current keyboard \a modifiers at the time of the event.
*/
@@ -4409,6 +4495,7 @@ QTouchEvent::QTouchEvent(QEvent::Type eventType,
}
}
+#if QT_DEPRECATED_SINCE(6, 0)
/*!
\deprecated [6.0] Use another constructor.
@@ -4428,8 +4515,9 @@ QTouchEvent::QTouchEvent(QEvent::Type eventType,
for (QEventPoint &point : m_points)
QMutableEventPoint::setDevice(point, device);
}
+#endif // QT_DEPRECATED_SINCE(6, 0)
-Q_IMPL_EVENT_COMMON(QTouchEvent)
+Q_IMPL_POINTER_EVENT(QTouchEvent)
/*!
Returns true if this event includes at least one newly-pressed touchpoint.
@@ -4669,6 +4757,46 @@ Q_IMPL_EVENT_COMMON(QApplicationStateChangeEvent)
Returns the state of the application.
*/
+/*!
+ \class QChildWindowEvent
+ \inmodule QtGui
+ \since 6.7
+ \brief The QChildWindowEvent class contains event parameters for
+ child window changes.
+
+ \ingroup events
+
+ Child window events are sent to windows when children are
+ added or removed.
+
+ In both cases you can only rely on the child being a QWindow
+ — not any subclass thereof. This is because in the
+ QEvent::ChildWindowAdded case the subclass is not yet fully
+ constructed, and in the QEvent::ChildWindowRemoved case it
+ might have already been destructed.
+*/
+
+/*!
+ Constructs a child window event object of a particular \a type
+ for the \a childWindow.
+
+ \a type can be QEvent::ChildWindowAdded or QEvent::ChildWindowRemoved.
+
+ \sa child()
+*/
+QChildWindowEvent::QChildWindowEvent(Type type, QWindow *childWindow)
+ : QEvent(type), c(childWindow)
+{
+}
+
+Q_IMPL_EVENT_COMMON(QChildWindowEvent)
+
+/*!
+ \fn QWindow *QChildWindowEvent::child() const
+
+ Returns the child window that was added or removed.
+*/
+
QMutableTouchEvent::~QMutableTouchEvent()
= default;
diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h
index 983cc6a9da..a24f0c471c 100644
--- a/src/gui/kernel/qevent.h
+++ b/src/gui/kernel/qevent.h
@@ -14,7 +14,6 @@
#include <QtCore/qiodevice.h>
#include <QtCore/qlist.h>
#include <QtCore/qnamespace.h>
-#include <QtCore/qpointer.h>
#include <QtCore/qstring.h>
#include <QtCore/qurl.h>
#include <QtCore/qvariant.h>
@@ -34,8 +33,12 @@ QT_BEGIN_NAMESPACE
class QFile;
class QAction;
class QMouseEvent;
+template <typename T> class QPointer;
class QPointerEvent;
class QScreen;
+#if QT_CONFIG(shortcut)
+class QShortcut;
+#endif
class QTabletEvent;
class QTouchEvent;
#if QT_CONFIG(gestures)
@@ -68,6 +71,7 @@ protected:
class Q_GUI_EXPORT QPointerEvent : public QInputEvent
{
+ Q_GADGET
Q_DECL_EVENT_COMMON(QPointerEvent)
public:
explicit QPointerEvent(Type type, const QPointingDevice *dev,
@@ -78,7 +82,7 @@ public:
return pointingDevice() ? pointingDevice()->pointerType() : QPointingDevice::PointerType::Unknown;
}
void setTimestamp(quint64 timestamp) override;
- qsizetype pointCount() const { return m_points.count(); }
+ qsizetype pointCount() const { return m_points.size(); }
QEventPoint &point(qsizetype i);
const QList<QEventPoint> &points() const { return m_points; }
QEventPoint *pointById(int id);
@@ -233,8 +237,8 @@ public:
QPointF windowPos() const { return scenePosition(); }
QT_DEPRECATED_VERSION_X_6_0("Use globalPosition()")
QPointF screenPos() const { return globalPosition(); }
- Qt::MouseEventSource source() const;
#endif // QT_DEPRECATED_SINCE(6, 0)
+ Qt::MouseEventSource source() const;
Qt::MouseEventFlags flags() const;
};
@@ -242,7 +246,7 @@ class Q_GUI_EXPORT QHoverEvent : public QSinglePointEvent
{
Q_DECL_EVENT_COMMON(QHoverEvent)
public:
- QHoverEvent(Type type, const QPointF &pos, const QPointF &globalPos, const QPointF &oldPos,
+ QHoverEvent(Type type, const QPointF &scenePos, const QPointF &globalPos, const QPointF &oldPos,
Qt::KeyboardModifiers modifiers = Qt::NoModifier,
const QPointingDevice *device = QPointingDevice::primaryPointingDevice());
#if QT_DEPRECATED_SINCE(6, 3)
@@ -520,6 +524,7 @@ public:
protected:
QRegion m_region;
+ friend class QWidgetWindow;
};
class Q_GUI_EXPORT QPlatformSurfaceEvent : public QEvent
@@ -848,7 +853,10 @@ public:
inline QString file() const { return m_file; }
QUrl url() const { return m_url; }
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_DEPRECATED_VERSION_X_6_6("Interpret the string returned by file()")
bool openFile(QFile &file, QIODevice::OpenMode flags) const;
+#endif
private:
QString m_file;
QUrl m_url;
@@ -872,9 +880,12 @@ class Q_GUI_EXPORT QShortcutEvent : public QEvent
{
Q_DECL_EVENT_COMMON(QShortcutEvent)
public:
+ // Note this is publicly deprecated, but should remain as internal constructor:
QShortcutEvent(const QKeySequence &key, int id, bool ambiguous = false);
+ QShortcutEvent(const QKeySequence &key, const QShortcut *shortcut = nullptr, bool ambiguous = false);
inline const QKeySequence &key() const { return m_sequence; }
+ // Note this is publicly deprecated, but should remain as internal getter:
inline int shortcutId() const { return m_shortcutId; }
inline bool isAmbiguous() const { return m_ambiguous; }
protected:
@@ -1010,6 +1021,17 @@ private:
Qt::ApplicationState m_applicationState;
};
+class Q_GUI_EXPORT QChildWindowEvent : public QEvent
+{
+ Q_DECL_EVENT_COMMON(QChildWindowEvent)
+public:
+ explicit QChildWindowEvent(Type type, QWindow *childWindow);
+ QWindow *child() const { return c; }
+
+private:
+ QWindow *c;
+};
+
QT_END_NAMESPACE
#endif // QEVENT_H
diff --git a/src/gui/kernel/qeventpoint.cpp b/src/gui/kernel/qeventpoint.cpp
index 4030ebce0c..66d4f44ea0 100644
--- a/src/gui/kernel/qeventpoint.cpp
+++ b/src/gui/kernel/qeventpoint.cpp
@@ -339,7 +339,7 @@ ulong QEventPoint::pressTimestamp() const
/*!
\property QEventPoint::timeHeld
- \brief the duration, in milliseconds, since this point was pressed and not released.
+ \brief the duration, in seconds, since this point was pressed and not released.
\sa pressTimestamp, timestamp
*/
@@ -430,6 +430,7 @@ QPointF QEventPoint::normalizedPosition() const
return (globalPosition() - geom.topLeft()) / geom.width();
}
+#if QT_DEPRECATED_SINCE(6, 0)
/*!
\deprecated [6.0] Use globalPressPosition() instead.
@@ -467,7 +468,7 @@ QPointF QEventPoint::lastNormalizedPos() const
return QPointF();
return (globalLastPosition() - geom.topLeft()) / geom.width();
}
-
+#endif // QT_DEPRECATED_SINCE(6, 0)
/*! \internal
This class is explicitly shared, which means if you construct an event and
@@ -535,6 +536,7 @@ void QMutableEventPoint::update(const QEventPoint &other, QEventPoint &target)
setEllipseDiameters(target, other.ellipseDiameters());
setRotation(target, other.rotation());
setVelocity(target, other.velocity());
+ setUniqueId(target, other.uniqueId()); // for TUIO
}
/*! \internal
diff --git a/src/gui/kernel/qeventpoint.h b/src/gui/kernel/qeventpoint.h
index 35b8b1f73d..518faecee2 100644
--- a/src/gui/kernel/qeventpoint.h
+++ b/src/gui/kernel/qeventpoint.h
@@ -20,7 +20,6 @@ class Q_GUI_EXPORT QEventPoint
{
Q_GADGET
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
- QDOC_PROPERTY(QPointingDevice *device READ device CONSTANT) // qdoc doesn't know const
Q_PROPERTY(const QPointingDevice *device READ device CONSTANT)
Q_PROPERTY(int id READ id CONSTANT)
Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId CONSTANT)
diff --git a/src/gui/kernel/qeventpoint_p.h b/src/gui/kernel/qeventpoint_p.h
index f70c285e3e..c339c7e3e1 100644
--- a/src/gui/kernel/qeventpoint_p.h
+++ b/src/gui/kernel/qeventpoint_p.h
@@ -17,7 +17,9 @@
#include <QtGui/private/qtguiglobal_p.h>
#include <QtGui/qevent.h>
+
#include <QtCore/qloggingcategory.h>
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
diff --git a/src/gui/kernel/qgenericpluginfactory.cpp b/src/gui/kernel/qgenericpluginfactory.cpp
index 3155b10456..5ce13ad5e1 100644
--- a/src/gui/kernel/qgenericpluginfactory.cpp
+++ b/src/gui/kernel/qgenericpluginfactory.cpp
@@ -12,7 +12,7 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, gpLoader,
(QGenericPluginFactoryInterface_iid, "/generic"_L1, Qt::CaseInsensitive))
/*!
@@ -34,7 +34,7 @@ Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
*/
QObject *QGenericPluginFactory::create(const QString& key, const QString &specification)
{
- return qLoadPlugin<QObject, QGenericPlugin>(loader(), key.toLower(), specification);
+ return qLoadPlugin<QObject, QGenericPlugin>(gpLoader(), key.toLower(), specification);
}
/*!
@@ -49,7 +49,7 @@ QStringList QGenericPluginFactory::keys()
typedef QMultiMap<int, QString> PluginKeyMap;
typedef PluginKeyMap::const_iterator PluginKeyMapConstIterator;
- const PluginKeyMap keyMap = loader()->keyMap();
+ const PluginKeyMap keyMap = gpLoader()->keyMap();
const PluginKeyMapConstIterator cend = keyMap.constEnd();
for (PluginKeyMapConstIterator it = keyMap.constBegin(); it != cend; ++it)
if (!list.contains(it.value()))
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index dbe50aee82..444091afef 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -9,6 +9,7 @@
#include <qpa/qplatformintegrationfactory_p.h>
#include "private/qevent_p.h"
#include "private/qeventpoint_p.h"
+#include "private/qiconloader_p.h"
#include "qfont.h"
#include "qpointingdevice.h"
#include <qpa/qplatformfontdatabase.h>
@@ -16,12 +17,15 @@
#include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatformtheme.h>
#include <qpa/qplatformintegration.h>
+#include <qpa/qplatformkeymapper.h>
#include <QtCore/QAbstractEventDispatcher>
+#include <QtCore/QFileInfo>
#include <QtCore/QStandardPaths>
#include <QtCore/QVariant>
#include <QtCore/private/qcoreapplication_p.h>
#include <QtCore/private/qabstracteventdispatcher_p.h>
+#include <QtCore/private/qminimalflatset_p.h>
#include <QtCore/qmutex.h>
#include <QtCore/private/qthread_p.h>
#include <QtCore/private/qlocking_p.h>
@@ -41,6 +45,7 @@
#include <QtGui/qgenericpluginfactory.h>
#include <QtGui/qstylehints.h>
+#include <QtGui/private/qstylehints_p.h>
#include <QtGui/qinputmethod.h>
#include <QtGui/qpixmapcache.h>
#include <qpa/qplatforminputcontext.h>
@@ -49,8 +54,11 @@
#include <qpa/qwindowsysteminterface.h>
#include <qpa/qwindowsysteminterface_p.h>
#include "private/qwindow_p.h"
+#include "private/qicon_p.h"
#include "private/qcursor_p.h"
-#include "private/qopenglcontext_p.h"
+#if QT_CONFIG(opengl)
+# include "private/qopenglcontext_p.h"
+#endif
#include "private/qinputdevicemanager_p.h"
#include "private/qinputmethod_p.h"
#include "private/qpointingdevice_p.h"
@@ -93,12 +101,14 @@
#include <qtgui_tracepoints_p.h>
-#include <ctype.h>
+#include <private/qtools_p.h>
+
#include <limits>
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
+using namespace QtMiscUtils;
// Helper macro for static functions to check on the existence of the application class.
#define CHECK_QAPP_INSTANCE(...) \
@@ -226,7 +236,7 @@ static void initThemeHints()
static bool checkNeedPortalSupport()
{
#if QT_CONFIG(dbus)
- return !QStandardPaths::locate(QStandardPaths::RuntimeLocation, "flatpak-info"_L1).isEmpty() || qEnvironmentVariableIsSet("SNAP");
+ return QFileInfo::exists("/.flatpak-info"_L1) || qEnvironmentVariableIsSet("SNAP");
#else
return false;
#endif // QT_CONFIG(dbus)
@@ -253,20 +263,20 @@ struct QWindowGeometrySpecification
static inline int nextGeometryToken(const QByteArray &a, int &pos, char *op)
{
*op = 0;
- const int size = a.size();
+ const qsizetype size = a.size();
if (pos >= size)
return -1;
*op = a.at(pos);
if (*op == '+' || *op == '-' || *op == 'x')
pos++;
- else if (isdigit(*op))
+ else if (isAsciiDigit(*op))
*op = 'x'; // If it starts with a digit, it is supposed to be a width specification.
else
return -1;
const int numberPos = pos;
- for ( ; pos < size && isdigit(a.at(pos)); ++pos) ;
+ for ( ; pos < size && isAsciiDigit(a.at(pos)); ++pos) ;
bool ok;
const int result = a.mid(numberPos, pos - numberPos).toInt(&ok);
@@ -554,10 +564,12 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME
\list
\li \c {altgr}, detect the key \c {AltGr} found on some keyboards as
Qt::GroupSwitchModifier (since Qt 5.12).
- \li \c {darkmode=[1|2]} controls how Qt responds to the activation
+ \li \c {darkmode=[0|1|2]} controls how Qt responds to the activation
of the \e{Dark Mode for applications} introduced in Windows 10
1903 (since Qt 5.15).
+ A value of 0 disables dark mode support.
+
A value of 1 causes Qt to switch the window borders to black
when \e{Dark Mode for applications} is activated and no High
Contrast Theme is in use. This is intended for applications
@@ -569,17 +581,17 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME
experimental pending the introduction of new style that
properly adapts to dark mode.
+ As of Qt 6.5, the default value is 2; to disable dark mode
+ support, set the value to 0 or 1.
+
\li \c {dialogs=[xp|none]}, \c xp uses XP-style native dialogs and
\c none disables them.
\li \c {fontengine=freetype}, uses the FreeType font engine.
- \li \c {fontengine=directwrite}, uses the experimental DirectWrite
- font database and defaults to using the DirectWrite font
+ \li \c {fontengine=gdi}, uses the legacy GDI-based
+ font database and defaults to using the GDI font
engine (which is otherwise only used for some font types
- or font properties.) This affects font selection and aims
- to provide font naming more consistent with other platforms,
- but does not support all font formats, such as Postscript
- Type-1 or Microsoft FNT fonts.
+ or font properties.) (Since Qt 6.8).
\li \c {menus=[native|none]}, controls the use of native menus.
Native menus are implemented using Win32 API and are simpler than
@@ -593,7 +605,8 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME
\li \c {nocolorfonts} Turn off DirectWrite Color fonts
(since Qt 5.8).
- \li \c {nodirectwrite} Turn off DirectWrite fonts (since Qt 5.8).
+ \li \c {nodirectwrite} Turn off DirectWrite fonts (since Qt 5.8). This implicitly
+ also selects the GDI font engine.
\li \c {nomousefromtouch} Ignores mouse events synthesized
from touch events by the operating system.
@@ -682,6 +695,7 @@ QGuiApplication::~QGuiApplication()
QGuiApplicationPrivate::lastCursorPosition.reset();
QGuiApplicationPrivate::currentMousePressWindow = QGuiApplicationPrivate::currentMouseWindow = nullptr;
QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive;
+ QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy = Qt::HighDpiScaleFactorRoundingPolicy::PassThrough;
QGuiApplicationPrivate::currentDragWindow = nullptr;
QGuiApplicationPrivate::tabletDevicePoints.clear();
}
@@ -736,13 +750,37 @@ QString QGuiApplication::applicationDisplayName()
}
/*!
+ Sets the application's badge to \a number.
+
+ Useful for providing feedback to the user about the number
+ of unread messages or similar.
+
+ The badge will be overlaid on the application's icon in the Dock
+ on \macos, the home screen icon on iOS, or the task bar on Windows
+ and Linux.
+
+ If the number is outside the range supported by the platform, the
+ number will be clamped to the supported range. If the number does
+ not fit within the badge, the number may be visually elided.
+
+ Setting the number to 0 will clear the badge.
+
+ \since 6.5
+ \sa applicationName
+*/
+void QGuiApplication::setBadgeNumber(qint64 number)
+{
+ QGuiApplicationPrivate::platformIntegration()->setApplicationBadge(number);
+}
+
+/*!
\property QGuiApplication::desktopFileName
\brief the base name of the desktop entry for this application
\since 5.7
- This is the file name, without the full path, of the desktop entry
- that represents this application according to the freedesktop desktop
- entry specification.
+ This is the file name, without the full path or the trailing ".desktop"
+ extension of the desktop entry that represents this application
+ according to the freedesktop desktop entry specification.
This property gives a precise indication of what desktop entry represents
the application and it is needed by the windowing system to retrieve
@@ -756,6 +794,15 @@ void QGuiApplication::setDesktopFileName(const QString &name)
if (!QGuiApplicationPrivate::desktopFileName)
QGuiApplicationPrivate::desktopFileName = new QString;
*QGuiApplicationPrivate::desktopFileName = name;
+ if (name.endsWith(QLatin1String(".desktop"))) { // ### Qt 7: remove
+ const QString filePath = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, name);
+ if (!filePath.isEmpty()) {
+ qWarning("QGuiApplication::setDesktopFileName: the specified desktop file name "
+ "ends with .desktop. For compatibility reasons, the .desktop suffix will "
+ "be removed. Please specify a desktop file name without .desktop suffix");
+ (*QGuiApplicationPrivate::desktopFileName).chop(8);
+ }
+ }
}
QString QGuiApplication::desktopFileName()
@@ -782,7 +829,7 @@ QWindow *QGuiApplication::modalWindow()
CHECK_QAPP_INSTANCE(nullptr)
if (QGuiApplicationPrivate::self->modalWindowList.isEmpty())
return nullptr;
- return QGuiApplicationPrivate::self->modalWindowList.first();
+ return QGuiApplicationPrivate::self->modalWindowList.constFirst();
}
static void updateBlockedStatusRecursion(QWindow *window, bool shouldBeBlocked)
@@ -833,7 +880,7 @@ void QGuiApplicationPrivate::showModalWindow(QWindow *modal)
}
}
- for (QWindow *window : qAsConst(QGuiApplicationPrivate::window_list)) {
+ for (QWindow *window : std::as_const(QGuiApplicationPrivate::window_list)) {
if (needsWindowBlockedEvent(window) && !window->d_func()->blockedByModalWindow)
updateBlockedStatus(window);
}
@@ -845,12 +892,23 @@ void QGuiApplicationPrivate::hideModalWindow(QWindow *window)
{
self->modalWindowList.removeAll(window);
- for (QWindow *window : qAsConst(QGuiApplicationPrivate::window_list)) {
+ for (QWindow *window : std::as_const(QGuiApplicationPrivate::window_list)) {
if (needsWindowBlockedEvent(window) && window->d_func()->blockedByModalWindow)
updateBlockedStatus(window);
}
}
+Qt::WindowModality QGuiApplicationPrivate::defaultModality() const
+{
+ return Qt::NonModal;
+}
+
+bool QGuiApplicationPrivate::windowNeverBlocked(QWindow *window) const
+{
+ Q_UNUSED(window);
+ return false;
+}
+
/*
Returns \c true if \a window is blocked by a modal window. If \a
blockingWindow is non-zero, *blockingWindow will be set to the blocking
@@ -858,55 +916,40 @@ void QGuiApplicationPrivate::hideModalWindow(QWindow *window)
*/
bool QGuiApplicationPrivate::isWindowBlocked(QWindow *window, QWindow **blockingWindow) const
{
+ Q_ASSERT_X(window, Q_FUNC_INFO, "The window must not be null");
+
QWindow *unused = nullptr;
if (!blockingWindow)
blockingWindow = &unused;
+ *blockingWindow = nullptr;
- if (modalWindowList.isEmpty()) {
- *blockingWindow = nullptr;
+ if (modalWindowList.isEmpty() || windowNeverBlocked(window))
return false;
- }
- for (int i = 0; i < modalWindowList.count(); ++i) {
+ for (int i = 0; i < modalWindowList.size(); ++i) {
QWindow *modalWindow = modalWindowList.at(i);
// A window is not blocked by another modal window if the two are
// the same, or if the window is a child of the modal window.
- if (window == modalWindow || modalWindow->isAncestorOf(window, QWindow::IncludeTransients)) {
- *blockingWindow = nullptr;
+ if (window == modalWindow || modalWindow->isAncestorOf(window, QWindow::IncludeTransients))
return false;
- }
- Qt::WindowModality windowModality = modalWindow->modality();
- switch (windowModality) {
+ switch (modalWindow->modality() == Qt::NonModal ? defaultModality()
+ : modalWindow->modality()) {
case Qt::ApplicationModal:
- {
- if (modalWindow != window) {
- *blockingWindow = modalWindow;
- return true;
- }
- break;
- }
- case Qt::WindowModal:
- {
- QWindow *w = window;
+ *blockingWindow = modalWindow;
+ return true;
+ case Qt::WindowModal: {
+ // Find the nearest ancestor of window which is also an ancestor of modal window to
+ // determine if the modal window blocks the window.
+ auto *current = window;
do {
- QWindow *m = modalWindow;
- do {
- if (m == w) {
- *blockingWindow = m;
- return true;
- }
- QWindow *p = m->parent();
- if (!p)
- p = m->transientParent();
- m = p;
- } while (m);
- QWindow *p = w->parent();
- if (!p)
- p = w->transientParent();
- w = p;
- } while (w);
+ if (current->isAncestorOf(modalWindow, QWindow::IncludeTransients)) {
+ *blockingWindow = modalWindow;
+ return true;
+ }
+ current = current->parent(QWindow::IncludeTransients);
+ } while (current);
break;
}
default:
@@ -914,13 +957,14 @@ bool QGuiApplicationPrivate::isWindowBlocked(QWindow *window, QWindow **blocking
break;
}
}
- *blockingWindow = nullptr;
return false;
}
/*!
Returns the QWindow that receives events tied to focus,
such as key events.
+
+ \sa QWindow::requestActivate()
*/
QWindow *QGuiApplication::focusWindow()
{
@@ -1098,7 +1142,7 @@ qreal QGuiApplication::devicePixelRatio() const
return QGuiApplicationPrivate::m_maxDevicePixelRatio;
QGuiApplicationPrivate::m_maxDevicePixelRatio = 1.0; // make sure we never return 0.
- for (QScreen *screen : qAsConst(QGuiApplicationPrivate::screen_list))
+ for (QScreen *screen : std::as_const(QGuiApplicationPrivate::screen_list))
QGuiApplicationPrivate::m_maxDevicePixelRatio = qMax(QGuiApplicationPrivate::m_maxDevicePixelRatio, screen->devicePixelRatio());
return QGuiApplicationPrivate::m_maxDevicePixelRatio;
@@ -1145,19 +1189,31 @@ QWindow *QGuiApplication::topLevelAt(const QPoint &pos)
\li \c offscreen
\li \c qnx
\li \c windows
- \li \c wayland is a platform plugin for modern Linux desktops and some
- embedded systems.
- \li \c xcb is the X11 plugin used on regular desktop Linux platforms.
+ \li \c wayland is a platform plugin for the Wayland display server protocol,
+ used on some Linux desktops and embedded systems.
+ \li \c xcb is a plugin for the X11 window system, used on some desktop Linux platforms.
\endlist
+ \note Calling this function without a QGuiApplication will return the default
+ platform name, if available. The default platform name is not affected by the
+ \c{-platform} command line option, or the \c QT_QPA_PLATFORM environment variable.
+
For more information about the platform plugins for embedded Linux devices,
see \l{Qt for Embedded Linux}.
*/
QString QGuiApplication::platformName()
{
- return QGuiApplicationPrivate::platform_name ?
- *QGuiApplicationPrivate::platform_name : QString();
+ if (!QGuiApplication::instance()) {
+#ifdef QT_QPA_DEFAULT_PLATFORM_NAME
+ return QStringLiteral(QT_QPA_DEFAULT_PLATFORM_NAME);
+#else
+ return QString();
+#endif
+ } else {
+ return QGuiApplicationPrivate::platform_name ?
+ *QGuiApplicationPrivate::platform_name : QString();
+ }
}
Q_LOGGING_CATEGORY(lcQpaPluginLoading, "qt.qpa.plugin");
@@ -1192,6 +1248,10 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString
QGuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, arguments, argc, argv, platformPluginPath);
if (Q_UNLIKELY(!QGuiApplicationPrivate::platform_integration)) {
if (availablePlugins.contains(name)) {
+ if (name == QStringLiteral("xcb") && QVersionNumber::compare(QLibraryInfo::version(), QVersionNumber(6, 5, 0)) >= 0) {
+ qCWarning(lcQpaPluginLoading).nospace().noquote()
+ << "From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin.";
+ }
qCInfo(lcQpaPluginLoading).nospace().noquote()
<< "Could not load the Qt platform plugin \"" << name << "\" in \""
<< QDir::toNativeSeparators(platformPluginPath) << "\" even though it was found.";
@@ -1213,7 +1273,7 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString
"Reinstalling the application may fix this problem.\n");
if (!availablePlugins.isEmpty())
- fatalMessage += QStringLiteral("\nAvailable platform plugins are: %1.\n").arg(availablePlugins.join(", "_L1));
+ fatalMessage += "\nAvailable platform plugins are: %1.\n"_L1.arg(availablePlugins.join(", "_L1));
#if defined(Q_OS_WIN)
// Windows: Display message box unless it is a console application
@@ -1246,7 +1306,7 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString
qCDebug(lcQpaTheme) << "Adding platform integration's theme names to list of theme names:" << platformIntegrationThemeNames;
themeNames += platformIntegrationThemeNames;
// 4) Look for a theme plugin.
- for (const QString &themeName : qAsConst(themeNames)) {
+ for (const QString &themeName : std::as_const(themeNames)) {
qCDebug(lcQpaTheme) << "Attempting to create platform theme" << themeName << "via QPlatformThemeFactory::create";
QGuiApplicationPrivate::platform_theme = QPlatformThemeFactory::create(themeName, platformPluginPath);
if (QGuiApplicationPrivate::platform_theme) {
@@ -1258,7 +1318,7 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString
// 5) If no theme plugin was found ask the platform integration to
// create a theme
if (!QGuiApplicationPrivate::platform_theme) {
- for (const QString &themeName : qAsConst(themeNames)) {
+ for (const QString &themeName : std::as_const(themeNames)) {
qCDebug(lcQpaTheme) << "Attempting to create platform theme" << themeName << "via createPlatformTheme";
QGuiApplicationPrivate::platform_theme = QGuiApplicationPrivate::platform_integration->createPlatformTheme(themeName);
if (QGuiApplicationPrivate::platform_theme) {
@@ -1279,8 +1339,8 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString
// boolean 'foo' or strings: 'foo=bar'
if (!platformArguments.isEmpty()) {
if (QObject *nativeInterface = QGuiApplicationPrivate::platform_integration->nativeInterface()) {
- for (const QString &argument : qAsConst(platformArguments)) {
- const int equalsPos = argument.indexOf(u'=');
+ for (const QString &argument : std::as_const(platformArguments)) {
+ const qsizetype equalsPos = argument.indexOf(u'=');
const QByteArray name =
equalsPos != -1 ? argument.left(equalsPos).toUtf8() : argument.toUtf8();
const QVariant value =
@@ -1293,14 +1353,14 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString
const auto platformIntegration = QGuiApplicationPrivate::platformIntegration();
fontSmoothingGamma = platformIntegration->styleHint(QPlatformIntegration::FontSmoothingGamma).toReal();
QCoreApplication::setAttribute(Qt::AA_DontShowShortcutsInContextMenus,
- !platformIntegration->styleHint(QPlatformIntegration::ShowShortcutsInContextMenus).toBool());
+ !QGuiApplication::styleHints()->showShortcutsInContextMenus());
}
static void init_plugins(const QList<QByteArray> &pluginList)
{
- for (int i = 0; i < pluginList.count(); ++i) {
+ for (int i = 0; i < pluginList.size(); ++i) {
QByteArray pluginSpec = pluginList.at(i);
- int colonPos = pluginSpec.indexOf(':');
+ qsizetype colonPos = pluginSpec.indexOf(':');
QObject *plugin;
if (colonPos < 0)
plugin = QGenericPluginFactory::create(QLatin1StringView(pluginSpec), QString());
@@ -1470,6 +1530,8 @@ void QGuiApplicationPrivate::createPlatformIntegration()
Q_UNUSED(platformExplicitlySelected);
init_platform(QLatin1StringView(platformName), platformPluginPath, platformThemeName, argc, argv);
+ if (const QPlatformTheme *theme = platformTheme())
+ QStyleHintsPrivate::get(QGuiApplication::styleHints())->updateColorScheme(theme->colorScheme());
if (!icon.isEmpty())
forcedWindowIcon = QDir::isAbsolutePath(icon) ? QIcon(icon) : QIcon::fromTheme(icon);
@@ -1488,7 +1550,11 @@ void QGuiApplicationPrivate::createEventDispatcher()
if (platform_integration == nullptr)
createPlatformIntegration();
- // The platform integration should not mess with the event dispatcher
+ // The platform integration should not result in creating an event dispatcher
+ Q_ASSERT_X(!threadData.loadRelaxed()->eventDispatcher, "QGuiApplication",
+ "Creating the platform integration resulted in creating an event dispatcher");
+
+ // Nor should it mess with the QCoreApplication's event dispatcher
Q_ASSERT(!eventDispatcher);
eventDispatcher = platform_integration->createEventDispatcher();
@@ -1502,7 +1568,7 @@ void QGuiApplicationPrivate::eventDispatcherReady()
platform_integration->initialize();
}
-void QGuiApplicationPrivate::init()
+void Q_TRACE_INSTRUMENT(qtgui) QGuiApplicationPrivate::init()
{
Q_TRACE_SCOPE(QGuiApplicationPrivate_init);
@@ -1565,7 +1631,7 @@ void QGuiApplicationPrivate::init()
++i;
if (argv[i] && *argv[i]) {
session_id = QString::fromLatin1(argv[i]);
- int p = session_id.indexOf(u'_');
+ qsizetype p = session_id.indexOf(u'_');
if (p >= 0) {
session_key = session_id.mid(p +1);
session_id = session_id.left(p);
@@ -1676,7 +1742,7 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate()
is_app_closing = true;
is_app_running = false;
- for (int i = 0; i < generic_plugin_list.count(); ++i)
+ for (int i = 0; i < generic_plugin_list.size(); ++i)
delete generic_plugin_list.at(i);
generic_plugin_list.clear();
@@ -1781,7 +1847,7 @@ Qt::KeyboardModifiers QGuiApplication::queryKeyboardModifiers()
{
CHECK_QAPP_INSTANCE(Qt::KeyboardModifiers{})
QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration();
- return pi->queryKeyboardModifiers();
+ return pi->keyMapper()->queryKeyboardModifiers();
}
/*!
@@ -1839,9 +1905,10 @@ QFunctionPointer QGuiApplication::platformFunction(const QByteArray &function)
Generally, no user interaction can take place before calling exec().
- To make your application perform idle processing, e.g., executing a special
- function whenever there are no pending events, use a QTimer with 0 timeout.
- More advanced idle processing schemes can be achieved using processEvents().
+ To make your application perform idle processing, e.g., executing a
+ special function whenever there are no pending events, use a QChronoTimer
+ with 0ns timeout. More advanced idle processing schemes can be achieved
+ using processEvents().
We recommend that you connect clean-up code to the
\l{QCoreApplication::}{aboutToQuit()} signal, instead of putting it in your
@@ -1931,7 +1998,8 @@ bool QGuiApplication::notify(QObject *object, QEvent *event)
*/
bool QGuiApplication::event(QEvent *e)
{
- if (e->type() == QEvent::LanguageChange) {
+ switch (e->type()) {
+ case QEvent::LanguageChange:
// if the layout direction was set explicitly, then don't override it here
if (layout_direction == Qt::LayoutDirectionAuto)
setLayoutDirection(layout_direction);
@@ -1939,13 +2007,15 @@ bool QGuiApplication::event(QEvent *e)
if (topLevelWindow->flags() != Qt::Desktop)
postEvent(topLevelWindow, new QEvent(QEvent::LanguageChange));
}
- } else if (e->type() == QEvent::ApplicationFontChange ||
- e->type() == QEvent::ApplicationPaletteChange) {
+ break;
+ case QEvent::ApplicationFontChange:
+ case QEvent::ApplicationPaletteChange:
for (auto *topLevelWindow : QGuiApplication::topLevelWindows()) {
if (topLevelWindow->flags() != Qt::Desktop)
postEvent(topLevelWindow, new QEvent(e->type()));
}
- } else if (e->type() == QEvent::Quit) {
+ break;
+ case QEvent::Quit:
// Close open windows. This is done in order to deliver de-expose
// events while the event loop is still running.
for (QWindow *topLevelWindow : QGuiApplication::topLevelWindows()) {
@@ -1957,8 +2027,10 @@ bool QGuiApplication::event(QEvent *e)
return true;
}
}
+ break;
+ default:
+ break;
}
-
return QCoreApplication::event(e);
}
@@ -1989,8 +2061,9 @@ bool QGuiApplicationPrivate::processNativeEvent(QWindow *window, const QByteArra
return window->nativeEvent(eventType, message, result);
}
-void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e)
+void Q_TRACE_INSTRUMENT(qtgui) QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e)
{
+ Q_TRACE_PARAM_REPLACE(QWindowSystemInterfacePrivate::WindowSystemEvent *, int);
Q_TRACE_SCOPE(QGuiApplicationPrivate_processWindowSystemEvent, e->type);
switch(e->type) {
@@ -2015,8 +2088,8 @@ void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePriv
case QWindowSystemInterfacePrivate::Leave:
QGuiApplicationPrivate::processLeaveEvent(static_cast<QWindowSystemInterfacePrivate::LeaveEvent *>(e));
break;
- case QWindowSystemInterfacePrivate::ActivatedWindow:
- QGuiApplicationPrivate::processActivatedEvent(static_cast<QWindowSystemInterfacePrivate::ActivatedWindowEvent *>(e));
+ case QWindowSystemInterfacePrivate::FocusWindow:
+ QGuiApplicationPrivate::processFocusWindowEvent(static_cast<QWindowSystemInterfacePrivate::FocusWindowEvent *>(e));
break;
case QWindowSystemInterfacePrivate::WindowStateChanged:
QGuiApplicationPrivate::processWindowStateChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowStateChangedEvent *>(e));
@@ -2024,6 +2097,9 @@ void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePriv
case QWindowSystemInterfacePrivate::WindowScreenChanged:
QGuiApplicationPrivate::processWindowScreenChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowScreenChangedEvent *>(e));
break;
+ case QWindowSystemInterfacePrivate::WindowDevicePixelRatioChanged:
+ QGuiApplicationPrivate::processWindowDevicePixelRatioChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowDevicePixelRatioChangedEvent *>(e));
+ break;
case QWindowSystemInterfacePrivate::SafeAreaMarginsChanged:
QGuiApplicationPrivate::processSafeAreaMarginsChangedEvent(static_cast<QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent *>(e));
break;
@@ -2181,12 +2257,14 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
qAbs(globalPoint.y() - pressPos.y()) > doubleClickDistance)
mousePressButton = Qt::NoButton;
} else {
+ static unsigned long lastPressTimestamp = 0;
mouse_buttons = e->buttons;
if (mousePress) {
ulong doubleClickInterval = static_cast<ulong>(QGuiApplication::styleHints()->mouseDoubleClickInterval());
- doubleClick = e->timestamp - persistentEPD->eventPoint.pressTimestamp()
+ doubleClick = e->timestamp - lastPressTimestamp
< doubleClickInterval && button == mousePressButton;
mousePressButton = button;
+ lastPressTimestamp = e ->timestamp;
}
}
@@ -2326,6 +2404,7 @@ void QGuiApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::Wh
mouse_buttons, e->modifiers, e->phase, e->inverted, e->source, device);
ev.setTimestamp(e->timestamp);
QGuiApplication::sendSpontaneousEvent(window, &ev);
+ e->eventAccepted = ev.isAccepted();
#else
Q_UNUSED(e);
#endif // QT_CONFIG(wheelevent)
@@ -2433,10 +2512,10 @@ void QGuiApplicationPrivate::processLeaveEvent(QWindowSystemInterfacePrivate::Le
QCoreApplication::sendSpontaneousEvent(e->leave.data(), &event);
}
-void QGuiApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *e)
+void QGuiApplicationPrivate::processFocusWindowEvent(QWindowSystemInterfacePrivate::FocusWindowEvent *e)
{
QWindow *previous = QGuiApplicationPrivate::focus_window;
- QWindow *newFocus = e->activated.data();
+ QWindow *newFocus = e->focused.data();
if (previous == newFocus)
return;
@@ -2506,37 +2585,46 @@ void QGuiApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate
void QGuiApplicationPrivate::processWindowStateChangedEvent(QWindowSystemInterfacePrivate::WindowStateChangedEvent *wse)
{
- if (QWindow *window = wse->window.data()) {
+ if (QWindow *window = wse->window.data()) {
+ QWindowPrivate *windowPrivate = qt_window_private(window);
+ const auto originalEffectiveState = QWindowPrivate::effectiveState(windowPrivate->windowState);
+
+ windowPrivate->windowState = wse->newState;
+ const auto newEffectiveState = QWindowPrivate::effectiveState(windowPrivate->windowState);
+ if (newEffectiveState != originalEffectiveState)
+ emit window->windowStateChanged(newEffectiveState);
+
+ windowPrivate->updateVisibility();
+
QWindowStateChangeEvent e(wse->oldState);
- window->d_func()->windowState = wse->newState;
QGuiApplication::sendSpontaneousEvent(window, &e);
}
}
void QGuiApplicationPrivate::processWindowScreenChangedEvent(QWindowSystemInterfacePrivate::WindowScreenChangedEvent *wse)
{
- if (QWindow *window = wse->window.data()) {
- if (window->screen() == wse->screen.data())
- return;
- if (QWindow *topLevelWindow = window->d_func()->topLevelWindow(QWindow::ExcludeTransients)) {
- if (QScreen *screen = wse->screen.data())
- topLevelWindow->d_func()->setTopLevelScreen(screen, false /* recreate */);
- else // Fall back to default behavior, and try to find some appropriate screen
- topLevelWindow->setScreen(nullptr);
- }
+ QWindow *window = wse->window.data();
+ if (!window)
+ return;
- // We may have changed scaling; trigger resize event if needed,
- // except on Windows, where we send resize events during WM_DPICHANGED
- // event handling. FIXME: unify DPI change handling across all platforms.
-#ifndef Q_OS_WIN
- if (window->handle()) {
- QWindowSystemInterfacePrivate::GeometryChangeEvent gce(window, QHighDpi::fromNativePixels(window->handle()->geometry(), window));
- processGeometryChangeEvent(&gce);
- }
-#endif
+ if (window->screen() == wse->screen.data())
+ return;
+
+ if (QWindow *topLevelWindow = window->d_func()->topLevelWindow(QWindow::ExcludeTransients)) {
+ if (QScreen *screen = wse->screen.data())
+ topLevelWindow->d_func()->setTopLevelScreen(screen, false /* recreate */);
+ else // Fall back to default behavior, and try to find some appropriate screen
+ topLevelWindow->setScreen(nullptr);
}
}
+void QGuiApplicationPrivate::processWindowDevicePixelRatioChangedEvent(QWindowSystemInterfacePrivate::WindowDevicePixelRatioChangedEvent *wde)
+{
+ if (wde->window.isNull())
+ return;
+ QWindowPrivate::get(wde->window)->updateDevicePixelRatio();
+}
+
void QGuiApplicationPrivate::processSafeAreaMarginsChangedEvent(QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent *wse)
{
if (wse->window.isNull())
@@ -2553,16 +2641,33 @@ void QGuiApplicationPrivate::processThemeChanged(QWindowSystemInterfacePrivate::
if (self)
self->handleThemeChanged();
+ QIconPrivate::clearIconCache();
+
+ QStyleHintsPrivate::get(QGuiApplication::styleHints())->updateColorScheme(colorScheme());
+
QEvent themeChangeEvent(QEvent::ThemeChange);
const QWindowList windows = tce->window ? QWindowList{tce->window} : window_list;
for (auto *window : windows)
QGuiApplication::sendSpontaneousEvent(window, &themeChangeEvent);
}
+/*!
+ \internal
+ \brief QGuiApplicationPrivate::colorScheme
+ \return the platform theme's color scheme
+ or Qt::ColorScheme::Unknown if a platform theme cannot be established
+ */
+Qt::ColorScheme QGuiApplicationPrivate::colorScheme()
+{
+ return platformTheme() ? platformTheme()->colorScheme()
+ : Qt::ColorScheme::Unknown;
+}
+
void QGuiApplicationPrivate::handleThemeChanged()
{
updatePalette();
+ QIconLoader::instance()->updateSystemTheme();
QAbstractFileIconProviderPrivate::clearIconTypeCache();
if (!(applicationResourceFlags & ApplicationFontExplicitlySet)) {
@@ -2629,8 +2734,10 @@ void QGuiApplicationPrivate::processCloseEvent(QWindowSystemInterfacePrivate::Cl
{
if (e->window.isNull())
return;
- if (e->window.data()->d_func()->blockedByModalWindow) {
- // a modal window is blocking this window, don't allow close events through
+ if (e->window.data()->d_func()->blockedByModalWindow && !e->window.data()->d_func()->inClose) {
+ // a modal window is blocking this window, don't allow close events through, unless they
+ // originate from a call to QWindow::close.
+ e->eventAccepted = false;
return;
}
@@ -2735,6 +2842,8 @@ void QGuiApplicationPrivate::processTabletEvent(QWindowSystemInterfacePrivate::T
QWindowSystemInterfacePrivate::MouseEvent mouseEvent(window, e->timestamp, e->local,
e->global, e->buttons, e->modifiers, button, mouseType, Qt::MouseEventNotSynthesized, false, device);
mouseEvent.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic;
+ qCDebug(lcPtrDispatch) << "synthesizing mouse from tablet event" << mouseType
+ << e->local << button << e->buttons << e->modifiers;
processMouseEvent(&mouseEvent);
}
#else
@@ -2825,17 +2934,17 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
// Send the TouchCancel to all windows with active touches and clean up.
QTouchEvent touchEvent(QEvent::TouchCancel, device, e->modifiers);
touchEvent.setTimestamp(e->timestamp);
- QSet<QWindow *> windowsNeedingCancel;
+ constexpr qsizetype Prealloc = decltype(devPriv->activePoints)::mapped_container_type::PreallocatedSize;
+ QMinimalVarLengthFlatSet<QWindow *, Prealloc> windowsNeedingCancel;
for (auto &epd : devPriv->activePoints.values()) {
if (QWindow *w = QMutableEventPoint::window(epd.eventPoint))
windowsNeedingCancel.insert(w);
}
- for (QSet<QWindow *>::const_iterator winIt = windowsNeedingCancel.constBegin(),
- winItEnd = windowsNeedingCancel.constEnd(); winIt != winItEnd; ++winIt) {
- QGuiApplication::sendSpontaneousEvent(*winIt, &touchEvent);
- }
+ for (QWindow *w : windowsNeedingCancel)
+ QGuiApplication::sendSpontaneousEvent(w, &touchEvent);
+
if (!self->synthesizedMousePoints.isEmpty() && !e->synthetic()) {
for (QHash<QWindow *, SynthesizedMouseData>::const_iterator synthIt = self->synthesizedMousePoints.constBegin(),
synthItEnd = self->synthesizedMousePoints.constEnd(); synthIt != synthItEnd; ++synthIt) {
@@ -2892,15 +3001,15 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
case QEventPoint::State::Released:
if (Q_UNLIKELY(!window.isNull() && window != QMutableEventPoint::window(ep)))
- qCWarning(lcPtrDispatch) << "delivering touch release to same window"
- << QMutableEventPoint::window(ep) << "not" << window.data();
+ qCDebug(lcPtrDispatch) << "delivering touch release to same window"
+ << QMutableEventPoint::window(ep) << "not" << window.data();
window = QMutableEventPoint::window(ep);
break;
default: // update or stationary
if (Q_UNLIKELY(!window.isNull() && window != QMutableEventPoint::window(ep)))
- qCWarning(lcPtrDispatch) << "delivering touch update to same window"
- << QMutableEventPoint::window(ep) << "not" << window.data();
+ qCDebug(lcPtrDispatch) << "delivering touch update to same window"
+ << QMutableEventPoint::window(ep) << "not" << window.data();
window = QMutableEventPoint::window(ep);
break;
}
@@ -3068,26 +3177,16 @@ void QGuiApplicationPrivate::processScreenGeometryChange(QWindowSystemInterfaceP
if (!e->screen)
return;
- QScreen *s = e->screen.data();
-
- bool geometryChanged = e->geometry != s->d_func()->geometry;
- s->d_func()->geometry = e->geometry;
+ {
+ QScreen *s = e->screen.data();
+ QScreenPrivate::UpdateEmitter updateEmitter(s);
- bool availableGeometryChanged = e->availableGeometry != s->d_func()->availableGeometry;
- s->d_func()->availableGeometry = e->availableGeometry;
+ // Note: The incoming geometries have already been scaled by QHighDpi
+ // in the QWSI layer, so we don't need to call updateGeometry() here.
+ s->d_func()->geometry = e->geometry;
+ s->d_func()->availableGeometry = e->availableGeometry;
- const Qt::ScreenOrientation primaryOrientation = s->primaryOrientation();
- if (geometryChanged)
s->d_func()->updatePrimaryOrientation();
-
- s->d_func()->emitGeometryChangeSignals(geometryChanged, availableGeometryChanged);
-
- if (geometryChanged) {
- emit s->physicalSizeChanged(s->physicalSize());
- emit s->logicalDotsPerInchChanged(s->logicalDotsPerInch());
-
- if (s->primaryOrientation() != primaryOrientation)
- emit s->primaryOrientationChanged(s->primaryOrientation());
}
resetCachedDevicePixelRatio();
@@ -3104,11 +3203,16 @@ void QGuiApplicationPrivate::processScreenLogicalDotsPerInchChange(QWindowSystem
if (!e->screen)
return;
- QScreen *s = e->screen.data();
- s->d_func()->logicalDpi = QDpi(e->dpiX, e->dpiY);
+ {
+ QScreen *s = e->screen.data();
+ QScreenPrivate::UpdateEmitter updateEmitter(s);
+ s->d_func()->logicalDpi = QDpi(e->dpiX, e->dpiY);
+ s->d_func()->updateGeometry();
+ }
- emit s->logicalDotsPerInchChanged(s->logicalDotsPerInch());
- s->d_func()->updateGeometriesWithSignals();
+ for (QWindow *window : QGuiApplication::allWindows())
+ if (window->screen() == e->screen)
+ QWindowPrivate::get(window)->updateDevicePixelRatio();
resetCachedDevicePixelRatio();
}
@@ -3168,6 +3272,16 @@ void QGuiApplicationPrivate::processExposeEvent(QWindowSystemInterfacePrivate::E
const bool wasExposed = p->exposed;
p->exposed = e->isExposed && window->screen();
+ // We expect that the platform plugins send DevicePixelRatioChange events.
+ // As a fail-safe make a final check here to make sure the cached DPR value is
+ // always up to date before sending the expose event.
+ if (e->isExposed && !e->region.isEmpty()) {
+ const bool dprWasChanged = QWindowPrivate::get(window)->updateDevicePixelRatio();
+ if (dprWasChanged)
+ qWarning() << "The cached device pixel ratio value was stale on window expose. "
+ << "Please file a QTBUG which explains how to reproduce.";
+ }
+
// We treat expose events for an already exposed window as paint events
if (wasExposed && p->exposed && shouldSynthesizePaintEvents) {
QPaintEvent paintEvent(e->region);
@@ -3393,11 +3507,13 @@ bool QGuiApplicationPrivate::setPalette(const QPalette &palette)
*/
QPalette QGuiApplicationPrivate::basePalette() const
{
- return platformTheme() ? *platformTheme()->palette() : Qt::gray;
+ const auto pf = platformTheme();
+ return pf && pf->palette() ? *pf->palette() : Qt::gray;
}
void QGuiApplicationPrivate::handlePaletteChanged(const char *className)
{
+#if QT_DEPRECATED_SINCE(6, 0)
if (!className) {
Q_ASSERT(app_pal);
QT_WARNING_PUSH
@@ -3405,6 +3521,9 @@ QT_WARNING_DISABLE_DEPRECATED
emit qGuiApp->paletteChanged(*QGuiApplicationPrivate::app_pal);
QT_WARNING_POP
}
+#else
+ Q_UNUSED(className);
+#endif // QT_DEPRECATED_SINCE(6, 0)
if (is_app_running && !is_app_closing) {
QEvent event(QEvent::ApplicationPaletteChange);
@@ -3463,10 +3582,14 @@ void QGuiApplication::setFont(const QFont &font)
if (emitChange && qGuiApp) {
auto font = *QGuiApplicationPrivate::app_font;
locker.unlock();
+#if QT_DEPRECATED_SINCE(6, 0)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
emit qGuiApp->fontChanged(font);
QT_WARNING_POP
+#else
+ Q_UNUSED(font);
+#endif // QT_DEPRECATED_SINCE(6, 0)
QEvent event(QEvent::ApplicationFontChange);
QGuiApplication::sendEvent(qGuiApp, &event);
}
@@ -3674,6 +3797,8 @@ Qt::ApplicationState QGuiApplication::applicationState()
*/
void QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy policy)
{
+ if (qApp)
+ qWarning("setHighDpiScaleFactorRoundingPolicy must be called before creating the QGuiApplication instance");
QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy = policy;
}
@@ -4264,6 +4389,9 @@ void *QGuiApplication::resolveInterface(const char *name, int revision) const
#if QT_CONFIG(xcb)
QT_NATIVE_INTERFACE_RETURN_IF(QX11Application, platformNativeInterface());
#endif
+#if QT_CONFIG(wayland)
+ QT_NATIVE_INTERFACE_RETURN_IF(QWaylandApplication, platformNativeInterface());
+#endif
return QCoreApplication::resolveInterface(name, revision);
}
diff --git a/src/gui/kernel/qguiapplication.h b/src/gui/kernel/qguiapplication.h
index b77984c381..14bce88c62 100644
--- a/src/gui/kernel/qguiapplication.h
+++ b/src/gui/kernel/qguiapplication.h
@@ -58,6 +58,8 @@ public:
static void setApplicationDisplayName(const QString &name);
static QString applicationDisplayName();
+ Q_SLOT void setBadgeNumber(qint64 number);
+
static void setDesktopFileName(const QString &name);
static QString desktopFileName();
diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h
index 72a3fc78d0..2bfcd87f3f 100644
--- a/src/gui/kernel/qguiapplication_p.h
+++ b/src/gui/kernel/qguiapplication_p.h
@@ -34,6 +34,8 @@
# include "private/qshortcutmap_p.h"
#endif
+#include <QtCore/qpointer.h>
+
#include <memory>
QT_BEGIN_NAMESPACE
@@ -113,9 +115,11 @@ public:
static void processEnterEvent(QWindowSystemInterfacePrivate::EnterEvent *e);
static void processLeaveEvent(QWindowSystemInterfacePrivate::LeaveEvent *e);
- static void processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *e);
+ static void processFocusWindowEvent(QWindowSystemInterfacePrivate::FocusWindowEvent *e);
+
static void processWindowStateChangedEvent(QWindowSystemInterfacePrivate::WindowStateChangedEvent *e);
static void processWindowScreenChangedEvent(QWindowSystemInterfacePrivate::WindowScreenChangedEvent *e);
+ static void processWindowDevicePixelRatioChangedEvent(QWindowSystemInterfacePrivate::WindowDevicePixelRatioChangedEvent *e);
static void processSafeAreaMarginsChangedEvent(QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent *e);
@@ -188,7 +192,10 @@ public:
static void showModalWindow(QWindow *window);
static void hideModalWindow(QWindow *window);
static void updateBlockedStatus(QWindow *window);
- virtual bool isWindowBlocked(QWindow *window, QWindow **blockingWindow = nullptr) const;
+
+ virtual Qt::WindowModality defaultModality() const;
+ virtual bool windowNeverBlocked(QWindow *window) const;
+ bool isWindowBlocked(QWindow *window, QWindow **blockingWindow = nullptr) const;
virtual bool popupActive() { return false; }
virtual bool closeAllPopups() { return false; }
@@ -203,6 +210,28 @@ public:
constexpr void reset() noexcept { *this = QLastCursorPosition{}; }
+ // QGuiApplicationPrivate::lastCursorPosition is used for mouse-move detection
+ // but even QPointF's qFuzzCompare on doubles is too precise, and causes move-noise
+ // e.g. on macOS (see QTBUG-111170). So we specialize the equality operators here
+ // to use single-point precision.
+ friend constexpr bool operator==(const QLastCursorPosition &p1, const QPointF &p2) noexcept
+ {
+ return qFuzzyCompare(float(p1.x()), float(p2.x()))
+ && qFuzzyCompare(float(p1.y()), float(p2.y()));
+ }
+ friend constexpr bool operator!=(const QLastCursorPosition &p1, const QPointF &p2) noexcept
+ {
+ return !(p1 == p2);
+ }
+ friend constexpr bool operator==(const QPointF &p1, const QLastCursorPosition &p2) noexcept
+ {
+ return p2 == p1;
+ }
+ friend constexpr bool operator!=(const QPointF &p1, const QLastCursorPosition &p2) noexcept
+ {
+ return !(p2 == p1);
+ }
+
private:
QPointF thePoint;
} lastCursorPosition;
@@ -294,6 +323,8 @@ public:
static void updatePalette();
+ static Qt::ColorScheme colorScheme();
+
protected:
virtual void handleThemeChanged();
@@ -328,11 +359,12 @@ private:
// ----------------- QNativeInterface -----------------
+class QWindowsMimeConverter;
+
namespace QNativeInterface::Private {
-#if defined(Q_OS_WIN) || defined(Q_CLANG_QDOC)
+#if defined(Q_OS_WIN) || defined(Q_QDOC)
-class QWindowsMime;
struct Q_GUI_EXPORT QWindowsApplication
{
@@ -364,18 +396,18 @@ struct Q_GUI_EXPORT QWindowsApplication
virtual WindowActivationBehavior windowActivationBehavior() const = 0;
virtual void setWindowActivationBehavior(WindowActivationBehavior behavior) = 0;
+ virtual void setHasBorderInFullScreenDefault(bool border) = 0;
+
virtual bool isTabletMode() const = 0;
virtual bool isWinTabEnabled() const = 0;
virtual bool setWinTabEnabled(bool enabled) = 0;
- virtual bool isDarkMode() const = 0;
-
virtual DarkModeHandling darkModeHandling() const = 0;
virtual void setDarkModeHandling(DarkModeHandling handling) = 0;
- virtual void registerMime(QWindowsMime *mime) = 0;
- virtual void unregisterMime(QWindowsMime *mime) = 0;
+ virtual void registerMime(QWindowsMimeConverter *mime) = 0;
+ virtual void unregisterMime(QWindowsMimeConverter *mime) = 0;
virtual int registerMimeType(const QString &mime) = 0;
@@ -388,6 +420,8 @@ struct Q_GUI_EXPORT QWindowsApplication
virtual QVariant gpu() const = 0; // internal, used by qtdiag
virtual QVariant gpuList() const = 0;
+
+ virtual void populateLightSystemPalette(QPalette &pal) const = 0;
};
#endif // Q_OS_WIN
diff --git a/src/gui/kernel/qguiapplication_platform.h b/src/gui/kernel/qguiapplication_platform.h
index 3220373c5e..545bf75c84 100644
--- a/src/gui/kernel/qguiapplication_platform.h
+++ b/src/gui/kernel/qguiapplication_platform.h
@@ -23,12 +23,21 @@ typedef struct _XDisplay Display;
struct xcb_connection_t;
#endif
+#if QT_CONFIG(wayland)
+struct wl_display;
+struct wl_compositor;
+struct wl_seat;
+struct wl_keyboard;
+struct wl_pointer;
+struct wl_touch;
+#endif
+
QT_BEGIN_NAMESPACE
namespace QNativeInterface
{
-#if QT_CONFIG(xcb) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(xcb) || defined(Q_QDOC)
struct Q_GUI_EXPORT QX11Application
{
QT_DECLARE_NATIVE_INTERFACE(QX11Application, 1, QGuiApplication)
@@ -37,6 +46,21 @@ struct Q_GUI_EXPORT QX11Application
};
#endif
+#if QT_CONFIG(wayland) || defined(Q_QDOC)
+struct Q_GUI_EXPORT QWaylandApplication
+{
+ QT_DECLARE_NATIVE_INTERFACE(QWaylandApplication, 1, QGuiApplication)
+ virtual wl_display *display() const = 0;
+ virtual wl_compositor *compositor() const = 0;
+ virtual wl_seat *seat() const = 0;
+ virtual wl_keyboard *keyboard() const = 0;
+ virtual wl_pointer *pointer() const = 0;
+ virtual wl_touch *touch() const = 0;
+ virtual uint lastInputSerial() const = 0;
+ virtual wl_seat *lastInputSeat() const = 0;
+};
+#endif
+
} // QNativeInterface
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qguivariant.cpp b/src/gui/kernel/qguivariant.cpp
index 656072c0bf..fe72e7782f 100644
--- a/src/gui/kernel/qguivariant.cpp
+++ b/src/gui/kernel/qguivariant.cpp
@@ -50,14 +50,14 @@
#include <float.h>
-#include "private/qvariant_p.h"
#include <private/qmetatype_p.h>
QT_BEGIN_NAMESPACE
namespace {
-static const struct : QMetaTypeModuleHelper
+// NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor): this is not a base class
+static constexpr struct : QMetaTypeModuleHelper
{
#define QT_IMPL_METATYPEINTERFACE_GUI_TYPES(MetaTypeName, MetaTypeId, RealName) \
QT_METATYPE_INTERFACE_INIT(RealName),
diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp
index 8d6bb62594..a0e1b48dcb 100644
--- a/src/gui/kernel/qhighdpiscaling.cpp
+++ b/src/gui/kernel/qhighdpiscaling.cpp
@@ -401,6 +401,8 @@ QDpi QHighDpiScaling::effectiveLogicalDpi(const QPlatformScreen *screen, qreal r
*/
void QHighDpiScaling::initHighDpiScaling()
{
+ qCDebug(lcHighDpi) << "Initializing high-DPI scaling";
+
// Read environment variables
static const char* envDebugStr = "environment variable set:";
std::optional<int> envEnableHighDpiScaling = qEnvironmentVariableOptionalInt(enableHighDpiScalingEnvVar);
@@ -474,6 +476,9 @@ void QHighDpiScaling::initHighDpiScaling()
// Set initial active state
m_active = m_globalScalingActive || m_usePlatformPluginDpi;
+
+ qCDebug(lcHighDpi) << "Initialization done, high-DPI scaling is"
+ << (m_active ? "active" : "inactive");
}
/*
@@ -482,14 +487,18 @@ void QHighDpiScaling::initHighDpiScaling()
*/
void QHighDpiScaling::updateHighDpiScaling()
{
+ qCDebug(lcHighDpi) << "Updating high-DPI scaling";
+
// Apply screen factors from environment
if (m_screenFactors.size() > 0) {
+ qCDebug(lcHighDpi) << "Applying screen factors" << m_screenFactors;
int i = -1;
const auto screens = QGuiApplication::screens();
- for (const auto &[name, factor] : m_screenFactors) {
+ for (const auto &[name, rawFactor]: m_screenFactors) {
+ const qreal factor = roundScaleFactor(rawFactor);
++i;
if (name.isNull()) {
- if (i < screens.count())
+ if (i < screens.size())
setScreenFactor(screens.at(i), factor);
} else {
for (QScreen *screen : screens) {
@@ -515,6 +524,9 @@ void QHighDpiScaling::updateHighDpiScaling()
}
m_active = m_globalScalingActive || m_screenFactorSet || m_platformPluginDpiScalingActive;
+
+ qCDebug(lcHighDpi) << "Update done, high-DPI scaling is"
+ << (m_active ? "active" : "inactive");
}
/*
@@ -522,17 +534,24 @@ void QHighDpiScaling::updateHighDpiScaling()
*/
void QHighDpiScaling::setGlobalFactor(qreal factor)
{
+ qCDebug(lcHighDpi) << "Setting global scale factor to" << factor;
+
if (qFuzzyCompare(factor, m_factor))
return;
if (!QGuiApplication::allWindows().isEmpty())
qWarning("QHighDpiScaling::setFactor: Should only be called when no windows exist.");
+ const auto screens = QGuiApplication::screens();
+
+ std::vector<QScreenPrivate::UpdateEmitter> updateEmitters;
+ for (QScreen *screen : screens)
+ updateEmitters.emplace_back(screen);
+
m_globalScalingActive = !qFuzzyCompare(factor, qreal(1));
m_factor = m_globalScalingActive ? factor : qreal(1);
m_active = m_globalScalingActive || m_screenFactorSet || m_platformPluginDpiScalingActive ;
- const auto screens = QGuiApplication::screens();
for (QScreen *screen : screens)
- screen->d_func()->updateHighDpi();
+ screen->d_func()->updateGeometry();
}
static const char scaleFactorProperty[] = "_q_scaleFactor";
@@ -542,11 +561,15 @@ static const char scaleFactorProperty[] = "_q_scaleFactor";
*/
void QHighDpiScaling::setScreenFactor(QScreen *screen, qreal factor)
{
+ qCDebug(lcHighDpi) << "Setting screen scale factor for" << screen << "to" << factor;
+
if (!qFuzzyCompare(factor, qreal(1))) {
m_screenFactorSet = true;
m_active = true;
}
+ QScreenPrivate::UpdateEmitter updateEmitter(screen);
+
// Prefer associating the factor with screen name over the object
// since the screen object may be deleted on screen disconnects.
const QString name = screen->name();
@@ -555,9 +578,7 @@ void QHighDpiScaling::setScreenFactor(QScreen *screen, qreal factor)
else
QHighDpiScaling::m_namedScreenScaleFactors.insert(name, factor);
- // hack to force re-evaluation of screen geometry
- if (screen->handle())
- screen->d_func()->setPlatformScreen(screen->handle()); // updates geometries based on scale factor
+ screen->d_func()->updateGeometry();
}
QPoint QHighDpiScaling::mapPositionToNative(const QPoint &pos, const QPlatformScreen *platformScreen)
@@ -678,7 +699,7 @@ QVector<QHighDpiScaling::ScreenFactor> QHighDpiScaling::parseScreenScaleFactorsS
// - a semicolon-separated name=factor list: "foo=1.5;bar=2;baz=3"
const auto specs = screenScaleFactors.split(u';');
for (const auto &spec : specs) {
- const int equalsPos = spec.lastIndexOf(u'=');
+ const qsizetype equalsPos = spec.lastIndexOf(u'=');
if (equalsPos == -1) {
// screens in order
bool ok;
@@ -732,7 +753,20 @@ QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QWindow *w
return scaleAndOrigin(targetScreen, position);
}
-#else
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QHighDpiScaling::ScreenFactor &factor)
+{
+ const QDebugStateSaver saver(debug);
+ debug.nospace();
+ if (!factor.name.isEmpty())
+ debug << factor.name << "=";
+ debug << factor.factor;
+ return debug;
+}
+#endif
+
+#else // QT_NO_HIGHDPISCALING
+
QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QPlatformScreen *, QPoint *)
{
return { qreal(1), QPoint() };
@@ -747,7 +781,9 @@ QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QWindow *,
{
return { qreal(1), QPoint() };
}
-#endif //QT_NO_HIGHDPISCALING
+
+#endif // QT_NO_HIGHDPISCALING
+
QT_END_NAMESPACE
#include "moc_qhighdpiscaling_p.cpp"
diff --git a/src/gui/kernel/qhighdpiscaling_p.h b/src/gui/kernel/qhighdpiscaling_p.h
index 31d1cfe784..189f31fd0a 100644
--- a/src/gui/kernel/qhighdpiscaling_p.h
+++ b/src/gui/kernel/qhighdpiscaling_p.h
@@ -114,6 +114,10 @@ private:
static QVector<ScreenFactor> m_screenFactors;
static DpiAdjustmentPolicy m_dpiAdjustmentPolicy;
static QHash<QString, qreal> m_namedScreenScaleFactors;
+
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_GUI_EXPORT QDebug operator<<(QDebug, const ScreenFactor &);
+#endif
};
namespace QHighDpi {
@@ -344,6 +348,9 @@ public:
namespace QHighDpi {
template <typename T> inline
+ T scale(const T &value, ...) { return value; }
+
+ template <typename T> inline
T toNative(const T &value, ...) { return value; }
template <typename T> inline
T fromNative(const T &value, ...) { return value; }
diff --git a/src/gui/kernel/qinputdevice.cpp b/src/gui/kernel/qinputdevice.cpp
index b49fe4b15c..f7b216dcf0 100644
--- a/src/gui/kernel/qinputdevice.cpp
+++ b/src/gui/kernel/qinputdevice.cpp
@@ -235,6 +235,13 @@ Q_CONSTINIT static QBasicMutex devicesMutex;
/*!
Returns a list of all registered input devices (keyboards and pointing devices).
+ \note The list of devices is not always complete on all platforms. So far,
+ the most-complete information is available on the \l {Qt for Linux/X11}{X11}
+ platform, at startup and in response to hot-plugging. Most other platforms
+ are only able to provide generic devices of various types, only after receiving
+ events from them; and most platforms do not tell Qt when a device is plugged in,
+ or when it is unplugged at runtime.
+
\note The returned list cannot be used to add new devices. To add a simulated
touch screen for an autotest, QTest::createTouchDevice() can be used.
Platform plugins should call QWindowSystemInterface::registerInputDevice()
@@ -360,19 +367,24 @@ bool QInputDevice::operator==(const QInputDevice &other) const
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const QInputDevice *device)
{
- const QInputDevicePrivate *d = QInputDevicePrivate::get(device);
- if (d->pointingDeviceType)
- return operator<<(debug, static_cast<const QPointingDevice *>(device));
QDebugStateSaver saver(debug);
debug.nospace();
debug.noquote();
+
debug << "QInputDevice(";
- if (device) {
- debug << '"' << device->name() << "\", type=" << device->type()
- << Qt::hex << ", ID=" << device->systemId() << ", seat='" << device->seatName() << "'";
- } else {
- debug << '0';
+ if (!device) {
+ debug << "0)";
+ return debug;
}
+
+ const QInputDevicePrivate *d = QInputDevicePrivate::get(device);
+
+ if (d->pointingDeviceType)
+ return operator<<(debug, static_cast<const QPointingDevice *>(device));
+
+ debug << "QInputDevice(";
+ debug << '"' << device->name() << "\", type=" << device->type()
+ << ", ID=" << device->systemId() << ", seat='" << device->seatName() << "'";
debug << ')';
return debug;
}
diff --git a/src/gui/kernel/qinputmethod.cpp b/src/gui/kernel/qinputmethod.cpp
index ea1978b5fb..e37e85e246 100644
--- a/src/gui/kernel/qinputmethod.cpp
+++ b/src/gui/kernel/qinputmethod.cpp
@@ -399,7 +399,11 @@ QVariant QInputMethod::queryFocusObject(Qt::InputMethodQuery query, const QVaria
Q_ARG(Qt::InputMethodQuery, query),
Q_ARG(QVariant, argument));
Q_ASSERT(ok);
- return retval;
+ if (retval.isValid())
+ return retval;
+
+ // If the new API didn't have an answer to the query, we fall
+ // back to use the old event-based API.
}
QInputMethodQueryEvent queryEvent(query);
diff --git a/src/gui/kernel/qinternalmimedata.cpp b/src/gui/kernel/qinternalmimedata.cpp
index 9fd7f89a0f..d33402703e 100644
--- a/src/gui/kernel/qinternalmimedata.cpp
+++ b/src/gui/kernel/qinternalmimedata.cpp
@@ -20,7 +20,7 @@ static QStringList imageMimeFormats(const QList<QByteArray> &imageFormats)
formats.append("image/"_L1 + QLatin1StringView(format.toLower()));
//put png at the front because it is best
- int pngIndex = formats.indexOf("image/png"_L1);
+ const qsizetype pngIndex = formats.indexOf("image/png"_L1);
if (pngIndex != -1 && pngIndex != 0)
formats.move(pngIndex, 0);
diff --git a/src/gui/kernel/qkeymapper.cpp b/src/gui/kernel/qkeymapper.cpp
index aa8eca5214..4ceb508465 100644
--- a/src/gui/kernel/qkeymapper.cpp
+++ b/src/gui/kernel/qkeymapper.cpp
@@ -9,6 +9,7 @@
#include <private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
+#include <qpa/qplatformkeymapper.h>
QT_BEGIN_NAMESPACE
@@ -23,8 +24,7 @@ QT_BEGIN_NAMESPACE
/*!
Constructs a new key mapper.
*/
-QKeyMapper::QKeyMapper()
- : QObject(*new QKeyMapperPrivate, nullptr)
+QKeyMapper::QKeyMapper() : QObject()
{
}
@@ -35,34 +35,34 @@ QKeyMapper::~QKeyMapper()
{
}
-static QList<int> extractKeyFromEvent(QKeyEvent *e)
+QList<QKeyCombination> QKeyMapper::possibleKeys(const QKeyEvent *e)
{
- QList<int> result;
- if (e->key() && (e->key() != Qt::Key_unknown))
- result << e->keyCombination().toCombined();
- else if (!e->text().isEmpty())
- result << int(e->text().at(0).unicode() + (int)e->modifiers());
- return result;
-}
+ qCDebug(lcQpaKeyMapper).verbosity(3) << "Computing possible key combinations for" << e;
-QList<int> QKeyMapper::possibleKeys(QKeyEvent *e)
-{
- return instance()->d_func()->possibleKeys(e);
-}
+ const auto *platformIntegration = QGuiApplicationPrivate::platformIntegration();
+ const auto *platformKeyMapper = platformIntegration->keyMapper();
+ QList<QKeyCombination> result = platformKeyMapper->possibleKeyCombinations(e);
-extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); // in qapplication_*.cpp
-void QKeyMapper::changeKeyboard()
-{
- // ## TODO: Support KeyboardLayoutChange on QPA
-#if 0
- // inform all toplevel widgets of the change
- QEvent e(QEvent::KeyboardLayoutChange);
- QWidgetList list = QApplication::topLevelWidgets();
- for (int i = 0; i < list.size(); ++i) {
- QWidget *w = list.at(i);
- qt_sendSpontaneousEvent(w, &e);
+ if (result.isEmpty()) {
+ if (e->key() && (e->key() != Qt::Key_unknown))
+ result << e->keyCombination();
+ else if (!e->text().isEmpty())
+ result << (Qt::Key(e->text().at(0).unicode()) | e->modifiers());
+ }
+
+#if QT_CONFIG(shortcut)
+ if (lcQpaKeyMapper().isDebugEnabled()) {
+ qCDebug(lcQpaKeyMapper) << "Resulting possible key combinations:";
+ for (auto keyCombination : result) {
+ auto keySequence = QKeySequence(keyCombination);
+ qCDebug(lcQpaKeyMapper).verbosity(0) << "\t-"
+ << keyCombination << "/" << keySequence << "/"
+ << qUtf8Printable(keySequence.toString(QKeySequence::NativeText));
+ }
}
#endif
+
+ return result;
}
Q_GLOBAL_STATIC(QKeyMapper, keymapper)
@@ -75,30 +75,6 @@ QKeyMapper *QKeyMapper::instance()
return keymapper();
}
-QKeyMapperPrivate *qt_keymapper_private()
-{
- return QKeyMapper::instance()->d_func();
-}
-
-QKeyMapperPrivate::QKeyMapperPrivate()
-{
- keyboardInputLocale = QLocale::system();
- keyboardInputDirection = keyboardInputLocale.textDirection();
-}
-
-QKeyMapperPrivate::~QKeyMapperPrivate()
-{
-}
-
-QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent *e)
-{
- QList<int> result = QGuiApplicationPrivate::platformIntegration()->possibleKeys(e);
- if (!result.isEmpty())
- return result;
-
- return extractKeyFromEvent(e);
-}
-
void *QKeyMapper::resolveInterface(const char *name, int revision) const
{
Q_UNUSED(name); Q_UNUSED(revision);
diff --git a/src/gui/kernel/qkeymapper_p.h b/src/gui/kernel/qkeymapper_p.h
index eb35724811..1a6a9a608f 100644
--- a/src/gui/kernel/qkeymapper_p.h
+++ b/src/gui/kernel/qkeymapper_p.h
@@ -25,7 +25,6 @@
QT_BEGIN_NAMESPACE
-class QKeyMapperPrivate;
class Q_GUI_EXPORT QKeyMapper : public QObject
{
Q_OBJECT
@@ -34,40 +33,19 @@ public:
~QKeyMapper();
static QKeyMapper *instance();
- static void changeKeyboard();
- static QList<int> possibleKeys(QKeyEvent *e);
+ static QList<QKeyCombination> possibleKeys(const QKeyEvent *e);
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QKeyMapper)
private:
- friend QKeyMapperPrivate *qt_keymapper_private();
- Q_DECLARE_PRIVATE(QKeyMapper)
Q_DISABLE_COPY_MOVE(QKeyMapper)
};
-struct KeyboardLayoutItem;
-class QKeyEvent;
-
-class QKeyMapperPrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QKeyMapper)
-public:
- QKeyMapperPrivate();
- ~QKeyMapperPrivate();
-
- QList<int> possibleKeys(QKeyEvent *e);
-
- QLocale keyboardInputLocale;
- Qt::LayoutDirection keyboardInputDirection;
-};
-
-QKeyMapperPrivate *qt_keymapper_private(); // from qkeymapper.cpp
-
// ----------------- QNativeInterface -----------------
namespace QNativeInterface::Private {
-#if QT_CONFIG(evdev) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(evdev) || defined(Q_QDOC)
struct Q_GUI_EXPORT QEvdevKeyMapper
{
QT_DECLARE_NATIVE_INTERFACE(QEvdevKeyMapper, 1, QKeyMapper)
diff --git a/src/gui/kernel/qkeysequence.cpp b/src/gui/kernel/qkeysequence.cpp
index c415a95ab9..0529d940d2 100644
--- a/src/gui/kernel/qkeysequence.cpp
+++ b/src/gui/kernel/qkeysequence.cpp
@@ -13,7 +13,7 @@
#endif
#include "qvariant.h"
-#if defined(Q_OS_MACOS)
+#if defined(Q_OS_APPLE)
#include <QtCore/private/qcore_mac_p.h>
#endif
@@ -24,11 +24,11 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-#if defined(Q_OS_MACOS) || defined(Q_CLANG_QDOC)
+#if defined(Q_OS_APPLE) || defined(Q_QDOC)
Q_CONSTINIT static bool qt_sequence_no_mnemonics = true;
-struct MacSpecialKey {
+struct AppleSpecialKey {
int key;
- ushort macSymbol;
+ ushort appleSymbol;
};
// Unicode code points for the glyphs associated with these keys
@@ -38,7 +38,7 @@ static constexpr int kControlUnicode = 0x2303;
static constexpr int kOptionUnicode = 0x2325;
static constexpr int kCommandUnicode = 0x2318;
-static constexpr MacSpecialKey entries[] = {
+static constexpr AppleSpecialKey entries[] = {
{ Qt::Key_Escape, 0x238B },
{ Qt::Key_Tab, 0x21E5 },
{ Qt::Key_Backtab, 0x21E4 },
@@ -63,45 +63,45 @@ static constexpr MacSpecialKey entries[] = {
{ Qt::Key_Eject, 0x23CF },
};
-static constexpr bool operator<(const MacSpecialKey &lhs, const MacSpecialKey &rhs)
+static constexpr bool operator<(const AppleSpecialKey &lhs, const AppleSpecialKey &rhs)
{
return lhs.key < rhs.key;
}
-static constexpr bool operator<(const MacSpecialKey &lhs, int rhs)
+static constexpr bool operator<(const AppleSpecialKey &lhs, int rhs)
{
return lhs.key < rhs;
}
-static constexpr bool operator<(int lhs, const MacSpecialKey &rhs)
+static constexpr bool operator<(int lhs, const AppleSpecialKey &rhs)
{
return lhs < rhs.key;
}
static_assert(q20::is_sorted(std::begin(entries), std::end(entries)));
-QChar qt_macSymbolForQtKey(int key)
+static QChar appleSymbolForQtKey(int key)
{
const auto i = std::lower_bound(std::begin(entries), std::end(entries), key);
if (i == std::end(entries) || key < *i)
return QChar();
- ushort macSymbol = i->macSymbol;
+ ushort appleSymbol = i->appleSymbol;
if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)
- && (macSymbol == kControlUnicode || macSymbol == kCommandUnicode)) {
- if (macSymbol == kControlUnicode)
- macSymbol = kCommandUnicode;
+ && (appleSymbol == kControlUnicode || appleSymbol == kCommandUnicode)) {
+ if (appleSymbol == kControlUnicode)
+ appleSymbol = kCommandUnicode;
else
- macSymbol = kControlUnicode;
+ appleSymbol = kControlUnicode;
}
- return QChar(macSymbol);
+ return QChar(appleSymbol);
}
-static int qtkeyForMacSymbol(const QChar ch)
+static int qtkeyForAppleSymbol(const QChar ch)
{
const ushort unicode = ch.unicode();
- for (const MacSpecialKey &entry : entries) {
- if (entry.macSymbol == unicode) {
+ for (const AppleSpecialKey &entry : entries) {
+ if (entry.appleSymbol == unicode) {
int key = entry.key;
if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)
&& (unicode == kControlUnicode || unicode == kCommandUnicode)) {
@@ -164,9 +164,10 @@ void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemoni
for users of different languages. Translations are made in the
"QShortcut" context.
\li For hard-coded shortcuts, integer key codes can be specified with
- a combination of values defined by the Qt::Key and Qt::Modifier enum
- values. Each key code consists of a single Qt::Key value and zero or
- more modifiers, such as Qt::SHIFT, Qt::CTRL, Qt::ALT and Qt::META.
+ a combination of values defined by the Qt::Key and Qt::KeyboardModifier
+ enum values. Each key code consists of a single Qt::Key value and zero
+ or more modifiers, such as Qt::ShiftModifier, Qt::ControlModifier,
+ Qt::AltModifier and Qt::MetaModifier.
\endlist
For example, \uicontrol{Ctrl P} might be a sequence used as a shortcut for
@@ -190,7 +191,7 @@ void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemoni
QKeySequence objects can be cast to a QString to obtain a human-readable
translated version of the sequence. Similarly, the toString() function
- produces human-readable strings for use in menus. On \macos, the
+ produces human-readable strings for use in menus. On Apple platforms, the
appropriate symbols are used to describe keyboard shortcuts using special
keys on the Macintosh keyboard.
@@ -198,12 +199,12 @@ void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemoni
code point of the character; for example, 'A' gives the same key sequence
as Qt::Key_A.
- \note On \macos, references to "Ctrl", Qt::CTRL, Qt::Key_Control
+ \note On Apple platforms, references to "Ctrl", Qt::CTRL, Qt::Key_Control
and Qt::ControlModifier correspond to the \uicontrol Command keys on the
Macintosh keyboard, and references to "Meta", Qt::META, Qt::Key_Meta and
- Qt::MetaModifier correspond to the \uicontrol Control keys. Developers on
- \macos can use the same shortcut descriptions across all platforms,
- and their applications will automatically work as expected on \macos.
+ Qt::MetaModifier correspond to the \uicontrol Control keys. In effect,
+ developers can use the same shortcut descriptions across all platforms,
+ and their applications will automatically work as expected on Apple platforms.
\section1 Standard Shortcuts
@@ -212,21 +213,21 @@ void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemoni
setting up actions in a typical application. The table below shows
some common key sequences that are often used for these standard
shortcuts by applications on four widely-used platforms. Note
- that on \macos, the \uicontrol Ctrl value corresponds to the \uicontrol
+ that on Apple platforms, the \uicontrol Ctrl value corresponds to the \uicontrol
Command keys on the Macintosh keyboard, and the \uicontrol Meta value
corresponds to the \uicontrol Control keys.
\table
- \header \li StandardKey \li Windows \li \macos \li KDE Plasma \li GNOME
+ \header \li StandardKey \li Windows \li Apple platforms \li KDE Plasma \li GNOME
\row \li HelpContents \li F1 \li Ctrl+? \li F1 \li F1
\row \li WhatsThis \li Shift+F1 \li Shift+F1 \li Shift+F1 \li Shift+F1
\row \li Open \li Ctrl+O \li Ctrl+O \li Ctrl+O \li Ctrl+O
\row \li Close \li Ctrl+F4, Ctrl+W \li Ctrl+W, Ctrl+F4 \li Ctrl+W \li Ctrl+W
\row \li Save \li Ctrl+S \li Ctrl+S \li Ctrl+S \li Ctrl+S
\row \li Quit \li \li Ctrl+Q \li Ctrl+Q \li Ctrl+Q
- \row \li SaveAs \li \li Ctrl+Shift+S \li \li Ctrl+Shift+S
+ \row \li SaveAs \li Ctrl+Shift+S \li Ctrl+Shift+S \li Ctrl+Shift+S \li Ctrl+Shift+S
\row \li New \li Ctrl+N \li Ctrl+N \li Ctrl+N \li Ctrl+N
- \row \li Delete \li Del \li Del, Meta+D \li Del, Ctrl+D \li Del, Ctrl+D
+ \row \li Delete \li Del \li Forward Delete, Meta+D \li Del, Ctrl+D \li Del, Ctrl+D
\row \li Cut \li Ctrl+X, Shift+Del \li Ctrl+X, Meta+K \li Ctrl+X, F20, Shift+Del \li Ctrl+X, F20, Shift+Del
\row \li Copy \li Ctrl+C, Ctrl+Ins \li Ctrl+C \li Ctrl+C, F16, Ctrl+Ins \li Ctrl+C, F16, Ctrl+Ins
\row \li Paste \li Ctrl+V, Shift+Ins \li Ctrl+V, Meta+Y \li Ctrl+V, F18, Shift+Ins \li Ctrl+V, F18, Shift+Ins
@@ -286,7 +287,7 @@ void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemoni
\row \li DeleteCompleteLine \li (none) \li (none) \li Ctrl+U \li Ctrl+U
\row \li InsertParagraphSeparator \li Enter \li Enter \li Enter \li Enter
\row \li InsertLineSeparator \li Shift+Enter \li Meta+Enter, Meta+O \li Shift+Enter \li Shift+Enter
- \row \li Backspace \li (none) \li Meta+H \li (none) \li (none)
+ \row \li Backspace \li (none) \li Delete, Meta+H \li (none) \li (none)
\row \li Cancel \li Escape \li Escape, Ctrl+. \li Escape \li Escape
\endtable
@@ -373,7 +374,7 @@ void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemoni
\enum QKeySequence::SequenceFormat
\value NativeText The key sequence as a platform specific string.
- This means that it will be shown translated and on the Mac it will
+ This means that it will be shown translated and on Apple platforms it will
resemble a key sequence from the menu bar. This enum is best used when you
want to display the string to the user.
@@ -709,7 +710,7 @@ static constexpr int numKeyNames = sizeof keyname / sizeof *keyname;
\value InsertLineSeparator Insert a new line.
\value InsertParagraphSeparator Insert a new paragraph.
\value Italic Italic text.
- \value MoveToEndOfBlock Move cursor to end of block. This shortcut is only used on the \macos.
+ \value MoveToEndOfBlock Move cursor to end of block. This shortcut is only used on Apple platforms.
\value MoveToEndOfDocument Move cursor to end of document.
\value MoveToEndOfLine Move cursor to end of line.
\value MoveToNextChar Move cursor to next character.
@@ -720,7 +721,7 @@ static constexpr int numKeyNames = sizeof keyname / sizeof *keyname;
\value MoveToPreviousLine Move cursor to previous line.
\value MoveToPreviousPage Move cursor to previous page.
\value MoveToPreviousWord Move cursor to previous word.
- \value MoveToStartOfBlock Move cursor to start of a block. This shortcut is only used on \macos.
+ \value MoveToStartOfBlock Move cursor to start of a block. This shortcut is only used on Apple platforms.
\value MoveToStartOfDocument Move cursor to start of document.
\value MoveToStartOfLine Move cursor to start of line.
\value New Create new document.
@@ -738,7 +739,7 @@ static constexpr int numKeyNames = sizeof keyname / sizeof *keyname;
\value Save Save document.
\value SelectAll Select all text.
\value Deselect Deselect text. Since 5.1
- \value SelectEndOfBlock Extend selection to the end of a text block. This shortcut is only used on \macos.
+ \value SelectEndOfBlock Extend selection to the end of a text block. This shortcut is only used on Apple platforms.
\value SelectEndOfDocument Extend selection to end of document.
\value SelectEndOfLine Extend selection to end of line.
\value SelectNextChar Extend selection to next character.
@@ -749,7 +750,7 @@ static constexpr int numKeyNames = sizeof keyname / sizeof *keyname;
\value SelectPreviousLine Extend selection to previous line.
\value SelectPreviousPage Extend selection to previous page.
\value SelectPreviousWord Extend selection to previous word.
- \value SelectStartOfBlock Extend selection to the start of a text block. This shortcut is only used on \macos.
+ \value SelectStartOfBlock Extend selection to the start of a text block. This shortcut is only used on Apple platforms.
\value SelectStartOfDocument Extend selection to start of document.
\value SelectStartOfLine Extend selection to start of line.
\value Underline Underline text.
@@ -783,8 +784,8 @@ QKeySequence::QKeySequence(StandardKey key)
{
const QList <QKeySequence> bindings = keyBindings(key);
//pick only the first/primary shortcut from current bindings
- if (bindings.size() > 0) {
- d = bindings.first().d;
+ if (!bindings.isEmpty()) {
+ d = bindings.constFirst().d;
d->ref.ref();
}
else
@@ -833,8 +834,8 @@ static_assert(QKeySequencePrivate::MaxKeyCount == 4, "Change docs and ctor impl
\a k3 and \a k4.
The key codes are listed in Qt::Key and can be combined with
- modifiers (see Qt::Modifier) such as Qt::SHIFT, Qt::CTRL,
- Qt::ALT, or Qt::META.
+ modifiers (see Qt::KeyboardModifier) such as Qt::ShiftModifier,
+ Qt::ControlModifier, Qt::AltModifier, or Qt::MetaModifier.
*/
QKeySequence::QKeySequence(int k1, int k2, int k3, int k4)
{
@@ -930,11 +931,6 @@ bool QKeySequence::isEmpty() const
For example, mnemonic("E&xit") returns \c{Qt::ALT+Qt::Key_X},
mnemonic("&Quit") returns \c{ALT+Key_Q}, and mnemonic("Quit")
returns an empty QKeySequence.
-
- We provide a \l{accelerators.html}{list of common mnemonics}
- in English. At the time of writing, Microsoft and Open Group do
- not appear to have issued equivalent recommendations for other
- languages.
*/
QKeySequence QKeySequence::mnemonic(const QString &text)
{
@@ -944,10 +940,10 @@ QKeySequence QKeySequence::mnemonic(const QString &text)
return ret;
bool found = false;
- int p = 0;
+ qsizetype p = 0;
while (p >= 0) {
p = text.indexOf(u'&', p) + 1;
- if (p <= 0 || p >= (int)text.length())
+ if (p <= 0 || p >= (int)text.size())
break;
if (text.at(p) != u'&') {
QChar c = text.at(p);
@@ -997,17 +993,17 @@ int QKeySequence::assign(const QString &ks, QKeySequence::SequenceFormat format)
{
QString keyseq = ks;
int n = 0;
- int p = 0, diff = 0;
+ qsizetype p = 0, diff = 0;
// Run through the whole string, but stop
// if we have MaxKeyCount keys before the end.
- while (keyseq.length() && n < QKeySequencePrivate::MaxKeyCount) {
+ while (keyseq.size() && n < QKeySequencePrivate::MaxKeyCount) {
// We MUST use something to separate each sequence, and space
// does not cut it, since some of the key names have space
// in them.. (Let's hope no one translate with a comma in it:)
p = keyseq.indexOf(u',');
if (-1 != p) {
- if (p == keyseq.length() - 1) { // Last comma 'Ctrl+,'
+ if (p == keyseq.size() - 1) { // Last comma 'Ctrl+,'
p = -1;
} else {
if (u',' == keyseq.at(p+1)) // e.g. 'Ctrl+,, Shift+,,'
@@ -1020,9 +1016,9 @@ int QKeySequence::assign(const QString &ks, QKeySequence::SequenceFormat format)
}
}
}
- QString part = keyseq.left(-1 == p ? keyseq.length() : p - diff);
- keyseq = keyseq.right(-1 == p ? 0 : keyseq.length() - (p + 1));
- d->key[n] = QKeySequencePrivate::decodeString(std::move(part), format);
+ QString part = keyseq.left(-1 == p ? keyseq.size() : p - diff);
+ keyseq = keyseq.right(-1 == p ? 0 : keyseq.size() - (p + 1));
+ d->key[n] = QKeySequencePrivate::decodeString(std::move(part), format).toCombined();
++n;
}
return n;
@@ -1040,15 +1036,7 @@ Q_DECLARE_TYPEINFO(QModifKeyName, Q_RELOCATABLE_TYPE);
Q_GLOBAL_STATIC(QList<QModifKeyName>, globalModifs)
Q_GLOBAL_STATIC(QList<QModifKeyName>, globalPortableModifs)
-/*!
- Constructs a single key from the string \a str.
-*/
-int QKeySequence::decodeString(const QString &str)
-{
- return QKeySequencePrivate::decodeString(str, NativeText);
-}
-
-int QKeySequencePrivate::decodeString(QString accel, QKeySequence::SequenceFormat format)
+QKeyCombination QKeySequencePrivate::decodeString(QString accel, QKeySequence::SequenceFormat format)
{
Q_ASSERT(!accel.isEmpty());
@@ -1060,7 +1048,7 @@ int QKeySequencePrivate::decodeString(QString accel, QKeySequence::SequenceForma
if (nativeText) {
gmodifs = globalModifs();
if (gmodifs->isEmpty()) {
-#if defined(Q_OS_MACOS)
+#if defined(Q_OS_APPLE)
const bool dontSwap = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta);
if (dontSwap)
*gmodifs << QModifKeyName(Qt::META, QChar(kCommandUnicode));
@@ -1073,20 +1061,20 @@ int QKeySequencePrivate::decodeString(QString accel, QKeySequence::SequenceForma
*gmodifs << QModifKeyName(Qt::META, QChar(kControlUnicode));
*gmodifs << QModifKeyName(Qt::SHIFT, QChar(kShiftUnicode));
#endif
- *gmodifs << QModifKeyName(Qt::CTRL, "ctrl+"_L1)
- << QModifKeyName(Qt::SHIFT, "shift+"_L1)
- << QModifKeyName(Qt::ALT, "alt+"_L1)
- << QModifKeyName(Qt::META, "meta+"_L1)
- << QModifKeyName(Qt::KeypadModifier, "num+"_L1);
+ *gmodifs << QModifKeyName(Qt::CTRL, u"ctrl+"_s)
+ << QModifKeyName(Qt::SHIFT, u"shift+"_s)
+ << QModifKeyName(Qt::ALT, u"alt+"_s)
+ << QModifKeyName(Qt::META, u"meta+"_s)
+ << QModifKeyName(Qt::KeypadModifier, u"num+"_s);
}
} else {
gmodifs = globalPortableModifs();
if (gmodifs->isEmpty()) {
- *gmodifs << QModifKeyName(Qt::CTRL, "ctrl+"_L1)
- << QModifKeyName(Qt::SHIFT, "shift+"_L1)
- << QModifKeyName(Qt::ALT, "alt+"_L1)
- << QModifKeyName(Qt::META, "meta+"_L1)
- << QModifKeyName(Qt::KeypadModifier, "num+"_L1);
+ *gmodifs << QModifKeyName(Qt::CTRL, u"ctrl+"_s)
+ << QModifKeyName(Qt::SHIFT, u"shift+"_s)
+ << QModifKeyName(Qt::ALT, u"alt+"_s)
+ << QModifKeyName(Qt::META, u"meta+"_s)
+ << QModifKeyName(Qt::KeypadModifier, u"num+"_s);
}
}
@@ -1102,7 +1090,7 @@ int QKeySequencePrivate::decodeString(QString accel, QKeySequence::SequenceForma
modifs += *gmodifs; // Test non-translated ones last
QString sl = accel;
-#if defined(Q_OS_MACOS)
+#if defined(Q_OS_APPLE)
for (int i = 0; i < modifs.size(); ++i) {
const QModifKeyName &mkf = modifs.at(i);
if (sl.contains(mkf.name)) {
@@ -1115,8 +1103,8 @@ int QKeySequencePrivate::decodeString(QString accel, QKeySequence::SequenceForma
return Qt::Key_unknown;
#endif
- int i = 0;
- int lastI = 0;
+ qsizetype i = 0;
+ qsizetype lastI = 0;
while ((i = sl.indexOf(u'+', i + 1)) != -1) {
const QStringView sub = QStringView{sl}.mid(lastI, i - lastI + 1);
// If we get here the shortcuts contains at least one '+'. We break up
@@ -1128,9 +1116,9 @@ int QKeySequencePrivate::decodeString(QString accel, QKeySequence::SequenceForma
// except for a single '+' at the end of the string.
// Only '+' can have length 1.
- if (sub.length() == 1) {
+ if (sub.size() == 1) {
// Make sure we only encounter a single '+' at the end of the accel
- if (accel.lastIndexOf(u'+') != accel.length()-1)
+ if (accel.lastIndexOf(u'+') != accel.size()-1)
return Qt::Key_unknown;
} else {
// Identify the modifier
@@ -1150,15 +1138,15 @@ int QKeySequencePrivate::decodeString(QString accel, QKeySequence::SequenceForma
lastI = i + 1;
}
- int p = accel.lastIndexOf(u'+', accel.length() - 2); // -2 so that Ctrl++ works
+ qsizetype p = accel.lastIndexOf(u'+', accel.size() - 2); // -2 so that Ctrl++ works
QStringView accelRef(accel);
if (p > 0)
accelRef = accelRef.mid(p + 1);
int fnum = 0;
- if (accelRef.length() == 1) {
-#if defined(Q_OS_MACOS)
- int qtKey = qtkeyForMacSymbol(accelRef.at(0));
+ if (accelRef.size() == 1) {
+#if defined(Q_OS_APPLE)
+ int qtKey = qtkeyForAppleSymbol(accelRef.at(0));
if (qtKey != -1) {
ret |= qtKey;
} else
@@ -1193,17 +1181,7 @@ int QKeySequencePrivate::decodeString(QString accel, QKeySequence::SequenceForma
if (!found)
return Qt::Key_unknown;
}
- return ret;
-}
-
-/*!
- Creates a shortcut string for \a key. For example,
- Qt::CTRL+Qt::Key_O gives "Ctrl+O". The strings, "Ctrl", "Shift", etc. are
- translated (using QObject::tr()) in the "QShortcut" context.
- */
-QString QKeySequence::encodeString(int key)
-{
- return QKeySequencePrivate::encodeString(key, NativeText);
+ return QKeyCombination::fromCombined(ret);
}
static inline void addKey(QString &str, const QString &theKey, QKeySequence::SequenceFormat format)
@@ -1220,20 +1198,24 @@ static inline void addKey(QString &str, const QString &theKey, QKeySequence::Seq
str += theKey;
}
-QString QKeySequencePrivate::encodeString(int key, QKeySequence::SequenceFormat format)
+QString QKeySequencePrivate::encodeString(QKeyCombination keyCombination, QKeySequence::SequenceFormat format)
{
bool nativeText = (format == QKeySequence::NativeText);
QString s;
+ const auto key = keyCombination.key();
+
// Handle -1 (Invalid Key) and Qt::Key_unknown gracefully
- if (key == -1 || key == Qt::Key_unknown)
+ if (keyCombination.toCombined() == -1 || key == Qt::Key_unknown)
return s;
-#if defined(Q_OS_MACOS)
+ const auto modifiers = keyCombination.keyboardModifiers();
+
+#if defined(Q_OS_APPLE)
if (nativeText) {
- // On OS X the order (by default) is Meta, Alt, Shift, Control.
+ // On Apple platforms the order (by default) is Meta, Alt, Shift, Control.
// If the AA_MacDontSwapCtrlAndMeta is enabled, then the order
- // is Ctrl, Alt, Shift, Meta. The macSymbolForQtKey does this swap
+ // is Ctrl, Alt, Shift, Meta. The appleSymbolForQtKey helper does this swap
// for us, which means that we have to adjust our order here.
// The upshot is a lot more infrastructure to keep the number of
// if tests down and the code relatively clean.
@@ -1252,33 +1234,33 @@ QString QKeySequencePrivate::encodeString(int key, QKeySequence::SequenceFormat
}
for (int i = 0; modifierOrder[i] != 0; ++i) {
- if (key & modifierOrder[i])
- s += qt_macSymbolForQtKey(qtkeyOrder[i]);
+ if (modifiers & modifierOrder[i])
+ s += appleSymbolForQtKey(qtkeyOrder[i]);
}
} else
#endif
{
// On other systems the order is Meta, Control, Alt, Shift
- if ((key & Qt::META) == Qt::META)
+ if (modifiers & Qt::MetaModifier)
s = nativeText ? QCoreApplication::translate("QShortcut", "Meta") : QString::fromLatin1("Meta");
- if ((key & Qt::CTRL) == Qt::CTRL)
+ if (modifiers & Qt::ControlModifier)
addKey(s, nativeText ? QCoreApplication::translate("QShortcut", "Ctrl") : QString::fromLatin1("Ctrl"), format);
- if ((key & Qt::ALT) == Qt::ALT)
+ if (modifiers & Qt::AltModifier)
addKey(s, nativeText ? QCoreApplication::translate("QShortcut", "Alt") : QString::fromLatin1("Alt"), format);
- if ((key & Qt::SHIFT) == Qt::SHIFT)
+ if (modifiers & Qt::ShiftModifier)
addKey(s, nativeText ? QCoreApplication::translate("QShortcut", "Shift") : QString::fromLatin1("Shift"), format);
}
- if ((key & Qt::KeypadModifier) == Qt::KeypadModifier)
+ if (modifiers & Qt::KeypadModifier)
addKey(s, nativeText ? QCoreApplication::translate("QShortcut", "Num") : QString::fromLatin1("Num"), format);
- QString p = keyName(key, format);
+ QString keyName = QKeySequencePrivate::keyName(key, format);
-#if defined(Q_OS_MACOS)
+#if defined(Q_OS_APPLE)
if (nativeText)
- s += p;
+ s += keyName;
else
#endif
- addKey(s, p, format);
+ addKey(s, keyName, format);
return s;
}
@@ -1290,10 +1272,9 @@ QString QKeySequencePrivate::encodeString(int key, QKeySequence::SequenceFormat
This static method is used by encodeString() and by the D-Bus menu exporter.
*/
-QString QKeySequencePrivate::keyName(int key, QKeySequence::SequenceFormat format)
+QString QKeySequencePrivate::keyName(Qt::Key key, QKeySequence::SequenceFormat format)
{
bool nativeText = (format == QKeySequence::NativeText);
- key &= ~(Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier);
QString p;
if (key && key < Qt::Key_Escape && key != Qt::Key_Space) {
@@ -1308,9 +1289,9 @@ QString QKeySequencePrivate::keyName(int key, QKeySequence::SequenceFormat forma
: QString::fromLatin1("F%1").arg(key - Qt::Key_F1 + 1);
} else if (key) {
int i=0;
-#if defined(Q_OS_MACOS)
+#if defined(Q_OS_APPLE)
if (nativeText) {
- QChar ch = qt_macSymbolForQtKey(key);
+ QChar ch = appleSymbolForQtKey(key);
if (!ch.isNull())
p = ch;
else
@@ -1318,7 +1299,7 @@ QString QKeySequencePrivate::keyName(int key, QKeySequence::SequenceFormat forma
} else
#endif
{
-#if defined(Q_OS_MACOS)
+#if defined(Q_OS_APPLE)
NonSymbol:
#endif
while (i < numKeyNames) {
@@ -1431,6 +1412,7 @@ bool QKeySequence::operator==(const QKeySequence &other) const
/*!
\since 5.6
+ \relates QKeySequence
Calculates the hash value of \a key, using
\a seed to seed the calculation.
@@ -1507,7 +1489,7 @@ bool QKeySequence::isDetached() const
If the key sequence has no keys, an empty string is returned.
- On \macos, the string returned resembles the sequence that is
+ On Apple platforms, the string returned resembles the sequence that is
shown in the menu bar if \a format is
QKeySequence::NativeText; otherwise, the string uses the
"portable" format, suitable for writing to a file.
@@ -1521,10 +1503,10 @@ QString QKeySequence::toString(SequenceFormat format) const
// look like our latin case on Windows and X11
int end = count();
for (int i = 0; i < end; ++i) {
- finalString += d->encodeString(d->key[i], format);
+ finalString += d->encodeString(QKeyCombination::fromCombined(d->key[i]), format);
finalString += ", "_L1;
}
- finalString.truncate(finalString.length() - 2);
+ finalString.truncate(finalString.size() - 2);
return finalString;
}
@@ -1553,7 +1535,7 @@ QList<QKeySequence> QKeySequence::listFromString(const QString &str, SequenceFor
QList<QKeySequence> result;
const QStringList strings = str.split("; "_L1);
- result.reserve(strings.count());
+ result.reserve(strings.size());
for (const QString &string : strings) {
result << fromString(string, format);
}
@@ -1577,7 +1559,7 @@ QString QKeySequence::listToString(const QList<QKeySequence> &list, SequenceForm
result += sequence.toString(format);
result += "; "_L1;
}
- result.truncate(result.length() - 2);
+ result.truncate(result.size() - 2);
return result;
}
diff --git a/src/gui/kernel/qkeysequence.h b/src/gui/kernel/qkeysequence.h
index 3e99f669de..4dae26a755 100644
--- a/src/gui/kernel/qkeysequence.h
+++ b/src/gui/kernel/qkeysequence.h
@@ -17,12 +17,12 @@ class QKeySequence;
/*****************************************************************************
QKeySequence stream functions
*****************************************************************************/
-#if !defined(QT_NO_DATASTREAM) || defined(Q_CLANG_QDOC)
+#if !defined(QT_NO_DATASTREAM) || defined(Q_QDOC)
Q_GUI_EXPORT QDataStream &operator<<(QDataStream &in, const QKeySequence &ks);
Q_GUI_EXPORT QDataStream &operator>>(QDataStream &out, QKeySequence &ks);
#endif
-#if defined(Q_CLANG_QDOC)
+#if defined(Q_QDOC)
void qt_set_sequence_auto_mnemonic(bool b);
#endif
@@ -115,6 +115,7 @@ public:
NativeText,
PortableText
};
+ Q_ENUM(SequenceFormat)
QKeySequence();
QKeySequence(const QString &key, SequenceFormat format = NativeText);
@@ -135,6 +136,7 @@ public:
PartialMatch,
ExactMatch
};
+ Q_ENUM(SequenceMatch);
QString toString(SequenceFormat format = PortableText) const;
static QKeySequence fromString(const QString &str, SequenceFormat format = PortableText);
@@ -165,8 +167,6 @@ public:
bool isDetached() const;
private:
- static int decodeString(const QString &ks);
- static QString encodeString(int key);
int assign(const QString &str);
int assign(const QString &str, SequenceFormat format);
void setKey(QKeyCombination key, int index);
@@ -186,7 +186,7 @@ public:
Q_DECLARE_SHARED(QKeySequence)
-#if !defined(QT_NO_DEBUG_STREAM) || defined(Q_CLANG_QDOC)
+#if !defined(QT_NO_DEBUG_STREAM) || defined(Q_QDOC)
Q_GUI_EXPORT QDebug operator<<(QDebug, const QKeySequence &);
#endif
diff --git a/src/gui/kernel/qkeysequence_p.h b/src/gui/kernel/qkeysequence_p.h
index 2f9a0b09aa..8b98e3778a 100644
--- a/src/gui/kernel/qkeysequence_p.h
+++ b/src/gui/kernel/qkeysequence_p.h
@@ -35,7 +35,7 @@ struct QKeyBinding
class QKeySequencePrivate
{
public:
- enum { MaxKeyCount = 4 }; // also used in QKeySequenceEdit
+ static constexpr int MaxKeyCount = 4 ; // also used in QKeySequenceEdit
constexpr QKeySequencePrivate() : ref(1), key{} {}
inline QKeySequencePrivate(const QKeySequencePrivate &copy) : ref(1)
{
@@ -44,10 +44,10 @@ public:
}
QAtomicInt ref;
int key[MaxKeyCount];
- static QString encodeString(int key, QKeySequence::SequenceFormat format);
+ static QString encodeString(QKeyCombination keyCombination, QKeySequence::SequenceFormat format);
// used in dbusmenu
- Q_GUI_EXPORT static QString keyName(int key, QKeySequence::SequenceFormat format);
- static int decodeString(QString accel, QKeySequence::SequenceFormat format);
+ Q_GUI_EXPORT static QString keyName(Qt::Key key, QKeySequence::SequenceFormat format);
+ static QKeyCombination decodeString(QString accel, QKeySequence::SequenceFormat format);
};
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qoffscreensurface.h b/src/gui/kernel/qoffscreensurface.h
index 15ad430dae..410b6d2b61 100644
--- a/src/gui/kernel/qoffscreensurface.h
+++ b/src/gui/kernel/qoffscreensurface.h
@@ -8,7 +8,7 @@
#include <QtCore/QObject>
#include <QtCore/qnativeinterface.h>
#include <QtGui/qsurface.h>
-Q_MOC_INCLUDE(<QScreen>)
+Q_MOC_INCLUDE(<QtGui/qscreen.h>)
QT_BEGIN_NAMESPACE
diff --git a/src/gui/kernel/qoffscreensurface_platform.h b/src/gui/kernel/qoffscreensurface_platform.h
index a093c08b15..33e72c7e17 100644
--- a/src/gui/kernel/qoffscreensurface_platform.h
+++ b/src/gui/kernel/qoffscreensurface_platform.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
namespace QNativeInterface {
-#if defined(Q_OS_ANDROID) || defined(Q_CLANG_QDOC)
+#if defined(Q_OS_ANDROID) || defined(Q_QDOC)
struct Q_GUI_EXPORT QAndroidOffscreenSurface
{
QT_DECLARE_NATIVE_INTERFACE(QAndroidOffscreenSurface, 1, QOffscreenSurface)
diff --git a/src/gui/kernel/qopenglcontext.cpp b/src/gui/kernel/qopenglcontext.cpp
index 5849c7318e..dd41318f72 100644
--- a/src/gui/kernel/qopenglcontext.cpp
+++ b/src/gui/kernel/qopenglcontext.cpp
@@ -42,7 +42,7 @@ static QOpenGLContext *global_share_context = nullptr;
#ifndef QT_NO_DEBUG
QHash<QOpenGLContext *, bool> QOpenGLContextPrivate::makeCurrentTracker;
-QMutex QOpenGLContextPrivate::makeCurrentTrackerMutex;
+Q_CONSTINIT QMutex QOpenGLContextPrivate::makeCurrentTrackerMutex;
#endif
/*!
@@ -69,6 +69,7 @@ QOpenGLContext *qt_gl_global_share_context()
/*!
\class QOpenGLContext
+ \ingroup painting-3D
\inmodule QtGui
\since 5.0
\brief The QOpenGLContext class represents a native OpenGL context, enabling
@@ -137,6 +138,22 @@ QOpenGLContext *qt_gl_global_share_context()
application is portable between different platforms. However, if you use
QOpenGLFunctions::glBindFramebuffer(), this is done automatically for you.
+ \warning WebAssembly
+
+ We recommend that only one QOpenGLContext is made current with a QSurface,
+ for the entire lifetime of the QSurface. Should more than once context be used,
+ it is important to understand that multiple QOpenGLContext instances may be
+ backed by the same native context underneath with the WebAssembly platform.
+ Therefore, calling makeCurrent() with the same QSurface on two QOpenGLContext
+ objects may not switch to a different native context in the second call. As
+ a result, any OpenGL state changes done after the second makeCurrent() may
+ alter the state of the first QOpenGLContext as well, as they are all backed
+ by the same native context.
+
+ \note This means that when targeting WebAssembly with existing OpenGL-based
+ Qt code, some porting may be required to cater to these limitations.
+
+
\sa QOpenGLFunctions, QOpenGLBuffer, QOpenGLShaderProgram, QOpenGLFramebufferObject
*/
@@ -153,6 +170,9 @@ QOpenGLContext *QOpenGLContextPrivate::setCurrentContext(QOpenGLContext *context
qWarning("No QTLS available. currentContext won't work");
return nullptr;
}
+ if (!context)
+ return nullptr;
+
threadContext = new QGuiGLThreadContext;
qwindow_context_storage()->setLocalData(threadContext);
}
@@ -360,6 +380,10 @@ bool QOpenGLContext::create()
return isValid();
}
+QOpenGLContextPrivate::~QOpenGLContextPrivate()
+{
+}
+
void QOpenGLContextPrivate::adopt(QPlatformOpenGLContext *context)
{
Q_Q(QOpenGLContext);
@@ -395,30 +419,47 @@ void QOpenGLContextPrivate::adopt(QPlatformOpenGLContext *context)
void QOpenGLContext::destroy()
{
Q_D(QOpenGLContext);
+
+ // Notify that the native context and the QPlatformOpenGLContext are going
+ // to go away.
if (d->platformGLContext)
emit aboutToBeDestroyed();
- if (QOpenGLContext::currentContext() == this)
- doneCurrent();
- if (d->shareGroup)
- d->shareGroup->d_func()->removeContext(this);
- d->shareGroup = nullptr;
- delete d->platformGLContext;
- d->platformGLContext = nullptr;
- delete d->functions;
- d->functions = nullptr;
+ // Invoke callbacks for helpers and invalidate.
if (d->textureFunctionsDestroyCallback) {
d->textureFunctionsDestroyCallback();
d->textureFunctionsDestroyCallback = nullptr;
}
d->textureFunctions = nullptr;
+ delete d->versionFunctions;
+ d->versionFunctions = nullptr;
+
if (d->vaoHelperDestroyCallback) {
Q_ASSERT(d->vaoHelper);
d->vaoHelperDestroyCallback(d->vaoHelper);
d->vaoHelperDestroyCallback = nullptr;
}
d->vaoHelper = nullptr;
+
+ // Tear down function wrappers.
+ delete d->versionFunctions;
+ d->versionFunctions = nullptr;
+
+ delete d->functions;
+ d->functions = nullptr;
+
+ // Clean up and destroy the native context machinery.
+ if (QOpenGLContext::currentContext() == this)
+ doneCurrent();
+
+ if (d->shareGroup)
+ d->shareGroup->d_func()->removeContext(this);
+
+ d->shareGroup = nullptr;
+
+ delete d->platformGLContext;
+ d->platformGLContext = nullptr;
}
/*!
@@ -430,6 +471,11 @@ void QOpenGLContext::destroy()
If you wish to make the context current in order to do clean-up, make sure
to only connect to the signal using a direct connection.
+
+ \note In Qt for Python, this signal will not be received when emitted
+ from the destructor of QOpenGLWidget or QOpenGLWindow due to the Python
+ instance already being destroyed. We recommend doing cleanups
+ in QWidget::hideEvent() instead.
*/
/*!
@@ -995,6 +1041,9 @@ QOpenGLContextGroup *QOpenGLContextGroup::currentContextGroup()
return current ? current->shareGroup() : nullptr;
}
+QOpenGLContextGroupPrivate::~QOpenGLContextGroupPrivate()
+ = default;
+
void QOpenGLContextGroupPrivate::addContext(QOpenGLContext *ctx)
{
const auto locker = qt_scoped_lock(m_mutex);
@@ -1136,6 +1185,10 @@ void QOpenGLSharedResource::free()
\inmodule QtGui
*/
+
+QOpenGLSharedResourceGuard::~QOpenGLSharedResourceGuard()
+ = default;
+
void QOpenGLSharedResourceGuard::freeResource(QOpenGLContext *context)
{
if (m_id) {
@@ -1235,6 +1288,9 @@ void QOpenGLMultiGroupSharedResource::cleanup(QOpenGLContextGroup *group, QOpenG
m_groups.removeOne(group);
}
+QOpenGLContextVersionFunctionHelper::~QOpenGLContextVersionFunctionHelper()
+ = default;
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const QOpenGLContext *ctx)
{
diff --git a/src/gui/kernel/qopenglcontext_p.h b/src/gui/kernel/qopenglcontext_p.h
index c0ceb682ce..a0e8eda679 100644
--- a/src/gui/kernel/qopenglcontext_p.h
+++ b/src/gui/kernel/qopenglcontext_p.h
@@ -74,6 +74,7 @@ public:
, m_func(func)
{
}
+ ~QOpenGLSharedResourceGuard() override;
GLuint id() const { return m_id; }
@@ -99,6 +100,7 @@ public:
, m_refs(0)
{
}
+ ~QOpenGLContextGroupPrivate() override;
void addContext(QOpenGLContext *ctx);
void removeContext(QOpenGLContext *ctx);
@@ -160,7 +162,7 @@ class QOpenGLVertexArrayObjectHelper;
class Q_GUI_EXPORT QOpenGLContextVersionFunctionHelper
{
public:
- virtual ~QOpenGLContextVersionFunctionHelper() {}
+ virtual ~QOpenGLContextVersionFunctionHelper();
};
class Q_GUI_EXPORT QOpenGLContextPrivate : public QObjectPrivate
@@ -190,13 +192,7 @@ public:
requestedFormat = QSurfaceFormat::defaultFormat();
}
- ~QOpenGLContextPrivate()
- {
- //do not delete the QOpenGLContext handle here as it is deleted in
- //QWidgetPrivate::deleteTLSysExtra()
-
- delete versionFunctions;
- }
+ ~QOpenGLContextPrivate() override;
void adopt(QPlatformOpenGLContext *);
diff --git a/src/gui/kernel/qopenglcontext_platform.h b/src/gui/kernel/qopenglcontext_platform.h
index c575b56c31..1f84cf6ce3 100644
--- a/src/gui/kernel/qopenglcontext_platform.h
+++ b/src/gui/kernel/qopenglcontext_platform.h
@@ -34,7 +34,7 @@ typedef void *EGLDisplay;
typedef void *EGLConfig;
#endif
-#if !defined(Q_OS_MACOS) && defined(Q_CLANG_QDOC)
+#if !defined(Q_OS_MACOS) && defined(Q_QDOC)
typedef void *NSOpenGLContext;
#endif
@@ -42,7 +42,7 @@ QT_BEGIN_NAMESPACE
namespace QNativeInterface {
-#if defined(Q_OS_MACOS) || defined(Q_CLANG_QDOC)
+#if defined(Q_OS_MACOS) || defined(Q_QDOC)
struct Q_GUI_EXPORT QCocoaGLContext
{
QT_DECLARE_NATIVE_INTERFACE(QCocoaGLContext, 1, QOpenGLContext)
@@ -51,7 +51,7 @@ struct Q_GUI_EXPORT QCocoaGLContext
};
#endif
-#if defined(Q_OS_WIN) || defined(Q_CLANG_QDOC)
+#if defined(Q_OS_WIN) || defined(Q_QDOC)
struct Q_GUI_EXPORT QWGLContext
{
QT_DECLARE_NATIVE_INTERFACE(QWGLContext, 1, QOpenGLContext)
@@ -61,7 +61,7 @@ struct Q_GUI_EXPORT QWGLContext
};
#endif
-#if QT_CONFIG(xcb_glx_plugin) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(xcb_glx_plugin) || defined(Q_QDOC)
struct Q_GUI_EXPORT QGLXContext
{
QT_DECLARE_NATIVE_INTERFACE(QGLXContext, 1, QOpenGLContext)
@@ -71,7 +71,7 @@ struct Q_GUI_EXPORT QGLXContext
};
#endif
-#if QT_CONFIG(egl) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(egl) || defined(Q_QDOC)
struct Q_GUI_EXPORT QEGLContext
{
QT_DECLARE_NATIVE_INTERFACE(QEGLContext, 1, QOpenGLContext)
@@ -79,6 +79,8 @@ struct Q_GUI_EXPORT QEGLContext
virtual EGLContext nativeContext() const = 0;
virtual EGLConfig config() const = 0;
virtual EGLDisplay display() const = 0;
+
+ virtual void invalidateContext() = 0;
};
#endif
diff --git a/src/gui/kernel/qpaintdevicewindow.cpp b/src/gui/kernel/qpaintdevicewindow.cpp
index bc7ac89b03..9e8c6ae5a8 100644
--- a/src/gui/kernel/qpaintdevicewindow.cpp
+++ b/src/gui/kernel/qpaintdevicewindow.cpp
@@ -171,6 +171,8 @@ bool QPaintDeviceWindow::event(QEvent *event)
auto region = QRect(QPoint(0, 0), size());
d->doFlush(region); // Will end up calling paintEvent
return true;
+ } else if (event->type() == QEvent::Resize) {
+ d->handleResizeEvent();
}
return QWindow::event(event);
diff --git a/src/gui/kernel/qpaintdevicewindow_p.h b/src/gui/kernel/qpaintdevicewindow_p.h
index 32db2cb7a4..85c11cbf91 100644
--- a/src/gui/kernel/qpaintdevicewindow_p.h
+++ b/src/gui/kernel/qpaintdevicewindow_p.h
@@ -31,6 +31,8 @@ public:
QPaintDeviceWindowPrivate();
~QPaintDeviceWindowPrivate() override;
+ virtual void handleResizeEvent() {}
+
virtual void beginPaint(const QRegion &region)
{
Q_UNUSED(region);
@@ -82,7 +84,7 @@ public:
void markWindowAsDirty()
{
Q_Q(QPaintDeviceWindow);
- dirtyRegion += QRect(QPoint(0, 0), q->size());
+ dirtyRegion = QRect(QPoint(0, 0), q->size());
}
private:
diff --git a/src/gui/kernel/qpalette.cpp b/src/gui/kernel/qpalette.cpp
index 311658d26c..256ea52f01 100644
--- a/src/gui/kernel/qpalette.cpp
+++ b/src/gui/kernel/qpalette.cpp
@@ -1,8 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qpalette.h"
-#include "qguiapplication.h"
+#include "qpalette_p.h"
#include "qguiapplication_p.h"
#include "qdatastream.h"
#include "qvariant.h"
@@ -12,39 +11,61 @@
QT_BEGIN_NAMESPACE
-static int qt_palette_count = 1;
+static_assert(QPalettePrivate::bitPosition(QPalette::ColorGroup(QPalette::NColorGroups - 1),
+ QPalette::ColorRole(QPalette::NColorRoles - 1))
+ < sizeof(QPalette::ResolveMask) * CHAR_BIT,
+ "The resolve mask type is not wide enough to fit the entire bit mask.");
-static constexpr QPalette::ResolveMask colorRoleOffset(QPalette::ColorGroup colorGroup)
+static QColor qt_mix_colors(QColor a, QColor b)
{
- return qToUnderlying(QPalette::NColorRoles) * qToUnderlying(colorGroup);
+ return QColor((a.red() + b.red()) / 2, (a.green() + b.green()) / 2,
+ (a.blue() + b.blue()) / 2, (a.alpha() + b.alpha()) / 2);
}
-static constexpr QPalette::ResolveMask bitPosition(QPalette::ColorGroup colorGroup,
- QPalette::ColorRole colorRole)
-{
- return colorRole + colorRoleOffset(colorGroup);
-}
+/*!
+ \internal
-static_assert(bitPosition(QPalette::ColorGroup(QPalette::NColorGroups - 1),
- QPalette::ColorRole(QPalette::NColorRoles - 1))
- < sizeof(QPalette::ResolveMask) * CHAR_BIT,
- "The resolve mask type is not wide enough to fit the entire bit mask.");
+ Derive undefined \l PlaceholderText colors from \l Text colors.
+ Unless already set, PlaceholderText colors will be derived from their Text pendents.
+ Colors of existing PlaceHolderText brushes will not be replaced.
-class QPalettePrivate
+ \a alpha represents the dim factor as a percentage. By default, a PlaceHolderText color
+ becomes a 50% more transparent version of the corresponding Text color.
+*/
+static void qt_placeholder_from_text(QPalette &pal, int alpha = 50)
{
-public:
- QPalettePrivate() : ref(1), ser_no(qt_palette_count++), detach_no(0) { }
- QAtomicInt ref;
- QBrush br[QPalette::NColorGroups][QPalette::NColorRoles];
- QPalette::ResolveMask resolveMask = {0};
- int ser_no;
- int detach_no;
-};
+ if (alpha < 0 or alpha > 100)
+ return;
-static QColor qt_mix_colors(QColor a, QColor b)
+ for (int cg = 0; cg < int(QPalette::NColorGroups); ++cg) {
+ const QPalette::ColorGroup group = QPalette::ColorGroup(cg);
+
+ // skip if the brush has been set already
+ if (!pal.isBrushSet(group, QPalette::PlaceholderText)) {
+ QColor c = pal.color(group, QPalette::Text);
+ const int a = (c.alpha() * alpha) / 100;
+ c.setAlpha(a);
+ pal.setColor(group, QPalette::PlaceholderText, c);
+ }
+ }
+}
+
+static void qt_ensure_default_accent_color(QPalette &pal)
{
- return QColor((a.red() + b.red()) / 2, (a.green() + b.green()) / 2,
- (a.blue() + b.blue()) / 2, (a.alpha() + b.alpha()) / 2);
+ // have a lighter/darker factor handy, depending on dark/light heuristics
+ const int lighter = pal.base().color().lightness() > pal.text().color().lightness() ? 130 : 70;
+
+ // Act only for color groups where no accent color is set
+ for (int i = 0; i < QPalette::NColorGroups; ++i) {
+ const QPalette::ColorGroup group = static_cast<QPalette::ColorGroup>(i);
+ if (!pal.isBrushSet(group, QPalette::Accent)) {
+ // Default to highlight if available, otherwise use a shade of base
+ const QBrush accentBrush = pal.isBrushSet(group, QPalette::Highlight)
+ ? pal.brush(group, QPalette::Highlight)
+ : pal.brush(group, QPalette::Base).color().lighter(lighter);
+ pal.setBrush(group, QPalette::Accent, accentBrush);
+ }
+ }
}
static void qt_palette_from_color(QPalette &pal, const QColor &button)
@@ -69,6 +90,9 @@ static void qt_palette_from_color(QPalette &pal, const QColor &button)
pal.setColorGroup(QPalette::Disabled, buttonBrushDark, buttonBrush, buttonBrushLight150,
buttonBrushDark, buttonBrushDark150, buttonBrushDark,
whiteBrush, buttonBrush, buttonBrush);
+
+ qt_placeholder_from_text(pal);
+ qt_ensure_default_accent_color(pal);
}
/*!
@@ -270,6 +294,15 @@ static void qt_palette_from_color(QPalette &pal, const QColor &button)
*/
/*!
+ \fn const QBrush & QPalette::accent() const
+ \since 6.6
+
+ Returns the accent brush of the current color group.
+
+ \sa ColorRole, brush()
+*/
+
+/*!
\fn const QBrush & QPalette::link() const
Returns the unvisited link text brush of the current color group.
@@ -469,6 +502,13 @@ static void qt_palette_from_color(QPalette &pal, const QColor &button)
item. By default, the highlight color is
Qt::darkBlue.
+ \value [since 6.6] Accent
+ A color that typically contrasts or complements
+ Base, Window and Button colors. It usually represents
+ the users' choice of desktop personalisation.
+ Styling of interactive components is a typical use case.
+ Unless explicitly set, it defaults to Highlight.
+
\value HighlightedText A text color that contrasts with \c Highlight.
By default, the highlighted text color is Qt::white.
@@ -556,6 +596,9 @@ QPalette::QPalette(const QBrush &windowText, const QBrush &button,
init();
setColorGroup(All, windowText, button, light, dark, mid, text, bright_text,
base, window);
+
+ qt_placeholder_from_text(*this);
+ qt_ensure_default_accent_color(*this);
}
@@ -611,6 +654,9 @@ QPalette::QPalette(const QColor &button, const QColor &window)
setColorGroup(Disabled, disabledForeground, buttonBrush, buttonBrushLight150,
buttonBrushDark, buttonBrushDark150, disabledForeground,
whiteBrush, baseBrush, windowBrush);
+
+ qt_placeholder_from_text(*this);
+ qt_ensure_default_accent_color(*this);
}
/*!
@@ -710,7 +756,7 @@ const QBrush &QPalette::brush(ColorGroup gr, ColorRole cr) const
gr = Active;
}
}
- return d->br[gr][cr];
+ return d->data->br[gr][cr];
}
/*!
@@ -748,11 +794,18 @@ void QPalette::setBrush(ColorGroup cg, ColorRole cr, const QBrush &b)
cg = Active;
}
- if (d->br[cg][cr] != b) {
+ const auto newResolveMask = d->resolveMask | ResolveMask(1) << QPalettePrivate::bitPosition(cg, cr);
+ const auto valueChanged = d->data->br[cg][cr] != b;
+
+ if (valueChanged) {
+ detach();
+ d->data.detach();
+ d->data->br[cg][cr] = b;
+ } else if (d->resolveMask != newResolveMask) {
detach();
- d->br[cg][cr] = b;
- d->resolveMask |= ResolveMask(1) << bitPosition(cg, cr);
}
+
+ d->resolveMask = newResolveMask;
}
/*!
@@ -771,6 +824,10 @@ void QPalette::setBrush(ColorGroup cg, ColorRole cr, const QBrush &b)
*/
bool QPalette::isBrushSet(ColorGroup cg, ColorRole cr) const
{
+ // NoRole has no resolve mask and should never be set anyway
+ if (cr == NoRole)
+ return false;
+
if (cg == Current)
cg = currentGroup;
@@ -784,7 +841,7 @@ bool QPalette::isBrushSet(ColorGroup cg, ColorRole cr) const
return false;
}
- return d->resolveMask & (ResolveMask(1) << bitPosition(cg, cr));
+ return d->resolveMask & (ResolveMask(1) << QPalettePrivate::bitPosition(cg, cr));
}
/*!
@@ -793,17 +850,14 @@ bool QPalette::isBrushSet(ColorGroup cg, ColorRole cr) const
void QPalette::detach()
{
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++)
- x->br[grp][role] = d->br[grp][role];
- }
+ QPalettePrivate *x = new QPalettePrivate(d->data);
x->resolveMask = d->resolveMask;
if (!d->ref.deref())
delete d;
d = x;
+ } else {
+ d->detach_no = ++QPalettePrivate::qt_palette_private_count;
}
- ++d->detach_no;
}
/*!
@@ -822,18 +876,24 @@ void QPalette::detach()
Returns \c true (usually quickly) if this palette is equal to \a p;
otherwise returns \c false (slowly).
- \note The current ColorGroup is not taken into account when
- comparing palettes
+ \note The following is not taken into account when comparing palettes:
+ \list
+ \li the \c current ColorGroup
+ \li ColorRole NoRole \since 6.6
+ \endlist
\sa operator!=()
*/
bool QPalette::operator==(const QPalette &p) const
{
- if (isCopyOf(p))
+ if (isCopyOf(p) || d->data == p.d->data)
return true;
for(int grp = 0; grp < (int)NColorGroups; grp++) {
for(int role = 0; role < (int)NColorRoles; role++) {
- if (d->br[grp][role] != p.d->br[grp][role])
+ // Dont't verify NoRole, because it has no resolve bit
+ if (role == NoRole)
+ continue;
+ if (d->data->br[grp][role] != p.d->data->br[grp][role])
return false;
}
}
@@ -867,7 +927,7 @@ bool QPalette::isEqual(QPalette::ColorGroup group1, QPalette::ColorGroup group2)
if (group1 == group2)
return true;
for(int role = 0; role < (int)NColorRoles; role++) {
- if (d->br[group1][role] != d->br[group2][role])
+ if (d->data->br[group1][role] != d->data->br[group2][role])
return false;
}
return true;
@@ -882,7 +942,7 @@ bool QPalette::isEqual(QPalette::ColorGroup group1, QPalette::ColorGroup group2)
*/
qint64 QPalette::cacheKey() const
{
- return (((qint64) d->ser_no) << 32) | ((qint64) (d->detach_no));
+ return (((qint64) d->data->ser_no) << 32) | ((qint64) (d->detach_no));
}
static constexpr QPalette::ResolveMask allResolveMask()
@@ -890,7 +950,7 @@ static constexpr QPalette::ResolveMask allResolveMask()
QPalette::ResolveMask mask = {0};
for (int role = 0; role < int(QPalette::NColorRoles); ++role) {
for (int grp = 0; grp < int(QPalette::NColorGroups); ++grp) {
- mask |= (QPalette::ResolveMask(1) << bitPosition(QPalette::ColorGroup(grp), QPalette::ColorRole(role)));
+ mask |= (QPalette::ResolveMask(1) << QPalettePrivate::bitPosition(QPalette::ColorGroup(grp), QPalette::ColorRole(role)));
}
}
return mask;
@@ -905,7 +965,7 @@ QPalette QPalette::resolve(const QPalette &other) const
if ((*this == other && d->resolveMask == other.d->resolveMask)
|| d->resolveMask == 0) {
QPalette o = other;
- o.d->resolveMask = d->resolveMask;
+ o.setResolveMask(d->resolveMask);
return o;
}
@@ -916,9 +976,14 @@ QPalette QPalette::resolve(const QPalette &other) const
palette.detach();
for (int role = 0; role < int(NColorRoles); ++role) {
+ // Don't resolve NoRole, its bits are needed for Accent (see bitPosition)
+ if (role == NoRole)
+ continue;
+
for (int grp = 0; grp < int(NColorGroups); ++grp) {
- if (!(d->resolveMask & (ResolveMask(1) << bitPosition(ColorGroup(grp), ColorRole(role))))) {
- palette.d->br[grp][role] = other.d->br[grp][role];
+ if (!(d->resolveMask & (ResolveMask(1) << QPalettePrivate::bitPosition(ColorGroup(grp), ColorRole(role))))) {
+ palette.d->data.detach();
+ palette.d->data->br[grp][role] = other.d->data->br[grp][role];
}
}
}
@@ -981,7 +1046,7 @@ QDataStream &operator<<(QDataStream &s, const QPalette &p)
if (s.version() == 1) {
// Qt 1.x
for (int i = 0; i < NumOldRoles; ++i)
- s << p.d->br[grp][oldRoles[i]].color();
+ s << p.d->data->br[grp][oldRoles[i]].color();
} else {
int max = (int)QPalette::NColorRoles;
if (s.version() <= QDataStream::Qt_2_1)
@@ -990,8 +1055,11 @@ QDataStream &operator<<(QDataStream &s, const QPalette &p)
max = QPalette::AlternateBase + 1;
else if (s.version() <= QDataStream::Qt_5_11)
max = QPalette::ToolTipText + 1;
+ else if (s.version() <= QDataStream::Qt_6_5)
+ max = QPalette::PlaceholderText + 1;
+
for (int r = 0; r < max; r++)
- s << p.d->br[grp][r];
+ s << p.d->data->br[grp][r];
}
}
return s;
@@ -1033,15 +1101,25 @@ QDataStream &operator>>(QDataStream &s, QPalette &p)
} else if (s.version() <= QDataStream::Qt_5_11) {
p = QPalette();
max = QPalette::ToolTipText + 1;
+ } else if (s.version() <= QDataStream::Qt_6_5) {
+ p = QPalette();
+ max = QPalette::PlaceholderText + 1;
}
+
QBrush tmp;
for(int grp = 0; grp < (int)QPalette::NColorGroups; ++grp) {
+ const QPalette::ColorGroup group = static_cast<QPalette::ColorGroup>(grp);
for(int role = 0; role < max; ++role) {
s >> tmp;
- p.setBrush((QPalette::ColorGroup)grp, (QPalette::ColorRole)role, tmp);
+ p.setBrush(group, (QPalette::ColorRole)role, tmp);
}
+
+ // Accent defaults to Highlight for stream versions that don't have it.
+ if (s.version() < QDataStream::Qt_6_6)
+ p.setBrush(group, QPalette::Accent, p.brush(group, QPalette::Highlight));
}
+
}
return s;
}
@@ -1088,10 +1166,10 @@ void QPalette::setColorGroup(ColorGroup cg, const QBrush &windowText, const QBru
for (int cr = Highlight; cr <= LinkVisited; ++cr) {
if (cg == All) {
for (int group = Active; group < NColorGroups; ++group) {
- d->resolveMask &= ~(ResolveMask(1) << bitPosition(ColorGroup(group), ColorRole(cr)));
+ d->resolveMask &= ~(ResolveMask(1) << QPalettePrivate::bitPosition(ColorGroup(group), ColorRole(cr)));
}
} else {
- d->resolveMask &= ~(ResolveMask(1) << bitPosition(ColorGroup(cg), ColorRole(cr)));
+ d->resolveMask &= ~(ResolveMask(1) << QPalettePrivate::bitPosition(ColorGroup(cg), ColorRole(cr)));
}
}
}
@@ -1146,47 +1224,6 @@ void QPalette::setColorGroup(ColorGroup cg, const QBrush &foreground, const QBru
setBrush(cg, ToolTipText, toolTipText);
}
-Q_GUI_EXPORT QPalette qt_fusionPalette()
-{
- QColor backGround(239, 239, 239);
- QColor light = backGround.lighter(150);
- QColor mid(backGround.darker(130));
- QColor midLight = mid.lighter(110);
- QColor base = Qt::white;
- QColor disabledBase(backGround);
- QColor dark = backGround.darker(150);
- QColor darkDisabled = QColor(209, 209, 209).darker(110);
- QColor text = Qt::black;
- QColor hightlightedText = Qt::white;
- QColor disabledText = QColor(190, 190, 190);
- QColor button = backGround;
- QColor shadow = dark.darker(135);
- QColor disabledShadow = shadow.lighter(150);
- QColor placeholder = text;
- placeholder.setAlpha(128);
-
- QPalette fusionPalette(Qt::black,backGround,light,dark,mid,text,base);
- fusionPalette.setBrush(QPalette::Midlight, midLight);
- fusionPalette.setBrush(QPalette::Button, button);
- fusionPalette.setBrush(QPalette::Shadow, shadow);
- fusionPalette.setBrush(QPalette::HighlightedText, hightlightedText);
-
- fusionPalette.setBrush(QPalette::Disabled, QPalette::Text, disabledText);
- fusionPalette.setBrush(QPalette::Disabled, QPalette::WindowText, disabledText);
- fusionPalette.setBrush(QPalette::Disabled, QPalette::ButtonText, disabledText);
- fusionPalette.setBrush(QPalette::Disabled, QPalette::Base, disabledBase);
- fusionPalette.setBrush(QPalette::Disabled, QPalette::Dark, darkDisabled);
- fusionPalette.setBrush(QPalette::Disabled, QPalette::Shadow, disabledShadow);
-
- fusionPalette.setBrush(QPalette::Active, QPalette::Highlight, QColor(48, 140, 198));
- fusionPalette.setBrush(QPalette::Inactive, QPalette::Highlight, QColor(48, 140, 198));
- fusionPalette.setBrush(QPalette::Disabled, QPalette::Highlight, QColor(145, 145, 145));
-
- fusionPalette.setBrush(QPalette::PlaceholderText, placeholder);
-
- return fusionPalette;
-}
-
#ifndef QT_NO_DEBUG_STREAM
static QString groupsToString(const QPalette &p, QPalette::ColorRole cr)
{
diff --git a/src/gui/kernel/qpalette.h b/src/gui/kernel/qpalette.h
index a9b1b59eb5..a6162e1090 100644
--- a/src/gui/kernel/qpalette.h
+++ b/src/gui/kernel/qpalette.h
@@ -32,7 +32,7 @@ public:
~QPalette();
QPalette &operator=(const QPalette &palette);
QPalette(QPalette &&other) noexcept
- : d(qExchange(other.d, nullptr)), currentGroup(other.currentGroup)
+ : d(std::exchange(other.d, nullptr)), currentGroup(other.currentGroup)
{}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QPalette)
@@ -45,6 +45,7 @@ public:
operator QVariant() const;
// Do not change the order, the serialization format depends on it
+ // Ensure these values are kept in sync with QQuickColorGroup's properties.
enum ColorGroup { Active, Disabled, Inactive, NColorGroups, Current, All, Normal = Active };
Q_ENUM(ColorGroup)
enum ColorRole { WindowText, Button, Light, Midlight, Dark, Mid,
@@ -55,7 +56,8 @@ public:
NoRole,
ToolTipBase, ToolTipText,
PlaceholderText,
- NColorRoles = PlaceholderText + 1,
+ Accent,
+ NColorRoles = Accent + 1,
};
Q_ENUM(ColorRole)
@@ -98,6 +100,7 @@ public:
inline const QBrush &link() const { return brush(Link); }
inline const QBrush &linkVisited() const { return brush(LinkVisited); }
inline const QBrush &placeholderText() const { return brush(PlaceholderText); }
+ inline const QBrush &accent() const { return brush(Accent); }
bool operator==(const QPalette &p) const;
inline bool operator!=(const QPalette &p) const { return !(operator==(p)); }
diff --git a/src/gui/kernel/qpalette_p.h b/src/gui/kernel/qpalette_p.h
new file mode 100644
index 0000000000..ce7c30d66d
--- /dev/null
+++ b/src/gui/kernel/qpalette_p.h
@@ -0,0 +1,77 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QPALETTE_P_H
+#define QPALETTE_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 "qpalette.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QPalettePrivate
+{
+public:
+ class Data : public QSharedData {
+ public:
+ // Every instance of Data has to have a unique serial number, even
+ // if it gets created by copying another - we wouldn't create a copy
+ // in the first place if the serial number should be the same!
+ Data(const Data &other)
+ : QSharedData(other)
+ {
+ for (int grp = 0; grp < int(QPalette::NColorGroups); grp++) {
+ for (int role = 0; role < int(QPalette::NColorRoles); role++)
+ br[grp][role] = other.br[grp][role];
+ }
+ }
+ Data() = default;
+
+ QBrush br[QPalette::NColorGroups][QPalette::NColorRoles];
+ const int ser_no = qt_palette_count++;
+ };
+
+ QPalettePrivate(const QExplicitlySharedDataPointer<Data> &data)
+ : ref(1), data(data)
+ { }
+ QPalettePrivate()
+ : QPalettePrivate(QExplicitlySharedDataPointer<Data>(new Data))
+ { }
+
+ static constexpr QPalette::ResolveMask colorRoleOffset(QPalette::ColorGroup colorGroup)
+ {
+ // Exclude NoRole; that bit is used for Accent
+ return (qToUnderlying(QPalette::NColorRoles) - 1) * qToUnderlying(colorGroup);
+ }
+
+ static constexpr QPalette::ResolveMask bitPosition(QPalette::ColorGroup colorGroup,
+ QPalette::ColorRole colorRole)
+ {
+ // Map Accent into NoRole for resolving purposes
+ if (colorRole == QPalette::Accent)
+ colorRole = QPalette::NoRole;
+
+ return colorRole + colorRoleOffset(colorGroup);
+ }
+
+ QAtomicInt ref;
+ QPalette::ResolveMask resolveMask = {0};
+ static inline int qt_palette_count = 0;
+ static inline int qt_palette_private_count = 0;
+ int detach_no = ++qt_palette_private_count;
+ QExplicitlySharedDataPointer<Data> data;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPALETTE_P_H
diff --git a/src/gui/kernel/qplatformdialoghelper.cpp b/src/gui/kernel/qplatformdialoghelper.cpp
index 0224fb2c61..93de7933d4 100644
--- a/src/gui/kernel/qplatformdialoghelper.cpp
+++ b/src/gui/kernel/qplatformdialoghelper.cpp
@@ -15,6 +15,7 @@
#include <QtCore/QUrl>
#include <QtCore/QVariant>
#include <QtGui/QColor>
+#include <QtGui/QPixmap>
#include <algorithm>
@@ -767,13 +768,19 @@ public:
{}
QString windowTitle;
- QMessageDialogOptions::Icon icon;
+ QMessageDialogOptions::StandardIcon icon;
QString text;
QString informativeText;
QString detailedText;
QPlatformDialogHelper::StandardButtons buttons;
QList<QMessageDialogOptions::CustomButton> customButtons;
int nextCustomButtonId;
+ QPixmap iconPixmap;
+ QString checkBoxLabel;
+ Qt::CheckState checkBoxState = Qt::Unchecked;
+ int defaultButtonId = 0;
+ int escapeButtonId = 0;
+ QMessageDialogOptions::Options options;
};
QMessageDialogOptions::QMessageDialogOptions(QMessageDialogOptionsPrivate *dd)
@@ -815,16 +822,26 @@ void QMessageDialogOptions::setWindowTitle(const QString &title)
d->windowTitle = title;
}
-QMessageDialogOptions::Icon QMessageDialogOptions::icon() const
+QMessageDialogOptions::StandardIcon QMessageDialogOptions::standardIcon() const
{
return d->icon;
}
-void QMessageDialogOptions::setIcon(Icon icon)
+void QMessageDialogOptions::setStandardIcon(StandardIcon icon)
{
d->icon = icon;
}
+void QMessageDialogOptions::setIconPixmap(const QPixmap &pixmap)
+{
+ d->iconPixmap = pixmap;
+}
+
+QPixmap QMessageDialogOptions::iconPixmap() const
+{
+ return d->iconPixmap;
+}
+
QString QMessageDialogOptions::text() const
{
return d->text;
@@ -866,9 +883,9 @@ QPlatformDialogHelper::StandardButtons QMessageDialogOptions::standardButtons()
}
int QMessageDialogOptions::addButton(const QString &label, QPlatformDialogHelper::ButtonRole role,
- void *buttonImpl)
+ void *buttonImpl, int buttonId)
{
- const CustomButton b(d->nextCustomButtonId++, label, role, buttonImpl);
+ const CustomButton b(buttonId ? buttonId : d->nextCustomButtonId++, label, role, buttonImpl);
d->customButtons.append(b);
return b.id;
}
@@ -888,12 +905,76 @@ const QList<QMessageDialogOptions::CustomButton> &QMessageDialogOptions::customB
return d->customButtons;
}
+void QMessageDialogOptions::clearCustomButtons()
+{
+ d->customButtons.clear();
+}
+
const QMessageDialogOptions::CustomButton *QMessageDialogOptions::customButton(int id)
{
- int i = d->customButtons.indexOf(CustomButton(id));
+ const int i = int(d->customButtons.indexOf(CustomButton(id)));
return (i < 0 ? nullptr : &d->customButtons.at(i));
}
+void QMessageDialogOptions::setCheckBox(const QString &label, Qt::CheckState state)
+{
+ d->checkBoxLabel = label;
+ d->checkBoxState = state;
+}
+
+QString QMessageDialogOptions::checkBoxLabel() const
+{
+ return d->checkBoxLabel;
+}
+
+Qt::CheckState QMessageDialogOptions::checkBoxState() const
+{
+ return d->checkBoxState;
+}
+
+void QMessageDialogOptions::setDefaultButton(int id)
+{
+ d->defaultButtonId = id;
+}
+
+int QMessageDialogOptions::defaultButton() const
+{
+ return d->defaultButtonId;
+}
+
+void QMessageDialogOptions::setEscapeButton(int id)
+{
+ d->escapeButtonId = id;
+}
+
+int QMessageDialogOptions::escapeButton() const
+{
+ return d->escapeButtonId;
+}
+
+void QMessageDialogOptions::setOption(QMessageDialogOptions::Option option, bool on)
+{
+ if (!(d->options & option) != !on)
+ setOptions(d->options ^ option);
+}
+
+bool QMessageDialogOptions::testOption(QMessageDialogOptions::Option option) const
+{
+ return d->options & option;
+}
+
+void QMessageDialogOptions::setOptions(QMessageDialogOptions::Options options)
+{
+ if (options != d->options)
+ d->options = options;
+}
+
+QMessageDialogOptions::Options QMessageDialogOptions::options() const
+{
+ return d->options;
+}
+
+
QPlatformDialogHelper::ButtonRole QPlatformDialogHelper::buttonRole(QPlatformDialogHelper::StandardButton button)
{
switch (button) {
diff --git a/src/gui/kernel/qplatformdialoghelper.h b/src/gui/kernel/qplatformdialoghelper.h
index 83857ba82e..0569a7e2f9 100644
--- a/src/gui/kernel/qplatformdialoghelper.h
+++ b/src/gui/kernel/qplatformdialoghelper.h
@@ -157,7 +157,8 @@ public:
enum ColorDialogOption {
ShowAlphaChannel = 0x00000001,
NoButtons = 0x00000002,
- DontUseNativeDialog = 0x00000004
+ DontUseNativeDialog = 0x00000004,
+ NoEyeDropperButton = 0x00000008
};
Q_DECLARE_FLAGS(ColorDialogOptions, ColorDialogOption)
@@ -403,9 +404,16 @@ protected:
~QMessageDialogOptions();
public:
+ // Keep in sync with QMessageBox Option
+ enum class Option {
+ DontUseNativeDialog = 0x00000001,
+ };
+ Q_DECLARE_FLAGS(Options, Option);
+ Q_FLAG(Options);
+
// Keep in sync with QMessageBox::Icon
- enum Icon { NoIcon, Information, Warning, Critical, Question };
- Q_ENUM(Icon)
+ enum StandardIcon { NoIcon, Information, Warning, Critical, Question };
+ Q_ENUM(StandardIcon)
static QSharedPointer<QMessageDialogOptions> create();
QSharedPointer<QMessageDialogOptions> clone() const;
@@ -413,8 +421,11 @@ public:
QString windowTitle() const;
void setWindowTitle(const QString &);
- void setIcon(Icon icon);
- Icon icon() const;
+ void setStandardIcon(StandardIcon icon);
+ StandardIcon standardIcon() const;
+
+ void setIconPixmap(const QPixmap &pixmap);
+ QPixmap iconPixmap() const;
void setText(const QString &text);
QString text() const;
@@ -425,6 +436,11 @@ public:
void setDetailedText(const QString &text);
QString detailedText() const;
+ void setOption(Option option, bool on = true);
+ bool testOption(Option option) const;
+ void setOptions(Options options);
+ Options options() const;
+
void setStandardButtons(QPlatformDialogHelper::StandardButtons buttons);
QPlatformDialogHelper::StandardButtons standardButtons() const;
@@ -443,10 +459,21 @@ public:
};
int addButton(const QString &label, QPlatformDialogHelper::ButtonRole role,
- void *buttonImpl = nullptr);
+ void *buttonImpl = nullptr, int buttonId = 0);
void removeButton(int id);
const QList<CustomButton> &customButtons();
const CustomButton *customButton(int id);
+ void clearCustomButtons();
+
+ void setCheckBox(const QString &label, Qt::CheckState state);
+ QString checkBoxLabel() const;
+ Qt::CheckState checkBoxState() const;
+
+ void setEscapeButton(int id);
+ int escapeButton() const;
+
+ void setDefaultButton(int id);
+ int defaultButton() const;
private:
QMessageDialogOptionsPrivate *d;
@@ -461,6 +488,7 @@ public:
Q_SIGNALS:
void clicked(QPlatformDialogHelper::StandardButton button, QPlatformDialogHelper::ButtonRole role);
+ void checkBoxStateChanged(Qt::CheckState state);
private:
QSharedPointer<QMessageDialogOptions> m_options;
diff --git a/src/gui/kernel/qplatformgraphicsbufferhelper.cpp b/src/gui/kernel/qplatformgraphicsbufferhelper.cpp
index 4c2b9e5b39..0ec2308453 100644
--- a/src/gui/kernel/qplatformgraphicsbufferhelper.cpp
+++ b/src/gui/kernel/qplatformgraphicsbufferhelper.cpp
@@ -179,14 +179,14 @@ bool QPlatformGraphicsBufferHelper::bindSWToTexture(const QPlatformGraphicsBuffe
QRect rect = subRect;
if (rect.isNull() || rect == QRect(QPoint(0,0),size)) {
if (needsRowLength)
- funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, image.bytesPerLine() / 4);
+ funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, GLint(image.bytesPerLine() / 4));
funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, size.width(), size.height(), 0, GL_RGBA, pixelType, image.constBits());
if (needsRowLength)
funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
} else {
if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
// OpenGL 2.1+ or OpenGL ES/3+
- funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, image.bytesPerLine() / 4);
+ funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, GLint(image.bytesPerLine() / 4));
funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType,
image.constScanLine(rect.y()) + rect.x() * 4);
funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
diff --git a/src/gui/kernel/qplatforminputcontext.cpp b/src/gui/kernel/qplatforminputcontext.cpp
index b8a53685e5..9d3ee4acb6 100644
--- a/src/gui/kernel/qplatforminputcontext.cpp
+++ b/src/gui/kernel/qplatforminputcontext.cpp
@@ -47,6 +47,11 @@ QT_BEGIN_NAMESPACE
QPlatformInputContext::QPlatformInputContext()
: QObject(*(new QPlatformInputContextPrivate))
{
+ // Delay initialization of cached input direction
+ // until super class has finished constructing.
+ QMetaObject::invokeMethod(this, [this]{
+ m_inputDirection = inputDirection();
+ }, Qt::QueuedConnection);
}
/*!
@@ -95,7 +100,7 @@ void QPlatformInputContext::update(Qt::InputMethodQueries)
}
/*!
- Called when when the word currently being composed in input item is tapped by
+ Called when the word currently being composed in the input item is tapped by
the user. Input methods often use this information to offer more word
suggestions to the user.
*/
@@ -192,22 +197,29 @@ void QPlatformInputContext::emitInputPanelVisibleChanged()
QLocale QPlatformInputContext::locale() const
{
- return qt_keymapper_private()->keyboardInputLocale;
+ return QLocale::system();
}
void QPlatformInputContext::emitLocaleChanged()
{
emit QGuiApplication::inputMethod()->localeChanged();
+
+ // Changing the locale might have updated the input direction
+ emitInputDirectionChanged(inputDirection());
}
Qt::LayoutDirection QPlatformInputContext::inputDirection() const
{
- return qt_keymapper_private()->keyboardInputDirection;
+ return locale().textDirection();
}
void QPlatformInputContext::emitInputDirectionChanged(Qt::LayoutDirection newDirection)
{
+ if (newDirection == m_inputDirection)
+ return;
+
emit QGuiApplication::inputMethod()->inputDirectionChanged(newDirection);
+ m_inputDirection = newDirection;
}
/*!
diff --git a/src/gui/kernel/qplatforminputcontext.h b/src/gui/kernel/qplatforminputcontext.h
index 05d7497f9c..481f97a065 100644
--- a/src/gui/kernel/qplatforminputcontext.h
+++ b/src/gui/kernel/qplatforminputcontext.h
@@ -72,6 +72,8 @@ private:
friend class QGuiApplication;
friend class QGuiApplicationPrivate;
friend class QInputMethod;
+
+ Qt::LayoutDirection m_inputDirection;
};
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qplatforminputcontextfactory.cpp b/src/gui/kernel/qplatforminputcontextfactory.cpp
index bc1e0cc78f..933d990f7c 100644
--- a/src/gui/kernel/qplatforminputcontextfactory.cpp
+++ b/src/gui/kernel/qplatforminputcontextfactory.cpp
@@ -15,23 +15,46 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
#if QT_CONFIG(settings)
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, icLoader,
(QPlatformInputContextFactoryInterface_iid, "/platforminputcontexts"_L1, Qt::CaseInsensitive))
#endif
QStringList QPlatformInputContextFactory::keys()
{
#if QT_CONFIG(settings)
- return loader()->keyMap().values();
+ return icLoader()->keyMap().values();
#else
return QStringList();
#endif
}
-QString QPlatformInputContextFactory::requested()
+QStringList QPlatformInputContextFactory::requested()
{
- QByteArray env = qgetenv("QT_IM_MODULE");
- return env.isNull() ? QString() : QString::fromLocal8Bit(env);
+ QStringList imList;
+ QByteArray env = qgetenv("QT_IM_MODULES");
+
+ if (!env.isEmpty())
+ imList = QString::fromLocal8Bit(env).split(QChar::fromLatin1(';'), Qt::SkipEmptyParts);
+
+ if (!imList.isEmpty())
+ return imList;
+
+ env = qgetenv("QT_IM_MODULE");
+ if (!env.isEmpty())
+ imList = {QString::fromLocal8Bit(env)};
+
+ return imList;
+}
+
+QPlatformInputContext *QPlatformInputContextFactory::create(const QStringList& keys)
+{
+ for (const QString &key : keys) {
+ auto plugin = create(key);
+ if (plugin)
+ return plugin;
+ }
+
+ return nullptr;
}
QPlatformInputContext *QPlatformInputContextFactory::create(const QString& key)
@@ -42,7 +65,7 @@ QPlatformInputContext *QPlatformInputContextFactory::create(const QString& key)
const QString platform = paramList.takeFirst().toLower();
QPlatformInputContext *ic = qLoadPlugin<QPlatformInputContext, QPlatformInputContextPlugin>
- (loader(), platform, paramList);
+ (icLoader(), platform, paramList);
if (ic && ic->isValid())
return ic;
diff --git a/src/gui/kernel/qplatforminputcontextfactory_p.h b/src/gui/kernel/qplatforminputcontextfactory_p.h
index 4968a51a8b..5f5881c508 100644
--- a/src/gui/kernel/qplatforminputcontextfactory_p.h
+++ b/src/gui/kernel/qplatforminputcontextfactory_p.h
@@ -27,7 +27,8 @@ class Q_GUI_EXPORT QPlatformInputContextFactory
{
public:
static QStringList keys();
- static QString requested();
+ static QStringList requested();
+ static QPlatformInputContext *create(const QStringList &keys);
static QPlatformInputContext *create(const QString &key);
static QPlatformInputContext *create();
};
diff --git a/src/gui/kernel/qplatformintegration.cpp b/src/gui/kernel/qplatformintegration.cpp
index 4d4f501ae4..130b479c64 100644
--- a/src/gui/kernel/qplatformintegration.cpp
+++ b/src/gui/kernel/qplatformintegration.cpp
@@ -6,6 +6,7 @@
#include <qpa/qplatformfontdatabase.h>
#include <qpa/qplatformclipboard.h>
#include <qpa/qplatformaccessibility.h>
+#include <qpa/qplatformkeymapper.h>
#include <qpa/qplatformtheme.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qpixmap_raster_p.h>
@@ -228,6 +229,11 @@ QPlatformServices *QPlatformIntegration::services() const
\value ScreenWindowGrabbing The platform supports grabbing window on screen.
On Wayland, this capability can be reported as \c false. The default implementation
of hasCapability() returns \c true.
+
+ \value BackingStoreStaticContents The platform backingstore supports static contents.
+ On resize of the backingstore the static contents region is provided, and the backing
+ store is expected to propagate the static content to the resized backing store, without
+ clients needing to repaint the static content region.
*/
/*!
@@ -342,6 +348,21 @@ QPlatformInputContext *QPlatformIntegration::inputContext() const
return nullptr;
}
+/*!
+ Accessor for the platform integration's key mapper.
+
+ Default implementation returns a default QPlatformKeyMapper.
+
+ \sa QPlatformKeyMapper
+*/
+QPlatformKeyMapper *QPlatformIntegration::keyMapper() const
+{
+ static QPlatformKeyMapper *keyMapper = nullptr;
+ if (!keyMapper)
+ keyMapper = new QPlatformKeyMapper;
+ return keyMapper;
+}
+
#if QT_CONFIG(accessibility)
/*!
@@ -393,7 +414,7 @@ QVariant QPlatformIntegration::styleHint(StyleHint hint) const
case UseRtlExtensions:
return QVariant(false);
case SetFocusOnTouchRelease:
- return QVariant(false);
+ return QPlatformTheme::defaultThemeHint(QPlatformTheme::SetFocusOnTouchRelease);
case MousePressAndHoldInterval:
return QPlatformTheme::defaultThemeHint(QPlatformTheme::MousePressAndHoldInterval);
case TabFocusBehavior:
@@ -410,6 +431,14 @@ QVariant QPlatformIntegration::styleHint(StyleHint hint) const
return QPlatformTheme::defaultThemeHint(QPlatformTheme::MouseQuickSelectionThreshold);
case MouseDoubleClickDistance:
return QPlatformTheme::defaultThemeHint(QPlatformTheme::MouseDoubleClickDistance);
+ case FlickStartDistance:
+ return QPlatformTheme::defaultThemeHint(QPlatformTheme::FlickStartDistance);
+ case FlickMaximumVelocity:
+ return QPlatformTheme::defaultThemeHint(QPlatformTheme::FlickMaximumVelocity);
+ case FlickDeceleration:
+ return QPlatformTheme::defaultThemeHint(QPlatformTheme::FlickDeceleration);
+ case UnderlineShortcut:
+ return true;
}
return 0;
@@ -563,7 +592,21 @@ void QPlatformIntegration::setApplicationIcon(const QIcon &icon) const
Q_UNUSED(icon);
}
-#if QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)
+/*!
+ \since 6.5
+
+ Should set the application's badge to \a number.
+
+ If the number is 0 the badge should be cleared.
+
+ \sa QGuiApplication::setBadge()
+*/
+void QPlatformIntegration::setApplicationBadge(qint64 number)
+{
+ Q_UNUSED(number);
+}
+
+#if QT_CONFIG(vulkan) || defined(Q_QDOC)
/*!
Factory function for QPlatformVulkanInstance. The \a instance parameter is a
diff --git a/src/gui/kernel/qplatformintegration.h b/src/gui/kernel/qplatformintegration.h
index cc617faca9..a18ae821c7 100644
--- a/src/gui/kernel/qplatformintegration.h
+++ b/src/gui/kernel/qplatformintegration.h
@@ -33,6 +33,7 @@ class QPlatformOpenGLContext;
class QGuiGLFormat;
class QAbstractEventDispatcher;
class QPlatformInputContext;
+class QPlatformKeyMapper;
class QPlatformAccessibility;
class QPlatformTheme;
class QPlatformDialogHelper;
@@ -98,7 +99,8 @@ public:
MaximizeUsingFullscreenGeometry,
PaintEvents,
RhiBasedRendering,
- ScreenWindowGrabbing // whether QScreen::grabWindow() is supported
+ ScreenWindowGrabbing, // whether QScreen::grabWindow() is supported
+ BackingStoreStaticContents
};
virtual ~QPlatformIntegration() { }
@@ -161,14 +163,22 @@ public:
WheelScrollLines,
ShowShortcutsInContextMenus,
MouseQuickSelectionThreshold,
- MouseDoubleClickDistance
+ MouseDoubleClickDistance,
+ FlickStartDistance,
+ FlickMaximumVelocity,
+ FlickDeceleration,
+ UnderlineShortcut,
};
virtual QVariant styleHint(StyleHint hint) const;
virtual Qt::WindowState defaultWindowState(Qt::WindowFlags) const;
+protected:
virtual Qt::KeyboardModifiers queryKeyboardModifiers() const;
virtual QList<int> possibleKeys(const QKeyEvent *) const;
+ friend class QPlatformKeyMapper;
+public:
+ virtual QPlatformKeyMapper *keyMapper() const;
virtual QStringList themeNames() const;
virtual QPlatformTheme *createPlatformTheme(const QString &name) const;
@@ -185,11 +195,12 @@ public:
virtual QOpenGLContext::OpenGLModuleType openGLModuleType();
#endif
virtual void setApplicationIcon(const QIcon &icon) const;
+ virtual void setApplicationBadge(qint64 number);
virtual void beep() const;
virtual void quit() const;
-#if QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(vulkan) || defined(Q_QDOC)
virtual QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const;
#endif
diff --git a/src/gui/kernel/qplatformintegrationfactory.cpp b/src/gui/kernel/qplatformintegrationfactory.cpp
index c1523260c1..d0a5e8871f 100644
--- a/src/gui/kernel/qplatformintegrationfactory.cpp
+++ b/src/gui/kernel/qplatformintegrationfactory.cpp
@@ -14,13 +14,13 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, piLoader,
(QPlatformIntegrationFactoryInterface_iid, "/platforms"_L1, Qt::CaseInsensitive))
QPlatformIntegration *QPlatformIntegrationFactory::create(const QString &platform, const QStringList &paramList, int &argc, char **argv, const QString &platformPluginPath)
{
- loader->setExtraSearchPath(platformPluginPath);
- return qLoadPlugin<QPlatformIntegration, QPlatformIntegrationPlugin>(loader(), platform, paramList, argc, argv);
+ piLoader->setExtraSearchPath(platformPluginPath);
+ return qLoadPlugin<QPlatformIntegration, QPlatformIntegrationPlugin>(piLoader(), platform, paramList, argc, argv);
}
/*!
@@ -32,8 +32,8 @@ QPlatformIntegration *QPlatformIntegrationFactory::create(const QString &platfor
QStringList QPlatformIntegrationFactory::keys(const QString &platformPluginPath)
{
- loader->setExtraSearchPath(platformPluginPath);
- return loader->keyMap().values();
+ piLoader->setExtraSearchPath(platformPluginPath);
+ return piLoader->keyMap().values();
}
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qplatformkeymapper.cpp b/src/gui/kernel/qplatformkeymapper.cpp
new file mode 100644
index 0000000000..f54e4ff379
--- /dev/null
+++ b/src/gui/kernel/qplatformkeymapper.cpp
@@ -0,0 +1,38 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qplatformkeymapper.h"
+
+#include <private/qguiapplication_p.h>
+#include <qpa/qplatformintegration.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcQpaKeyMapper, "qt.qpa.keymapper")
+
+QPlatformKeyMapper::~QPlatformKeyMapper()
+{
+}
+
+/*
+ Should return a list of possible key combinations for the given key event.
+
+ For example, given a US English keyboard layout, the key event Shift+5
+ can represent both a "Shift+5" key combination, as well as just "%".
+*/
+QList<QKeyCombination> QPlatformKeyMapper::possibleKeyCombinations(const QKeyEvent *event) const
+{
+ auto *platformIntegration = QGuiApplicationPrivate::platformIntegration();
+ QList<int> possibleKeys = platformIntegration->possibleKeys(event);
+ QList<QKeyCombination> combinations;
+ for (int key : possibleKeys)
+ combinations << QKeyCombination::fromCombined(key);
+ return combinations;
+}
+
+Qt::KeyboardModifiers QPlatformKeyMapper::queryKeyboardModifiers() const
+{
+ return QGuiApplicationPrivate::platformIntegration()->queryKeyboardModifiers();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/kernel/qplatformkeymapper.h b/src/gui/kernel/qplatformkeymapper.h
new file mode 100644
index 0000000000..fb5b0cdb8b
--- /dev/null
+++ b/src/gui/kernel/qplatformkeymapper.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QPLATFORMKEYMAPPER_P
+#define QPLATFORMKEYMAPPER_P
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is part of the QPA API and is not meant to be used
+// in applications. Usage of this API may make your code
+// source and binary incompatible with future versions of Qt.
+//
+
+#include <QtGui/qtguiglobal.h>
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_EXPORTED_LOGGING_CATEGORY(lcQpaKeyMapper, Q_GUI_EXPORT)
+
+class QKeyEvent;
+
+class Q_GUI_EXPORT QPlatformKeyMapper
+{
+public:
+ virtual ~QPlatformKeyMapper();
+
+ virtual QList<QKeyCombination> possibleKeyCombinations(const QKeyEvent *event) const;
+ virtual Qt::KeyboardModifiers queryKeyboardModifiers() const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPLATFORMKEYMAPPER_P
diff --git a/src/gui/kernel/qplatformmenu_p.h b/src/gui/kernel/qplatformmenu_p.h
index 22eba7af41..00e3fa56da 100644
--- a/src/gui/kernel/qplatformmenu_p.h
+++ b/src/gui/kernel/qplatformmenu_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
// ----------------- QNativeInterface -----------------
-#if !defined(Q_OS_MACOS) && defined(Q_CLANG_QDOC)
+#if !defined(Q_OS_MACOS) && defined(Q_QDOC)
typedef void NSMenu;
#else
QT_END_NAMESPACE
@@ -33,7 +33,7 @@ QT_BEGIN_NAMESPACE
namespace QNativeInterface::Private {
-#if defined(Q_OS_MACOS) || defined(Q_CLANG_QDOC)
+#if defined(Q_OS_MACOS) || defined(Q_QDOC)
struct Q_GUI_EXPORT QCocoaMenu
{
QT_DECLARE_NATIVE_INTERFACE(QCocoaMenu)
diff --git a/src/gui/kernel/qplatformscreen.cpp b/src/gui/kernel/qplatformscreen.cpp
index fd9b63c450..0369a0b12a 100644
--- a/src/gui/kernel/qplatformscreen.cpp
+++ b/src/gui/kernel/qplatformscreen.cpp
@@ -25,10 +25,8 @@ QPlatformScreen::QPlatformScreen()
QPlatformScreen::~QPlatformScreen()
{
Q_D(QPlatformScreen);
- if (d->screen) {
- qWarning("Manually deleting a QPlatformScreen. Call QWindowSystemInterface::handleScreenRemoved instead.");
- delete d->screen;
- }
+ Q_ASSERT_X(!d->screen, "QPlatformScreen",
+ "QPlatformScreens should be removed via QWindowSystemInterface::handleScreenRemoved()");
}
/*!
@@ -56,8 +54,9 @@ QPixmap QPlatformScreen::grabWindow(WId window, int x, int y, int width, int hei
QWindow *QPlatformScreen::topLevelAt(const QPoint & pos) const
{
const QWindowList list = QGuiApplication::topLevelWindows();
- for (int i = list.size()-1; i >= 0; --i) {
- QWindow *w = list[i];
+ const auto crend = list.crend();
+ for (auto it = list.crbegin(); it != crend; ++it) {
+ QWindow *w = *it;
if (w->isVisible() && QHighDpi::toNativePixels(w->geometry(), w).contains(pos))
return w;
}
@@ -435,13 +434,6 @@ QRect QPlatformScreen::mapBetween(Qt::ScreenOrientation a, Qt::ScreenOrientation
return rect;
}
-QRect QPlatformScreen::deviceIndependentGeometry() const
-{
- qreal scaleFactor = QHighDpiScaling::factor(this);
- QRect nativeGeometry = geometry();
- return QRect(nativeGeometry.topLeft(), QHighDpi::fromNative(nativeGeometry.size(), scaleFactor));
-}
-
/*!
Returns a hint about this screen's subpixel layout structure.
diff --git a/src/gui/kernel/qplatformscreen.h b/src/gui/kernel/qplatformscreen.h
index ca828171ff..a547a635e9 100644
--- a/src/gui/kernel/qplatformscreen.h
+++ b/src/gui/kernel/qplatformscreen.h
@@ -42,6 +42,7 @@ typedef QPair<qreal, qreal> QDpi;
class Q_GUI_EXPORT QPlatformScreen
{
+ Q_GADGET
Q_DECLARE_PRIVATE(QPlatformScreen)
public:
@@ -124,9 +125,6 @@ public:
static QTransform transformBetween(Qt::ScreenOrientation a, Qt::ScreenOrientation b, const QRect &target);
static QRect mapBetween(Qt::ScreenOrientation a, Qt::ScreenOrientation b, const QRect &rect);
- // The platform screen's geometry in device independent coordinates
- QRect deviceIndependentGeometry() const;
-
static QDpi overrideDpi(const QDpi &in);
protected:
@@ -135,7 +133,7 @@ protected:
QScopedPointer<QPlatformScreenPrivate> d_ptr;
private:
- friend class QScreenPrivate;
+ friend class QScreen;
};
// Qt doesn't currently support running with no platform screen
diff --git a/src/gui/kernel/qplatformscreen_p.h b/src/gui/kernel/qplatformscreen_p.h
index 2d3fb2d418..345a90845c 100644
--- a/src/gui/kernel/qplatformscreen_p.h
+++ b/src/gui/kernel/qplatformscreen_p.h
@@ -34,7 +34,7 @@ public:
namespace QNativeInterface::Private {
-#if QT_CONFIG(xcb) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(xcb) || defined(Q_QDOC)
struct Q_GUI_EXPORT QXcbScreen
{
QT_DECLARE_NATIVE_INTERFACE(QXcbScreen, 1, QScreen)
@@ -42,7 +42,7 @@ struct Q_GUI_EXPORT QXcbScreen
};
#endif
-#if QT_CONFIG(vsp2) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(vsp2) || defined(Q_QDOC)
struct Q_GUI_EXPORT QVsp2Screen
{
QT_DECLARE_NATIVE_INTERFACE(QVsp2Screen, 1, QScreen)
@@ -55,7 +55,7 @@ struct Q_GUI_EXPORT QVsp2Screen
};
#endif
-#if defined(Q_OS_WEBOS) || defined(Q_CLANG_QDOC)
+#if defined(Q_OS_WEBOS) || defined(Q_QDOC)
struct Q_GUI_EXPORT QWebOSScreen
{
QT_DECLARE_NATIVE_INTERFACE(QWebOSScreen, 1, QScreen)
@@ -67,7 +67,6 @@ struct Q_GUI_EXPORT QWebOSScreen
virtual void addFlipListener(void (*callback)()) = 0;
};
#endif
-
} // QNativeInterface::Private
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qplatformservices.cpp b/src/gui/kernel/qplatformservices.cpp
index a0db26dda3..7ff2a6c2f8 100644
--- a/src/gui/kernel/qplatformservices.cpp
+++ b/src/gui/kernel/qplatformservices.cpp
@@ -19,6 +19,19 @@ QT_BEGIN_NAMESPACE
\brief The QPlatformServices provides the backend for desktop-related functionality.
*/
+/*!
+ \enum QPlatformServices::Capability
+
+ Capabilities are used to determine a specific platform service's availability.
+
+ \value ColorPickingFromScreen The platform natively supports color picking from screen.
+ This capability indicates that the platform supports "opaque" color picking, where the
+ platform implements a complete user experience for color picking and outputs a color.
+ This is in contrast to the application implementing the color picking user experience
+ (taking care of showing a cross hair, instructing the platform integration to obtain
+ the color at a given pixel, etc.). The related service function is pickColor().
+ */
+
QPlatformServices::QPlatformServices()
{ }
@@ -49,5 +62,18 @@ QByteArray QPlatformServices::desktopEnvironment() const
return QByteArray("UNKNOWN");
}
+QPlatformServiceColorPicker *QPlatformServices::colorPicker(QWindow *parent)
+{
+ Q_UNUSED(parent);
+ return nullptr;
+}
+
+bool QPlatformServices::hasCapability(Capability capability) const
+{
+ Q_UNUSED(capability)
+ return false;
+}
QT_END_NAMESPACE
+
+#include "moc_qplatformservices.cpp"
diff --git a/src/gui/kernel/qplatformservices.h b/src/gui/kernel/qplatformservices.h
index 50db36689e..063247af08 100644
--- a/src/gui/kernel/qplatformservices.h
+++ b/src/gui/kernel/qplatformservices.h
@@ -14,16 +14,32 @@
//
#include <QtGui/qtguiglobal.h>
+#include <QtCore/qobject.h>
QT_BEGIN_NAMESPACE
class QUrl;
+class QWindow;
+
+class Q_GUI_EXPORT QPlatformServiceColorPicker : public QObject
+{
+ Q_OBJECT
+public:
+ using QObject::QObject;
+ virtual void pickColor() = 0;
+Q_SIGNALS:
+ void colorPicked(const QColor &color);
+};
class Q_GUI_EXPORT QPlatformServices
{
public:
Q_DISABLE_COPY_MOVE(QPlatformServices)
+ enum Capability {
+ ColorPicking,
+ };
+
QPlatformServices();
virtual ~QPlatformServices() { }
@@ -31,6 +47,10 @@ public:
virtual bool openDocument(const QUrl &url);
virtual QByteArray desktopEnvironment() const;
+
+ virtual bool hasCapability(Capability capability) const;
+
+ virtual QPlatformServiceColorPicker *colorPicker(QWindow *parent = nullptr);
};
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qplatformsystemtrayicon.h b/src/gui/kernel/qplatformsystemtrayicon.h
index c2c80f9334..76a7ef03d9 100644
--- a/src/gui/kernel/qplatformsystemtrayicon.h
+++ b/src/gui/kernel/qplatformsystemtrayicon.h
@@ -6,6 +6,7 @@
#define QPLATFORMSYSTEMTRAYICON_H
#include <QtGui/qtguiglobal.h>
+#include <qpa/qplatformscreen.h>
#include "QtCore/qobject.h"
#ifndef QT_NO_SYSTEMTRAYICON
@@ -13,7 +14,6 @@
QT_BEGIN_NAMESPACE
class QPlatformMenu;
-class QPlatformScreen;
class QIcon;
class QString;
class QRect;
@@ -21,7 +21,6 @@ class QRect;
class Q_GUI_EXPORT QPlatformSystemTrayIcon : public QObject
{
Q_OBJECT
- Q_MOC_INCLUDE(<qpa/qplatformscreen.h>)
public:
enum ActivationReason {
Unknown,
diff --git a/src/gui/kernel/qplatformtheme.cpp b/src/gui/kernel/qplatformtheme.cpp
index 65f600c338..48978b849a 100644
--- a/src/gui/kernel/qplatformtheme.cpp
+++ b/src/gui/kernel/qplatformtheme.cpp
@@ -143,6 +143,16 @@ QT_BEGIN_NAMESPACE
\value ButtonPressKeys (QList<Qt::Key>) A list of keys that can be used to press buttons via keyboard input.
+ \value SetFocusOnTouchRelease (bool) Whether focus objects (line edits etc) should receive
+ input focus after a touch/mouse release.
+ This enum value has been added in Qt 6.5.
+
+ \value MouseCursorTheme (QString) Name of the mouse cursor theme.
+ This enum value has been added in Qt 6.5.
+
+ \value MouseCursorSize (QSize) Size of the mouse cursor.
+ This enum value has been added in Qt 6.5.
+
\sa themeHint(), QStyle::pixelMetric()
*/
@@ -315,7 +325,7 @@ const QKeyBinding QPlatformThemePrivate::keyBindings[] = {
{QKeySequence::InsertLineSeparator, 0, Qt::SHIFT | Qt::Key_Enter, KB_All},
{QKeySequence::InsertLineSeparator, 0, Qt::SHIFT | Qt::Key_Return, KB_All},
{QKeySequence::InsertLineSeparator, 0, Qt::META | Qt::Key_O, KB_Mac},
- {QKeySequence::SaveAs, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_S, KB_Gnome | KB_Mac},
+ {QKeySequence::SaveAs, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_S, KB_All},
{QKeySequence::Preferences, 0, Qt::CTRL | Qt::Key_Comma, KB_Mac},
{QKeySequence::Quit, 0, Qt::CTRL | Qt::Key_Q, KB_X11 | KB_Gnome | KB_KDE | KB_Mac},
{QKeySequence::FullScreen, 1, Qt::META | Qt::CTRL | Qt::Key_F, KB_Mac},
@@ -325,6 +335,7 @@ const QKeyBinding QPlatformThemePrivate::keyBindings[] = {
{QKeySequence::FullScreen, 1, Qt::Key_F11, KB_Win | KB_KDE},
{QKeySequence::Deselect, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_A, KB_X11},
{QKeySequence::DeleteCompleteLine, 0, Qt::CTRL | Qt::Key_U, KB_X11},
+ {QKeySequence::Backspace, 1, Qt::Key_Backspace, KB_Mac},
{QKeySequence::Backspace, 0, Qt::META | Qt::Key_H, KB_Mac},
{QKeySequence::Cancel, 0, Qt::Key_Escape, KB_All},
{QKeySequence::Cancel, 0, Qt::CTRL | Qt::Key_Period, KB_Mac}
@@ -342,7 +353,61 @@ QPlatformThemePrivate::~QPlatformThemePrivate()
delete systemPalette;
}
-Q_GUI_EXPORT QPalette qt_fusionPalette();
+Q_GUI_EXPORT QPalette qt_fusionPalette()
+{
+ auto theme = QGuiApplicationPrivate::platformTheme();
+ const bool darkAppearance = theme
+ ? theme->colorScheme() == Qt::ColorScheme::Dark
+ : false;
+ const QColor windowText = darkAppearance ? QColor(240, 240, 240) : Qt::black;
+ const QColor backGround = darkAppearance ? QColor(50, 50, 50) : QColor(239, 239, 239);
+ const QColor light = backGround.lighter(150);
+ const QColor mid = (backGround.darker(130));
+ const QColor midLight = mid.lighter(110);
+ const QColor base = darkAppearance ? backGround.darker(140) : Qt::white;
+ const QColor disabledBase(backGround);
+ const QColor dark = backGround.darker(150);
+ const QColor darkDisabled = QColor(209, 209, 209).darker(110);
+ const QColor text = darkAppearance ? windowText : Qt::black;
+ const QColor highlight = QColor(48, 140, 198);
+ const QColor hightlightedText = darkAppearance ? windowText : Qt::white;
+ const QColor disabledText = darkAppearance ? QColor(130, 130, 130) : QColor(190, 190, 190);
+ const QColor button = backGround;
+ const QColor shadow = dark.darker(135);
+ const QColor disabledShadow = shadow.lighter(150);
+ const QColor disabledHighlight(145, 145, 145);
+ QColor placeholder = text;
+ placeholder.setAlpha(128);
+
+ QPalette fusionPalette(windowText, backGround, light, dark, mid, text, base);
+ fusionPalette.setBrush(QPalette::Midlight, midLight);
+ fusionPalette.setBrush(QPalette::Button, button);
+ fusionPalette.setBrush(QPalette::Shadow, shadow);
+ fusionPalette.setBrush(QPalette::HighlightedText, hightlightedText);
+
+ fusionPalette.setBrush(QPalette::Disabled, QPalette::Text, disabledText);
+ fusionPalette.setBrush(QPalette::Disabled, QPalette::WindowText, disabledText);
+ fusionPalette.setBrush(QPalette::Disabled, QPalette::ButtonText, disabledText);
+ fusionPalette.setBrush(QPalette::Disabled, QPalette::Base, disabledBase);
+ fusionPalette.setBrush(QPalette::Disabled, QPalette::Dark, darkDisabled);
+ fusionPalette.setBrush(QPalette::Disabled, QPalette::Shadow, disabledShadow);
+
+ fusionPalette.setBrush(QPalette::Active, QPalette::Highlight, highlight);
+ fusionPalette.setBrush(QPalette::Inactive, QPalette::Highlight, highlight);
+ fusionPalette.setBrush(QPalette::Disabled, QPalette::Highlight, disabledHighlight);
+
+ fusionPalette.setBrush(QPalette::Active, QPalette::Accent, highlight);
+ fusionPalette.setBrush(QPalette::Inactive, QPalette::Accent, highlight);
+ fusionPalette.setBrush(QPalette::Disabled, QPalette::Accent, disabledHighlight);
+
+ fusionPalette.setBrush(QPalette::PlaceholderText, placeholder);
+
+ // Use a more legible light blue on dark backgrounds than the default Qt::blue.
+ if (darkAppearance)
+ fusionPalette.setBrush(QPalette::Link, highlight);
+
+ return fusionPalette;
+}
void QPlatformThemePrivate::initializeSystemPalette()
{
@@ -377,9 +442,9 @@ QPlatformDialogHelper *QPlatformTheme::createPlatformDialogHelper(DialogType typ
return nullptr;
}
-QPlatformTheme::Appearance QPlatformTheme::appearance() const
+Qt::ColorScheme QPlatformTheme::colorScheme() const
{
- return Appearance::Unknown;
+ return Qt::ColorScheme::Unknown;
}
const QPalette *QPlatformTheme::palette(Palette type) const
@@ -458,6 +523,16 @@ QVariant QPlatformTheme::themeHint(ThemeHint hint) const
return QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::UiEffects);
case QPlatformTheme::ShowShortcutsInContextMenus:
return QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::ShowShortcutsInContextMenus);
+ case QPlatformTheme::SetFocusOnTouchRelease:
+ return QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::SetFocusOnTouchRelease);
+ case QPlatformTheme::FlickStartDistance:
+ return QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FlickStartDistance);
+ case QPlatformTheme::FlickMaximumVelocity:
+ return QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FlickMaximumVelocity);
+ case QPlatformTheme::FlickDeceleration:
+ return QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FlickDeceleration);
+ case QPlatformTheme::UnderlineShortcut:
+ return QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::UnderlineShortcut);
default:
return QPlatformTheme::defaultThemeHint(hint);
}
@@ -556,7 +631,24 @@ QVariant QPlatformTheme::defaultThemeHint(ThemeHint hint)
return false;
case ButtonPressKeys:
return QVariant::fromValue(QList<Qt::Key>({ Qt::Key_Space, Qt::Key_Select }));
+ case SetFocusOnTouchRelease:
+ return false;
+ case FlickStartDistance:
+ return QVariant(15);
+ case FlickMaximumVelocity:
+ return QVariant(2500);
+ case FlickDeceleration:
+ return QVariant(1500);
+ case MenuBarFocusOnAltPressRelease:
+ return false;
+ case MouseCursorTheme:
+ return QVariant(QString());
+ case MouseCursorSize:
+ return QVariant(QSize(16, 16));
+ case UnderlineShortcut:
+ return true;
}
+
return QVariant();
}
@@ -724,33 +816,38 @@ QString QPlatformTheme::defaultStandardButtonText(int button)
QString QPlatformTheme::removeMnemonics(const QString &original)
{
+ const auto mnemonicInParentheses = [](QStringView text) {
+ /* Format of mnemonics to remove is /\(&[^&]\)/ but accept full-width
+ forms of ( and ) as equivalent, for cross-platform compatibility with
+ MS (and consequent behavior of translators, see QTBUG-110829).
+ */
+ Q_ASSERT(text.size() == 4); // Caller's responsibility.
+ constexpr QChar wideOpen = u'\uff08', wideClose = u'\uff09';
+ if (!text.startsWith(u'(') && !text.startsWith(wideOpen))
+ return false;
+ if (text[1] != u'&' || text[2] == u'&')
+ return false;
+ return text.endsWith(u')') || text.endsWith(wideClose);
+ };
QString returnText(original.size(), u'\0');
int finalDest = 0;
- int currPos = 0;
- int l = original.length();
- while (l) {
- if (original.at(currPos) == u'&') {
- ++currPos;
- --l;
- if (l == 0)
+ QStringView text(original);
+ while (!text.isEmpty()) {
+ if (text.startsWith(u'&')) {
+ text = text.sliced(1);
+ if (text.isEmpty())
break;
- } else if (original.at(currPos) == u'(' && l >= 4 &&
- original.at(currPos + 1) == u'&' &&
- original.at(currPos + 2) != u'&' &&
- original.at(currPos + 3) == u')') {
- /* remove mnemonics its format is "\s*(&X)" */
- int n = 0;
- while (finalDest > n && returnText.at(finalDest - n - 1).isSpace())
- ++n;
- finalDest -= n;
- currPos += 4;
- l -= 4;
+ } else if (text.size() >= 4 && mnemonicInParentheses(text.first(4))) {
+ // Advance over the matched mnemonic:
+ text = text.sliced(4);
+ // Also strip any leading space before it:
+ while (finalDest > 0 && returnText.at(finalDest - 1).isSpace())
+ --finalDest;
continue;
}
- returnText[finalDest] = original.at(currPos);
- ++currPos;
+ returnText[finalDest] = text.front();
+ text = text.sliced(1);
++finalDest;
- --l;
}
returnText.truncate(finalDest);
return returnText;
@@ -769,6 +866,11 @@ unsigned QPlatformThemePrivate::currentKeyPlatforms()
return result;
}
+QString QPlatformTheme::name() const
+{
+ return d_func()->name;
+}
+
QT_END_NAMESPACE
#include "moc_qplatformtheme.cpp"
diff --git a/src/gui/kernel/qplatformtheme.h b/src/gui/kernel/qplatformtheme.h
index 50594bfdf0..c0193947b9 100644
--- a/src/gui/kernel/qplatformtheme.h
+++ b/src/gui/kernel/qplatformtheme.h
@@ -14,6 +14,7 @@
//
#include <QtGui/qtguiglobal.h>
+#include <QtCore/QObject>
#include <QtCore/QScopedPointer>
#if QT_CONFIG(shortcut)
# include <QtGui/QKeySequence>
@@ -87,7 +88,15 @@ public:
InteractiveResizeAcrossScreens,
ShowDirectoriesFirst,
PreselectFirstFileInDirectory,
- ButtonPressKeys
+ ButtonPressKeys,
+ SetFocusOnTouchRelease,
+ FlickStartDistance,
+ FlickMaximumVelocity,
+ FlickDeceleration,
+ MenuBarFocusOnAltPressRelease,
+ MouseCursorTheme,
+ MouseCursorSize,
+ UnderlineShortcut,
};
Q_ENUM(ThemeHint)
@@ -97,12 +106,7 @@ public:
FontDialog,
MessageDialog
};
-
- enum class Appearance {
- Unknown = 0x0000,
- Light = 0x0001,
- Dark = 0x0002
- };
+ Q_ENUM(DialogType);
enum Palette {
SystemPalette,
@@ -125,6 +129,7 @@ public:
TextLineEditPalette,
NPalettes
};
+ Q_ENUM(Palette)
enum Font {
SystemFont,
@@ -230,9 +235,20 @@ public:
MediaVolume,
MediaVolumeMuted,
LineEditClearButton,
+ DialogYesToAllButton,
+ DialogNoToAllButton,
+ DialogSaveAllButton,
+ DialogAbortButton,
+ DialogRetryButton,
+ DialogIgnoreButton,
+ RestoreDefaultsButton,
+ TabCloseButton,
+ NStandardPixmap, // assertion value for sync with QStyle::StandardPixmap
+
// do not add any values below/greater than this
CustomBase = 0xf0000000
};
+ Q_ENUM(StandardPixmap)
enum KeyboardSchemes
{
@@ -243,6 +259,7 @@ public:
GnomeKeyboardScheme,
CdeKeyboardScheme
};
+ Q_ENUM(KeyboardSchemes)
enum UiEffect
{
@@ -255,6 +272,7 @@ public:
AnimateToolBoxUiEffect = 0x40,
HoverEffect = 0x80
};
+ Q_ENUM(UiEffect)
enum IconOption {
DontUseCustomDirectoryIcons = 0x01
@@ -276,7 +294,7 @@ public:
virtual QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const;
#endif
- virtual Appearance appearance() const;
+ virtual Qt::ColorScheme colorScheme() const;
virtual const QPalette *palette(Palette type = SystemPalette) const;
@@ -301,10 +319,14 @@ public:
static QVariant defaultThemeHint(ThemeHint hint);
static QString defaultStandardButtonText(int button);
static QString removeMnemonics(const QString &original);
+ QString name() const;
protected:
explicit QPlatformTheme(QPlatformThemePrivate *priv);
QScopedPointer<QPlatformThemePrivate> d_ptr;
+
+private:
+ friend class QPlatformThemeFactory;
};
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qplatformtheme_p.h b/src/gui/kernel/qplatformtheme_p.h
index bdf8a34e19..e847ead3be 100644
--- a/src/gui/kernel/qplatformtheme_p.h
+++ b/src/gui/kernel/qplatformtheme_p.h
@@ -41,6 +41,8 @@ public:
static unsigned currentKeyPlatforms();
QPalette *systemPalette;
+
+ QString name;
};
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qplatformthemefactory.cpp b/src/gui/kernel/qplatformthemefactory.cpp
index a66ad0f880..beefa1c294 100644
--- a/src/gui/kernel/qplatformthemefactory.cpp
+++ b/src/gui/kernel/qplatformthemefactory.cpp
@@ -8,21 +8,26 @@
#include "qmutex.h"
#include "qguiapplication.h"
+#include "qplatformtheme.h"
+#include "qplatformtheme_p.h"
#include "qdebug.h"
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, ptLoader,
(QPlatformThemeFactoryInterface_iid, "/platformthemes"_L1, Qt::CaseInsensitive))
QPlatformTheme *QPlatformThemeFactory::create(const QString& key, const QString &platformPluginPath)
{
QStringList paramList = key.split(u':');
const QString platform = paramList.takeFirst().toLower();
- loader->setExtraSearchPath(platformPluginPath);
- return qLoadPlugin<QPlatformTheme, QPlatformThemePlugin>(loader(), platform, paramList);
+ ptLoader->setExtraSearchPath(platformPluginPath);
+ QPlatformTheme *theme = qLoadPlugin<QPlatformTheme, QPlatformThemePlugin>(ptLoader(), platform, paramList);
+ if (theme)
+ theme->d_func()->name = key;
+ return theme;
}
/*!
@@ -33,8 +38,8 @@ QPlatformTheme *QPlatformThemeFactory::create(const QString& key, const QString
*/
QStringList QPlatformThemeFactory::keys(const QString &platformPluginPath)
{
- loader->setExtraSearchPath(platformPluginPath);
- return loader->keyMap().values();
+ ptLoader->setExtraSearchPath(platformPluginPath);
+ return ptLoader->keyMap().values();
}
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qplatformwindow.cpp b/src/gui/kernel/qplatformwindow.cpp
index d1bbbe5c39..5c0ae2ee2a 100644
--- a/src/gui/kernel/qplatformwindow.cpp
+++ b/src/gui/kernel/qplatformwindow.cpp
@@ -370,18 +370,18 @@ void QPlatformWindow::setMask(const QRegion &region)
Reimplement to let Qt be able to request activation/focus for a window
Some window systems will probably not have callbacks for this functionality,
- and then calling QWindowSystemInterface::handleWindowActivated(QWindow *w)
+ and then calling QWindowSystemInterface::handleFocusWindowChanged(QWindow *w)
would be sufficient.
If the window system has some event handling/callbacks then call
- QWindowSystemInterface::handleWindowActivated(QWindow *w) when the window system
+ QWindowSystemInterface::handleFocusWindowChanged(QWindow *w) when the window system
gives the notification.
- Default implementation calls QWindowSystem::handleWindowActivated(QWindow *w)
+ Default implementation calls QWindowSystem::handleFocusWindowChanged(QWindow *w)
*/
void QPlatformWindow::requestActivateWindow()
{
- QWindowSystemInterface::handleWindowActivated(window());
+ QWindowSystemInterface::handleFocusWindowChanged(window());
}
/*!
@@ -727,20 +727,29 @@ QRect QPlatformWindow::initialGeometry(const QWindow *w, const QRect &initialGeo
should be delivered using QPlatformWindow::deliverUpdateRequest()
to not get out of sync with the internal state of QWindow.
- The default implementation posts an UpdateRequest event to the
- window after 5 ms. The additional time is there to give the event
- loop a bit of idle time to gather system events.
+ The default implementation posts an UpdateRequest event to the window after
+ an interval that is at most 5 ms. If the window's associated screen reports
+ a \l{QPlatformScreen::refreshRate()}{refresh rate} higher than 60 Hz, the
+ interval is scaled down to a valid smaller than 5. The additional time is
+ there to give the event loop a bit of idle time to gather system events.
*/
void QPlatformWindow::requestUpdate()
{
Q_D(QPlatformWindow);
- static int updateInterval = []() {
- bool ok = false;
- int customUpdateInterval = qEnvironmentVariableIntValue("QT_QPA_UPDATE_IDLE_TIME", &ok);
- return ok ? customUpdateInterval : 5;
- }();
+ static bool customUpdateIntervalValid = false;
+ static int customUpdateInterval = qEnvironmentVariableIntValue("QT_QPA_UPDATE_IDLE_TIME",
+ &customUpdateIntervalValid);
+ int updateInterval = customUpdateInterval;
+ if (!customUpdateIntervalValid) {
+ updateInterval = 5;
+ if (QPlatformScreen *currentScreen = screen()) {
+ const qreal refreshRate = currentScreen->refreshRate();
+ if (refreshRate > 60.0)
+ updateInterval /= refreshRate / 60.0;
+ }
+ }
Q_ASSERT(!d->updateTimer.isActive());
d->updateTimer.start(updateInterval, Qt::PreciseTimer, window());
@@ -769,6 +778,15 @@ void QPlatformWindow::deliverUpdateRequest()
QWindow *w = window();
QWindowPrivate *wp = qt_window_private(w);
+
+ // We expect that the platform plugins send DevicePixelRatioChange events.
+ // As a fail-safe make a final check here to make sure the cached DPR value is
+ // always up to date before delivering the update request.
+ if (wp->updateDevicePixelRatio()) {
+ qWarning() << "The cached device pixel ratio value was stale on window update. "
+ << "Please file a QTBUG which explains how to reproduce.";
+ }
+
wp->updateRequestPending = false;
QEvent request(QEvent::UpdateRequest);
QCoreApplication::sendEvent(w, &request);
diff --git a/src/gui/kernel/qplatformwindow_p.h b/src/gui/kernel/qplatformwindow_p.h
index 8facac927d..2bbdfd5bf9 100644
--- a/src/gui/kernel/qplatformwindow_p.h
+++ b/src/gui/kernel/qplatformwindow_p.h
@@ -19,6 +19,14 @@
#include <QtCore/qbasictimer.h>
#include <QtCore/qrect.h>
#include <QtCore/qnativeinterface.h>
+#include <QtGui/qwindow.h>
+
+#if QT_CONFIG(wayland)
+#include <any>
+#include <QtCore/qobject.h>
+
+struct wl_surface;
+#endif
QT_BEGIN_NAMESPACE
@@ -35,7 +43,16 @@ public:
namespace QNativeInterface::Private {
-#if defined(Q_OS_MACOS) || defined(Q_CLANG_QDOC)
+#if defined(Q_OS_WASM) || defined(Q_QDOC)
+struct Q_GUI_EXPORT QWasmWindow
+{
+ QT_DECLARE_NATIVE_INTERFACE(QWasmWindow, 1, QWindow)
+ virtual emscripten::val document() const = 0;
+ virtual emscripten::val clientArea() const = 0;
+};
+#endif
+
+#if defined(Q_OS_MACOS) || defined(Q_QDOC)
struct Q_GUI_EXPORT QCocoaWindow
{
QT_DECLARE_NATIVE_INTERFACE(QCocoaWindow, 1, QWindow)
@@ -44,7 +61,7 @@ struct Q_GUI_EXPORT QCocoaWindow
};
#endif
-#if QT_CONFIG(xcb) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(xcb) || defined(Q_QDOC)
struct Q_GUI_EXPORT QXcbWindow
{
QT_DECLARE_NATIVE_INTERFACE(QXcbWindow, 1, QWindow)
@@ -76,7 +93,7 @@ struct Q_GUI_EXPORT QXcbWindow
};
#endif // xcb
-#if defined(Q_OS_WIN) || defined(Q_CLANG_QDOC)
+#if defined(Q_OS_WIN) || defined(Q_QDOC)
struct Q_GUI_EXPORT QWindowsWindow
{
QT_DECLARE_NATIVE_INTERFACE(QWindowsWindow, 1, QWindow)
@@ -89,6 +106,35 @@ struct Q_GUI_EXPORT QWindowsWindow
};
#endif // Q_OS_WIN
+#if QT_CONFIG(wayland)
+struct Q_GUI_EXPORT QWaylandWindow : public QObject
+{
+ Q_OBJECT
+public:
+ QT_DECLARE_NATIVE_INTERFACE(QWaylandWindow, 1, QWindow)
+
+ virtual wl_surface *surface() const = 0;
+ virtual void setCustomMargins(const QMargins &margins) = 0;
+ virtual void requestXdgActivationToken(uint serial) = 0;
+ template<typename T>
+ T *surfaceRole() const
+ {
+ std::any anyRole = _surfaceRole();
+ auto role = std::any_cast<T *>(&anyRole);
+ return role ? *role : nullptr;
+ }
+Q_SIGNALS:
+ void surfaceCreated();
+ void surfaceDestroyed();
+ void surfaceRoleCreated();
+ void surfaceRoleDestroyed();
+ void xdgActivationTokenCreated(const QString &token);
+
+protected:
+ virtual std::any _surfaceRole() const = 0;
+};
+#endif
+
} // QNativeInterface::Private
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qpointingdevice.cpp b/src/gui/kernel/qpointingdevice.cpp
index e84ba32f4a..c4c1e5fd5c 100644
--- a/src/gui/kernel/qpointingdevice.cpp
+++ b/src/gui/kernel/qpointingdevice.cpp
@@ -111,6 +111,48 @@ Q_LOGGING_CATEGORY(lcPointerGrab, "qt.pointer.grab");
Any of the above (used as a default filter value).
*/
+/*! \enum QPointingDevice::GrabTransition
+
+ This enum represents a transition of exclusive or passive grab
+ from one object (possibly \c nullptr) to another (possibly \c nullptr).
+ It is emitted as an argument of the QPointingDevice::grabChanged() signal.
+
+ Valid values are:
+
+ \value GrabExclusive
+ Emitted after QPointerEvent::setExclusiveGrabber().
+ \value UngrabExclusive
+ Emitted after QPointerEvent::setExclusiveGrabber() when the grabber is
+ set to \c nullptr, to notify that the grab has terminated normally.
+ \value CancelGrabExclusive
+ Emitted after QPointerEvent::setExclusiveGrabber() when the grabber is set
+ to a different object, to notify that the old grabber's grab is "stolen".
+ \value GrabPassive
+ Emitted after QPointerEvent::addPassiveGrabber().
+ \value UngrabPassive
+ Emitted when a passive grab is terminated normally,
+ for example after QPointerEvent::removePassiveGrabber().
+ \value CancelGrabPassive
+ Emitted when a passive grab is terminated abnormally (a gesture is canceled).
+ \value OverrideGrabPassive
+ This value is not currently used.
+*/
+
+/*! \fn void QPointingDevice::grabChanged(QObject *grabber, QPointingDevice::GrabTransition transition, const QPointerEvent *event, const QEventPoint &point) const
+
+ This signal is emitted when the \a grabber object gains or loses an
+ exclusive or passive grab of \a point during delivery of \a event.
+ The \a transition tells what happened, from the perspective of the
+ \c grabber object.
+
+ \note A grab transition from one object to another results in two signals,
+ to notify that one object has lost its grab, and to notify that there is
+ another grabber. In other cases, when transitioning to or from a non-grabbing
+ state, only one signal is emitted: the \a grabber argument is never \c nullptr.
+
+ \sa QPointerEvent::setExclusiveGrabber(), QPointerEvent::addPassiveGrabber(), QPointerEvent::removePassiveGrabber()
+*/
+
/*!
Creates a new invalid pointing device instance as a child of \a parent.
*/
@@ -145,6 +187,7 @@ QPointingDevice::QPointingDevice(QPointingDevicePrivate &d, QObject *parent)
{
}
+#if QT_DEPRECATED_SINCE(6, 0)
/*!
\internal
\deprecated [6.0] Please use the constructor rather than setters.
@@ -195,6 +238,7 @@ void QPointingDevice::setMaximumTouchPoints(int c)
Q_D(QPointingDevice);
d->maximumTouchPoints = c;
}
+#endif // QT_DEPRECATED_SINCE(6, 0)
/*!
Returns the pointer type.
@@ -275,7 +319,7 @@ const QPointingDevice *QPointingDevice::primaryPointingDevice(const QString& sea
QInputDevicePrivate::registerDevice(mouse);
return mouse;
}
- if (v.length() > 1)
+ if (v.size() > 1)
qCDebug(lcQpaInputDevices) << "core pointer ambiguous for seat" << seatName;
if (mouse)
return mouse;
@@ -463,6 +507,7 @@ void QPointingDevicePrivate::setExclusiveGrabber(const QPointerEvent *event, con
qWarning() << "point is not in activePoints" << point;
return;
}
+ Q_ASSERT(persistentPoint->eventPoint.id() == point.id());
if (persistentPoint->exclusiveGrabber == exclusiveGrabber)
return;
auto oldGrabber = persistentPoint->exclusiveGrabber;
@@ -519,7 +564,7 @@ bool QPointingDevicePrivate::addPassiveGrabber(const QPointerEvent *event, const
bool QPointingDevicePrivate::setPassiveGrabberContext(QPointingDevicePrivate::EventPointData *epd, QObject *grabber, QObject *context)
{
- int i = epd->passiveGrabbers.indexOf(grabber);
+ qsizetype i = epd->passiveGrabbers.indexOf(grabber);
if (i < 0)
return false;
if (epd->passiveGrabbersContext.size() <= i)
@@ -536,7 +581,7 @@ bool QPointingDevicePrivate::removePassiveGrabber(const QPointerEvent *event, co
qWarning() << "point is not in activePoints" << point;
return false;
}
- int i = persistentPoint->passiveGrabbers.indexOf(grabber);
+ qsizetype i = persistentPoint->passiveGrabbers.indexOf(grabber);
if (i >= 0) {
if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
@@ -599,7 +644,7 @@ void QPointingDevicePrivate::removeGrabber(QObject *grabber, bool cancel)
cancel ? QPointingDevice::CancelGrabExclusive : QPointingDevice::UngrabExclusive,
nullptr, epd.eventPoint);
}
- int pi = epd.passiveGrabbers.indexOf(grabber);
+ qsizetype pi = epd.passiveGrabbers.indexOf(grabber);
if (pi >= 0) {
qCDebug(lcPointerGrab) << name << "point" << epd.eventPoint.id() << epd.eventPoint.state()
<< ": removing passive grabber" << grabber;
@@ -663,7 +708,7 @@ QDebug operator<<(QDebug debug, const QPointingDevice *device)
if (device) {
debug << '"' << device->name() << "\" ";
QtDebugUtils::formatQEnum(debug, device->type());
- debug << " id=" << Qt::hex << device->systemId() << Qt::dec;
+ debug << " id=" << device->systemId();
if (!device->seatName().isEmpty())
debug << " seat=" << device->seatName();
if (device->pointerType() != QPointingDevice::PointerType::Generic) {
diff --git a/src/gui/kernel/qpointingdevice.h b/src/gui/kernel/qpointingdevice.h
index de33e27bd0..b8e6460af7 100644
--- a/src/gui/kernel/qpointingdevice.h
+++ b/src/gui/kernel/qpointingdevice.h
@@ -107,7 +107,13 @@ public:
bool operator==(const QPointingDevice &other) const;
Q_SIGNALS:
- void grabChanged(QObject *grabber, GrabTransition transition, const QPointerEvent *event, const QEventPoint &point) const;
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ void grabChanged(QObject *grabber, GrabTransition transition,
+ const QPointerEvent *event, const QEventPoint &point) const;
+#else
+ void grabChanged(QObject *grabber, QPointingDevice::GrabTransition transition,
+ const QPointerEvent *event, const QEventPoint &point);
+#endif
protected:
QPointingDevice(QPointingDevicePrivate &d, QObject *parent);
diff --git a/src/gui/kernel/qpointingdevice_p.h b/src/gui/kernel/qpointingdevice_p.h
index 3638dbc39a..b2f0574e9b 100644
--- a/src/gui/kernel/qpointingdevice_p.h
+++ b/src/gui/kernel/qpointingdevice_p.h
@@ -20,6 +20,8 @@
#include <QtGui/qpointingdevice.h>
#include <QtGui/private/qtguiglobal_p.h>
#include <QtGui/private/qinputdevice_p.h>
+
+#include <QtCore/qpointer.h>
#include <QtCore/private/qflatmap_p.h>
QT_BEGIN_NAMESPACE
@@ -72,7 +74,7 @@ public:
void clearPassiveGrabbers(const QPointerEvent *event, const QEventPoint &point);
void removeGrabber(QObject *grabber, bool cancel = false);
- using EventPointMap = QFlatMap<int, EventPointData>;
+ using EventPointMap = QVarLengthFlatMap<int, EventPointData, 20>;
mutable EventPointMap activePoints;
QPointingDeviceUniqueId uniqueId;
diff --git a/src/gui/kernel/qrasterwindow.cpp b/src/gui/kernel/qrasterwindow.cpp
index 19d84c601d..f292344ca1 100644
--- a/src/gui/kernel/qrasterwindow.cpp
+++ b/src/gui/kernel/qrasterwindow.cpp
@@ -34,14 +34,20 @@ class QRasterWindowPrivate : public QPaintDeviceWindowPrivate
{
Q_DECLARE_PUBLIC(QRasterWindow)
public:
+ void handleResizeEvent() override
+ {
+ Q_Q(QRasterWindow);
+ if (backingstore->size() != q->size())
+ markWindowAsDirty();
+ }
+
void beginPaint(const QRegion &region) override
{
Q_Q(QRasterWindow);
const QSize size = q->size();
- if (backingstore->size() != size) {
+ if (backingstore->size() != size)
backingstore->resize(size);
- markWindowAsDirty();
- }
+
backingstore->beginPaint(region);
}
@@ -102,6 +108,10 @@ QPaintDevice *QRasterWindow::redirected(QPoint *) const
return d->backingstore->paintDevice();
}
+void QRasterWindow::resizeEvent(QResizeEvent *)
+{
+}
+
QT_END_NAMESPACE
#include "moc_qrasterwindow.cpp"
diff --git a/src/gui/kernel/qrasterwindow.h b/src/gui/kernel/qrasterwindow.h
index 05d71e1655..986bf6b511 100644
--- a/src/gui/kernel/qrasterwindow.h
+++ b/src/gui/kernel/qrasterwindow.h
@@ -23,6 +23,7 @@ public:
protected:
int metric(PaintDeviceMetric metric) const override;
QPaintDevice *redirected(QPoint *) const override;
+ void resizeEvent(QResizeEvent *event) override;
private:
Q_DISABLE_COPY(QRasterWindow)
diff --git a/src/gui/kernel/qscreen.cpp b/src/gui/kernel/qscreen.cpp
index fd0e2ba2f7..83641e7676 100644
--- a/src/gui/kernel/qscreen.cpp
+++ b/src/gui/kernel/qscreen.cpp
@@ -37,100 +37,42 @@ QT_BEGIN_NAMESPACE
\inmodule QtGui
*/
-QScreen::QScreen(QPlatformScreen *screen)
+QScreen::QScreen(QPlatformScreen *platformScreen)
: QObject(*new QScreenPrivate(), nullptr)
{
Q_D(QScreen);
- d->setPlatformScreen(screen);
-}
-void QScreenPrivate::updateGeometriesWithSignals()
-{
- const QRect oldGeometry = geometry;
- const QRect oldAvailableGeometry = availableGeometry;
- updateHighDpi();
- emitGeometryChangeSignals(oldGeometry != geometry, oldAvailableGeometry != availableGeometry);
-}
+ d->platformScreen = platformScreen;
+ platformScreen->d_func()->screen = this;
-void QScreenPrivate::emitGeometryChangeSignals(bool geometryChanged, bool availableGeometryChanged)
-{
- Q_Q(QScreen);
- if (geometryChanged)
- emit q->geometryChanged(geometry);
-
- if (availableGeometryChanged)
- emit q->availableGeometryChanged(availableGeometry);
-
- if (geometryChanged || availableGeometryChanged) {
- const auto siblings = q->virtualSiblings();
- for (QScreen* sibling : siblings)
- emit sibling->virtualGeometryChanged(sibling->virtualGeometry());
- }
+ d->orientation = platformScreen->orientation();
+ d->logicalDpi = QPlatformScreen::overrideDpi(platformScreen->logicalDpi());
+ d->refreshRate = platformScreen->refreshRate();
+ // safeguard ourselves against buggy platform behavior...
+ if (d->refreshRate < 1.0)
+ d->refreshRate = 60.0;
- if (geometryChanged)
- emit q->physicalDotsPerInchChanged(q->physicalDotsPerInch());
+ d->updateGeometry();
+ d->updatePrimaryOrientation(); // derived from the geometry
}
-void QScreenPrivate::setPlatformScreen(QPlatformScreen *screen)
+void QScreenPrivate::updateGeometry()
{
- Q_Q(QScreen);
- platformScreen = screen;
- platformScreen->d_func()->screen = q;
- orientation = platformScreen->orientation();
-
- logicalDpi = QPlatformScreen::overrideDpi(platformScreen->logicalDpi());
-
- refreshRate = platformScreen->refreshRate();
- // safeguard ourselves against buggy platform behavior...
- if (refreshRate < 1.0)
- refreshRate = 60.0;
-
- updateHighDpi();
- updatePrimaryOrientation(); // derived from the geometry
+ qreal scaleFactor = QHighDpiScaling::factor(platformScreen);
+ QRect nativeGeometry = platformScreen->geometry();
+ geometry = QRect(nativeGeometry.topLeft(), QHighDpi::fromNative(nativeGeometry.size(), scaleFactor));
+ availableGeometry = QHighDpi::fromNative(platformScreen->availableGeometry(), scaleFactor, geometry.topLeft());
}
-
/*!
Destroys the screen.
+
+ \internal
*/
QScreen::~QScreen()
{
- // Remove screen
- const bool wasPrimary = QGuiApplication::primaryScreen() == this;
- QGuiApplicationPrivate::screen_list.removeOne(this);
- QGuiApplicationPrivate::resetCachedDevicePixelRatio();
-
- if (!qGuiApp)
- return;
-
- QScreen *newPrimaryScreen = QGuiApplication::primaryScreen();
- if (wasPrimary && newPrimaryScreen)
- emit qGuiApp->primaryScreenChanged(newPrimaryScreen);
-
- // Allow clients to manage windows that are affected by the screen going
- // away, before we fall back to moving them to the primary screen.
- emit qApp->screenRemoved(this);
-
- if (QGuiApplication::closingDown())
- return;
-
- bool movingFromVirtualSibling = newPrimaryScreen
- && newPrimaryScreen->handle()->virtualSiblings().contains(handle());
-
- // Move any leftover windows to the primary screen
- const auto allWindows = QGuiApplication::allWindows();
- for (QWindow *window : allWindows) {
- if (!window->isTopLevel() || window->screen() != this)
- continue;
-
- const bool wasVisible = window->isVisible();
- window->setScreen(newPrimaryScreen);
-
- // Re-show window if moved from a virtual sibling screen. Otherwise
- // leave it up to the application developer to show the window.
- if (movingFromVirtualSibling)
- window->setVisible(wasVisible);
- }
+ Q_ASSERT_X(!QGuiApplicationPrivate::screen_list.contains(this), "QScreen",
+ "QScreens should be removed via QWindowSystemInterface::handleScreenRemoved()");
}
/*!
@@ -150,6 +92,10 @@ QPlatformScreen *QScreen::handle() const
For example, on X11 these correspond to the XRandr screen names,
typically "VGA1", "HDMI1", etc.
+
+ \note The user presentable string is not guaranteed to match the
+ result of any native APIs, and should not be used to uniquely identify
+ a screen.
*/
QString QScreen::name() const
{
@@ -413,9 +359,12 @@ QList<QScreen *> QScreen::virtualSiblings() const
Q_D(const QScreen);
const QList<QPlatformScreen *> platformScreens = d->platformScreen->virtualSiblings();
QList<QScreen *> screens;
- screens.reserve(platformScreens.count());
- for (QPlatformScreen *platformScreen : platformScreens)
- screens << platformScreen->screen();
+ screens.reserve(platformScreens.size());
+ for (QPlatformScreen *platformScreen : platformScreens) {
+ // Only consider platform screens that have been added
+ if (auto *knownScreen = platformScreen->screen())
+ screens << knownScreen;
+ }
return screens;
}
@@ -512,8 +461,8 @@ Qt::ScreenOrientation QScreen::orientation() const
\property QScreen::refreshRate
\brief the approximate vertical refresh rate of the screen in Hz
- \warning Avoid using the screen's refresh rate to drive animations
- via a timer such as QTimer. Instead use QWindow::requestUpdate().
+ \warning Avoid using the screen's refresh rate to drive animations via a
+ timer such as QChronoTimer. Instead use QWindow::requestUpdate().
\sa QWindow::requestUpdate()
*/
@@ -754,8 +703,23 @@ QPixmap QScreen::grabWindow(WId window, int x, int y, int width, int height)
result.setDevicePixelRatio(result.devicePixelRatio() * factor);
return result;
}
+
+/*!
+ \fn template <typename QNativeInterface> QNativeInterface *QScreen::nativeInterface() const
+
+ Returns a native interface of the given type for the screen.
+
+ This function provides access to platform specific functionality
+ of QScreen, as defined in the QNativeInterface namespace:
+
+ \annotatedlist native-interfaces-qscreen
+
+ If the requested interface is not available a \nullptr is returned.
+ */
+
void *QScreen::resolveInterface(const char *name, int revision) const
{
+ using namespace QNativeInterface;
using namespace QNativeInterface::Private;
auto *platformScreen = handle();
@@ -775,6 +739,18 @@ void *QScreen::resolveInterface(const char *name, int revision) const
QT_NATIVE_INTERFACE_RETURN_IF(QWebOSScreen, platformScreen);
#endif
+#if defined(Q_OS_WIN32)
+ QT_NATIVE_INTERFACE_RETURN_IF(QWindowsScreen, platformScreen);
+#endif
+
+#if defined(Q_OS_ANDROID)
+ QT_NATIVE_INTERFACE_RETURN_IF(QAndroidScreen, platformScreen);
+#endif
+
+#if QT_CONFIG(wayland)
+ QT_NATIVE_INTERFACE_RETURN_IF(QWaylandScreen, platformScreen);
+#endif
+
return nullptr;
}
@@ -806,6 +782,60 @@ Q_GUI_EXPORT QDebug operator<<(QDebug debug, const QScreen *screen)
}
#endif // !QT_NO_DEBUG_STREAM
+QScreenPrivate::UpdateEmitter::UpdateEmitter(QScreen *screen)
+{
+ initialState.platformScreen = screen->handle();
+
+ // Use public APIs to read out current state, rather
+ // than accessing the QScreenPrivate members, so that
+ // we detect any changes to the high-DPI scale factors
+ // that may be applied in the getters.
+
+ initialState.logicalDpi = QDpi{
+ screen->logicalDotsPerInchX(),
+ screen->logicalDotsPerInchY()
+ };
+ initialState.geometry = screen->geometry();
+ initialState.availableGeometry = screen->availableGeometry();
+ initialState.primaryOrientation = screen->primaryOrientation();
+}
+
+QScreenPrivate::UpdateEmitter::~UpdateEmitter()
+{
+ QScreen *screen = initialState.platformScreen->screen();
+
+ const auto logicalDotsPerInch = QDpi{
+ screen->logicalDotsPerInchX(),
+ screen->logicalDotsPerInchY()
+ };
+ if (logicalDotsPerInch != initialState.logicalDpi)
+ emit screen->logicalDotsPerInchChanged(screen->logicalDotsPerInch());
+
+ const auto geometry = screen->geometry();
+ const auto geometryChanged = geometry != initialState.geometry;
+ if (geometryChanged)
+ emit screen->geometryChanged(geometry);
+
+ const auto availableGeometry = screen->availableGeometry();
+ const auto availableGeometryChanged = availableGeometry != initialState.availableGeometry;
+ if (availableGeometryChanged)
+ emit screen->availableGeometryChanged(availableGeometry);
+
+ if (geometryChanged || availableGeometryChanged) {
+ const auto siblings = screen->virtualSiblings();
+ for (QScreen* sibling : siblings)
+ emit sibling->virtualGeometryChanged(sibling->virtualGeometry());
+ }
+
+ if (geometryChanged) {
+ emit screen->physicalDotsPerInchChanged(screen->physicalDotsPerInch());
+
+ const auto primaryOrientation = screen->primaryOrientation();
+ if (primaryOrientation != initialState.primaryOrientation)
+ emit screen->primaryOrientationChanged(primaryOrientation);
+ }
+}
+
QT_END_NAMESPACE
#include "moc_qscreen.cpp"
diff --git a/src/gui/kernel/qscreen.h b/src/gui/kernel/qscreen.h
index d2fc74fbda..9442e7525b 100644
--- a/src/gui/kernel/qscreen.h
+++ b/src/gui/kernel/qscreen.h
@@ -147,5 +147,7 @@ Q_GUI_EXPORT QDebug operator<<(QDebug, const QScreen *);
QT_END_NAMESPACE
+#include <QtGui/qscreen_platform.h>
+
#endif // QSCREEN_H
diff --git a/src/gui/kernel/qscreen_p.h b/src/gui/kernel/qscreen_p.h
index 1ba0c7c442..964bc1cc58 100644
--- a/src/gui/kernel/qscreen_p.h
+++ b/src/gui/kernel/qscreen_p.h
@@ -24,21 +24,8 @@
QT_BEGIN_NAMESPACE
-class QScreenPrivate : public QObjectPrivate
+struct QScreenData
{
- Q_DECLARE_PUBLIC(QScreen)
-public:
- void setPlatformScreen(QPlatformScreen *screen);
- void updateHighDpi()
- {
- geometry = platformScreen->deviceIndependentGeometry();
- availableGeometry = QHighDpi::fromNative(platformScreen->availableGeometry(), QHighDpiScaling::factor(platformScreen), geometry.topLeft());
- }
-
- void updatePrimaryOrientation();
- void updateGeometriesWithSignals();
- void emitGeometryChangeSignals(bool geometryChanged, bool availableGeometryChanged);
-
QPlatformScreen *platformScreen = nullptr;
Qt::ScreenOrientation orientation = Qt::PrimaryOrientation;
@@ -49,6 +36,25 @@ public:
qreal refreshRate = 60;
};
+class QScreenPrivate : public QObjectPrivate, public QScreenData
+{
+ Q_DECLARE_PUBLIC(QScreen)
+public:
+ void updateGeometry();
+ void updatePrimaryOrientation();
+
+ class UpdateEmitter
+ {
+ public:
+ explicit UpdateEmitter(QScreen *screen);
+ ~UpdateEmitter();
+ UpdateEmitter(UpdateEmitter&&) noexcept = default;
+ private:
+ Q_DISABLE_COPY(UpdateEmitter)
+ QScreenData initialState;
+ };
+};
+
QT_END_NAMESPACE
#endif // QSCREEN_P_H
diff --git a/src/gui/kernel/qscreen_platform.h b/src/gui/kernel/qscreen_platform.h
new file mode 100644
index 0000000000..6f40e9273f
--- /dev/null
+++ b/src/gui/kernel/qscreen_platform.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSCREEN_PLATFORM_H
+#define QSCREEN_PLATFORM_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is part of the native interface APIs. Usage of
+// this API may make your code source and binary incompatible
+// with future versions of Qt.
+//
+
+#include <QtGui/qtguiglobal.h>
+
+#include <QtCore/qnativeinterface.h>
+#include <QtGui/qguiapplication.h>
+
+#if defined(Q_OS_WIN32)
+#include <QtGui/qwindowdefs_win.h>
+#endif
+
+#if QT_CONFIG(wayland)
+struct wl_output;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace QNativeInterface {
+
+#if defined(Q_OS_WIN32) || defined(Q_QDOC)
+struct Q_GUI_EXPORT QWindowsScreen
+{
+ QT_DECLARE_NATIVE_INTERFACE(QWindowsScreen, 1, QScreen)
+ virtual HMONITOR handle() const = 0;
+};
+#endif
+
+#if QT_CONFIG(wayland) || defined(Q_QDOC)
+struct Q_GUI_EXPORT QWaylandScreen
+{
+ QT_DECLARE_NATIVE_INTERFACE(QWaylandScreen, 1, QScreen)
+ virtual wl_output *output() const = 0;
+};
+#endif
+
+#if defined(Q_OS_ANDROID) || defined(Q_QDOC)
+struct Q_GUI_EXPORT QAndroidScreen
+{
+ QT_DECLARE_NATIVE_INTERFACE(QAndroidScreen, 1, QScreen)
+ virtual int displayId() const = 0;
+};
+#endif
+
+} // namespace QNativeInterface
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/kernel/qsessionmanager.cpp b/src/gui/kernel/qsessionmanager.cpp
index 705866f903..0a1ff95656 100644
--- a/src/gui/kernel/qsessionmanager.cpp
+++ b/src/gui/kernel/qsessionmanager.cpp
@@ -281,10 +281,6 @@ void QSessionManager::setRestartCommand(const QStringList &command)
/*!
Returns the currently set restart command.
- To iterate over the list, you can use the \l foreach pseudo-keyword:
-
- \snippet code/src_gui_kernel_qguiapplication.cpp 3
-
\sa setRestartCommand(), restartHint()
*/
QStringList QSessionManager::restartCommand() const
@@ -307,10 +303,6 @@ void QSessionManager::setDiscardCommand(const QStringList &command)
/*!
Returns the currently set discard command.
- To iterate over the list, you can use the \l foreach pseudo-keyword:
-
- \snippet code/src_gui_kernel_qguiapplication.cpp 4
-
\sa setDiscardCommand(), restartCommand(), setRestartCommand()
*/
QStringList QSessionManager::discardCommand() const
diff --git a/src/gui/kernel/qsessionmanager_p.h b/src/gui/kernel/qsessionmanager_p.h
index 60e20b1747..c98f7a5a1d 100644
--- a/src/gui/kernel/qsessionmanager_p.h
+++ b/src/gui/kernel/qsessionmanager_p.h
@@ -27,7 +27,7 @@ QT_BEGIN_NAMESPACE
class QPlatformSessionManager;
-class QSessionManagerPrivate : public QObjectPrivate
+class Q_GUI_EXPORT QSessionManagerPrivate : public QObjectPrivate
{
public:
QSessionManagerPrivate(const QString &id,
diff --git a/src/gui/kernel/qshapedpixmapdndwindow.cpp b/src/gui/kernel/qshapedpixmapdndwindow.cpp
index c33192778c..546a1a4937 100644
--- a/src/gui/kernel/qshapedpixmapdndwindow.cpp
+++ b/src/gui/kernel/qshapedpixmapdndwindow.cpp
@@ -20,7 +20,7 @@ QShapedPixmapWindow::QShapedPixmapWindow(QScreen *screen)
QSurfaceFormat format;
format.setAlphaBufferSize(8);
setFormat(format);
- setFlags(Qt::ToolTip | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint
+ setFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint
| Qt::WindowTransparentForInput | Qt::WindowDoesNotAcceptFocus);
}
diff --git a/src/gui/kernel/qshortcut.cpp b/src/gui/kernel/qshortcut.cpp
index eccbb5b873..3f6822cb03 100644
--- a/src/gui/kernel/qshortcut.cpp
+++ b/src/gui/kernel/qshortcut.cpp
@@ -96,7 +96,7 @@ QT_BEGIN_NAMESPACE
\sa activated()
*/
-static bool simpleContextMatcher(QObject *object, Qt::ShortcutContext context)
+bool QShortcutPrivate::simpleContextMatcher(QObject *object, Qt::ShortcutContext context)
{
auto guiShortcut = qobject_cast<QShortcut *>(object);
if (QGuiApplication::applicationState() != Qt::ApplicationActive || guiShortcut == nullptr)
@@ -127,14 +127,14 @@ void QShortcutPrivate::redoGrab(QShortcutMap &map)
return;
}
- for (int id : qAsConst(sc_ids))
+ for (int id : std::as_const(sc_ids))
map.removeShortcut(id, q);
sc_ids.clear();
if (sc_sequences.isEmpty())
return;
- sc_ids.reserve(sc_sequences.count());
- for (const auto &keySequence : qAsConst(sc_sequences)) {
+ sc_ids.reserve(sc_sequences.size());
+ for (const auto &keySequence : std::as_const(sc_sequences)) {
if (keySequence.isEmpty())
continue;
int id = map.addShortcut(q, keySequence, sc_context, contextMatcher());
@@ -337,7 +337,7 @@ QShortcut::~QShortcut()
{
Q_D(QShortcut);
if (qApp) {
- for (int id : qAsConst(d->sc_ids))
+ for (int id : std::as_const(d->sc_ids))
QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(id, this);
}
}
@@ -536,6 +536,8 @@ QString QShortcut::whatsThis() const
/*!
Returns the primary key binding's ID.
+ \deprecated
+
\sa QShortcutEvent::shortcutId()
*/
int QShortcut::id() const
diff --git a/src/gui/kernel/qshortcut.h b/src/gui/kernel/qshortcut.h
index dbdf6f97f9..00b9168bea 100644
--- a/src/gui/kernel/qshortcut.h
+++ b/src/gui/kernel/qshortcut.h
@@ -32,7 +32,7 @@ public:
const char *member = nullptr, const char *ambiguousMember = nullptr,
Qt::ShortcutContext context = Qt::WindowShortcut);
-#ifdef Q_CLANG_QDOC
+#ifdef Q_QDOC
template<typename Functor>
QShortcut(const QKeySequence &key, QObject *parent,
Functor functor,
@@ -170,17 +170,18 @@ public:
bool autoRepeat() const;
#if QT_DEPRECATED_SINCE(6,0)
- Q_DECL_DEPRECATED int id() const;
+ QT_DEPRECATED_VERSION_6_0 int id() const;
#endif
void setWhatsThis(const QString &text);
QString whatsThis() const;
#if QT_DEPRECATED_SINCE(6,0)
-#ifdef Q_CLANG_QDOC
+#ifdef Q_QDOC
QWidget *parentWidget() const;
#else
template<typename T = QWidget*>
+ QT_DEPRECATED_VERSION_X_6_0("Use parent() and qobject_cast instead")
inline T parentWidget() const
{ return static_cast<T>(QObject::parent()); }
#endif
diff --git a/src/gui/kernel/qshortcut_p.h b/src/gui/kernel/qshortcut_p.h
index a99cca27c1..8ff833d477 100644
--- a/src/gui/kernel/qshortcut_p.h
+++ b/src/gui/kernel/qshortcut_p.h
@@ -43,6 +43,8 @@ public:
virtual QShortcutMap::ContextMatcher contextMatcher() const;
virtual bool handleWhatsThis() { return false; }
+ static bool simpleContextMatcher(QObject *object, Qt::ShortcutContext context);
+
QList<QKeySequence> sc_sequences;
QString sc_whatsthis;
Qt::ShortcutContext sc_context = Qt::WindowShortcut;
diff --git a/src/gui/kernel/qshortcutmap.cpp b/src/gui/kernel/qshortcutmap.cpp
index b643393430..800e703ac2 100644
--- a/src/gui/kernel/qshortcutmap.cpp
+++ b/src/gui/kernel/qshortcutmap.cpp
@@ -29,23 +29,23 @@ Q_LOGGING_CATEGORY(lcShortcutMap, "qt.gui.shortcutmap")
struct QShortcutEntry
{
QShortcutEntry()
- : keyseq(0), context(Qt::WindowShortcut), enabled(false), autorepeat(1), id(0), owner(nullptr), contextMatcher(nullptr)
+ : keySequence(0), context(Qt::WindowShortcut), enabled(false), autorepeat(1), id(0), owner(nullptr), contextMatcher(nullptr)
{}
QShortcutEntry(const QKeySequence &k)
- : keyseq(k), context(Qt::WindowShortcut), enabled(false), autorepeat(1), id(0), owner(nullptr), contextMatcher(nullptr)
+ : keySequence(k), context(Qt::WindowShortcut), enabled(false), autorepeat(1), id(0), owner(nullptr), contextMatcher(nullptr)
{}
QShortcutEntry(QObject *o, const QKeySequence &k, Qt::ShortcutContext c, int i, bool a, QShortcutMap::ContextMatcher m)
- : keyseq(k), context(c), enabled(true), autorepeat(a), id(i), owner(o), contextMatcher(m)
+ : keySequence(k), context(c), enabled(true), autorepeat(a), id(i), owner(o), contextMatcher(m)
{}
bool correctContext() const { return contextMatcher(owner, context); }
bool operator<(const QShortcutEntry &f) const
- { return keyseq < f.keyseq; }
+ { return keySequence < f.keySequence; }
- QKeySequence keyseq;
+ QKeySequence keySequence;
Qt::ShortcutContext context;
bool enabled : 1;
bool autorepeat : 1;
@@ -88,7 +88,7 @@ public:
}
QShortcutMap *q_ptr; // Private's parent
- QList<QShortcutEntry> sequences; // All sequences!
+ QList<QShortcutEntry> shortcuts; // All shortcuts!
int currentId; // Global shortcut ID number
int ambigCount; // Index of last enabled ambiguous dispatch
@@ -120,18 +120,18 @@ QShortcutMap::~QShortcutMap()
Adds a shortcut to the global map.
Returns the id of the newly added shortcut.
*/
-int QShortcutMap::addShortcut(QObject *owner, const QKeySequence &key, Qt::ShortcutContext context, ContextMatcher matcher)
+int QShortcutMap::addShortcut(QObject *owner, const QKeySequence &keySequence, Qt::ShortcutContext context, ContextMatcher matcher)
{
Q_ASSERT_X(owner, "QShortcutMap::addShortcut", "All shortcuts need an owner");
- Q_ASSERT_X(!key.isEmpty(), "QShortcutMap::addShortcut", "Cannot add keyless shortcuts to map");
+ Q_ASSERT_X(!keySequence.isEmpty(), "QShortcutMap::addShortcut", "Cannot add keyless shortcuts to map");
Q_D(QShortcutMap);
- QShortcutEntry newEntry(owner, key, context, --(d->currentId), true, matcher);
- const auto it = std::upper_bound(d->sequences.begin(), d->sequences.end(), newEntry);
- d->sequences.insert(it, newEntry); // Insert sorted
+ QShortcutEntry newEntry(owner, keySequence, context, --(d->currentId), true, matcher);
+ const auto it = std::upper_bound(d->shortcuts.begin(), d->shortcuts.end(), newEntry);
+ d->shortcuts.insert(it, newEntry); // Insert sorted
qCDebug(lcShortcutMap).nospace()
<< "QShortcutMap::addShortcut(" << owner << ", "
- << key << ", " << context << ") added shortcut with ID " << d->currentId;
+ << keySequence << ", " << context << ") added shortcut with ID " << d->currentId;
return d->currentId;
}
@@ -144,36 +144,36 @@ int QShortcutMap::addShortcut(QObject *owner, const QKeySequence &key, Qt::Short
Returns the number of sequences removed from the map.
*/
-int QShortcutMap::removeShortcut(int id, QObject *owner, const QKeySequence &key)
+int QShortcutMap::removeShortcut(int id, QObject *owner, const QKeySequence &keySequence)
{
Q_D(QShortcutMap);
int itemsRemoved = 0;
bool allOwners = (owner == nullptr);
- bool allKeys = key.isEmpty();
+ bool allKeys = keySequence.isEmpty();
bool allIds = id == 0;
auto debug = qScopeGuard([&](){
qCDebug(lcShortcutMap).nospace()
<< "QShortcutMap::removeShortcut(" << id << ", " << owner << ", "
- << key << ") removed " << itemsRemoved << " shortcuts(s)";
+ << keySequence << ") removed " << itemsRemoved << " shortcuts(s)";
});
// Special case, remove everything
if (allOwners && allKeys && allIds) {
- itemsRemoved = d->sequences.size();
- d->sequences.clear();
+ itemsRemoved = d->shortcuts.size();
+ d->shortcuts.clear();
return itemsRemoved;
}
- int i = d->sequences.size()-1;
+ int i = d->shortcuts.size()-1;
while (i>=0)
{
- const QShortcutEntry &entry = d->sequences.at(i);
+ const QShortcutEntry &entry = d->shortcuts.at(i);
int entryId = entry.id;
if ((allOwners || entry.owner == owner)
&& (allIds || entry.id == id)
- && (allKeys || entry.keyseq == key)) {
- d->sequences.removeAt(i);
+ && (allKeys || entry.keySequence == keySequence)) {
+ d->shortcuts.removeAt(i);
++itemsRemoved;
}
if (id == entryId)
@@ -191,22 +191,22 @@ int QShortcutMap::removeShortcut(int id, QObject *owner, const QKeySequence &key
are changed.
Returns the number of sequences which are matched in the map.
*/
-int QShortcutMap::setShortcutEnabled(bool enable, int id, QObject *owner, const QKeySequence &key)
+int QShortcutMap::setShortcutEnabled(bool enable, int id, QObject *owner, const QKeySequence &keySequence)
{
Q_D(QShortcutMap);
int itemsChanged = 0;
bool allOwners = (owner == nullptr);
- bool allKeys = key.isEmpty();
+ bool allKeys = keySequence.isEmpty();
bool allIds = id == 0;
- int i = d->sequences.size()-1;
+ int i = d->shortcuts.size()-1;
while (i>=0)
{
- QShortcutEntry entry = d->sequences.at(i);
+ QShortcutEntry entry = d->shortcuts.at(i);
if ((allOwners || entry.owner == owner)
&& (allIds || entry.id == id)
- && (allKeys || entry.keyseq == key)) {
- d->sequences[i].enabled = enable;
+ && (allKeys || entry.keySequence == keySequence)) {
+ d->shortcuts[i].enabled = enable;
++itemsChanged;
}
if (id == entry.id)
@@ -215,7 +215,7 @@ int QShortcutMap::setShortcutEnabled(bool enable, int id, QObject *owner, const
}
qCDebug(lcShortcutMap).nospace()
<< "QShortcutMap::setShortcutEnabled(" << enable << ", " << id << ", "
- << owner << ", " << key << ") = " << itemsChanged;
+ << owner << ", " << keySequence << ") = " << itemsChanged;
return itemsChanged;
}
@@ -227,22 +227,22 @@ int QShortcutMap::setShortcutEnabled(bool enable, int id, QObject *owner, const
are changed.
Returns the number of sequences which are matched in the map.
*/
-int QShortcutMap::setShortcutAutoRepeat(bool on, int id, QObject *owner, const QKeySequence &key)
+int QShortcutMap::setShortcutAutoRepeat(bool on, int id, QObject *owner, const QKeySequence &keySequence)
{
Q_D(QShortcutMap);
int itemsChanged = 0;
bool allOwners = (owner == nullptr);
- bool allKeys = key.isEmpty();
+ bool allKeys = keySequence.isEmpty();
bool allIds = id == 0;
- int i = d->sequences.size()-1;
+ int i = d->shortcuts.size()-1;
while (i>=0)
{
- QShortcutEntry entry = d->sequences.at(i);
+ QShortcutEntry entry = d->shortcuts.at(i);
if ((allOwners || entry.owner == owner)
&& (allIds || entry.id == id)
- && (allKeys || entry.keyseq == key)) {
- d->sequences[i].autorepeat = on;
+ && (allKeys || entry.keySequence == keySequence)) {
+ d->shortcuts[i].autorepeat = on;
++itemsChanged;
}
if (id == entry.id)
@@ -251,7 +251,7 @@ int QShortcutMap::setShortcutAutoRepeat(bool on, int id, QObject *owner, const Q
}
qCDebug(lcShortcutMap).nospace()
<< "QShortcutMap::setShortcutAutoRepeat(" << on << ", " << id << ", "
- << owner << ", " << key << ") = " << itemsChanged;
+ << owner << ", " << keySequence << ") = " << itemsChanged;
return itemsChanged;
}
@@ -305,7 +305,7 @@ bool QShortcutMap::tryShortcut(QKeyEvent *e)
case QKeySequence::ExactMatch: {
// Save number of identical matches before dispatching
// to keep QShortcutMap and tryShortcut reentrant.
- const int identicalMatches = d->identicals.count();
+ const int identicalMatches = d->identicals.size();
resetState();
dispatchEvent(e);
// If there are no identicals we've only found disabled shortcuts, and
@@ -313,8 +313,7 @@ bool QShortcutMap::tryShortcut(QKeyEvent *e)
return identicalMatches > 0;
}
}
- Q_UNREACHABLE();
- return false;
+ Q_UNREACHABLE_RETURN(false);
}
/*! \internal
@@ -366,11 +365,12 @@ bool QShortcutMap::hasShortcutForKeySequence(const QKeySequence &seq) const
{
Q_D(const QShortcutMap);
QShortcutEntry entry(seq); // needed for searching
- const auto itEnd = d->sequences.cend();
- auto it = std::lower_bound(d->sequences.cbegin(), itEnd, entry);
+ const auto itEnd = d->shortcuts.cend();
+ auto it = std::lower_bound(d->shortcuts.cbegin(), itEnd, entry);
for (;it != itEnd; ++it) {
- if (matches(entry.keyseq, (*it).keyseq) == QKeySequence::ExactMatch && (*it).correctContext() && (*it).enabled) {
+ if (entry.keySequence.matches(it->keySequence) == QKeySequence::ExactMatch
+ && (*it).correctContext() && (*it).enabled) {
return true;
}
}
@@ -389,15 +389,15 @@ bool QShortcutMap::hasShortcutForKeySequence(const QKeySequence &seq) const
QKeySequence::SequenceMatch QShortcutMap::find(QKeyEvent *e, int ignoredModifiers)
{
Q_D(QShortcutMap);
- if (!d->sequences.count())
+ if (!d->shortcuts.size())
return QKeySequence::NoMatch;
createNewSequences(e, d->newEntries, ignoredModifiers);
- qCDebug(lcShortcutMap) << "Possible shortcut key sequences:" << d->newEntries;
+ qCDebug(lcShortcutMap) << "Possible input sequences:" << d->newEntries;
// Should never happen
if (d->newEntries == d->currentSequences) {
- Q_ASSERT_X(e->key() != Qt::Key_unknown || e->text().length(),
+ Q_ASSERT_X(e->key() != Qt::Key_unknown || e->text().size(),
"QShortcutMap::find", "New sequence to find identical to previous");
return QKeySequence::NoMatch;
}
@@ -408,26 +408,33 @@ QKeySequence::SequenceMatch QShortcutMap::find(QKeyEvent *e, int ignoredModifier
bool partialFound = false;
bool identicalDisabledFound = false;
QList<QKeySequence> okEntries;
- int result = QKeySequence::NoMatch;
- for (int i = d->newEntries.count()-1; i >= 0 ; --i) {
+ QKeySequence::SequenceMatch result = QKeySequence::NoMatch;
+ for (int i = d->newEntries.size()-1; i >= 0 ; --i) {
QShortcutEntry entry(d->newEntries.at(i)); // needed for searching
- const auto itEnd = d->sequences.constEnd();
- auto it = std::lower_bound(d->sequences.constBegin(), itEnd, entry);
+ qCDebug(lcShortcutMap) << "Looking for shortcuts matching" << entry.keySequence;
+
+ QKeySequence::SequenceMatch bestMatchForEntry = QKeySequence::NoMatch;
- int oneKSResult = QKeySequence::NoMatch;
- int tempRes = QKeySequence::NoMatch;
- do {
- if (it == itEnd)
+ const auto itEnd = d->shortcuts.constEnd();
+ auto it = std::lower_bound(d->shortcuts.constBegin(), itEnd, entry);
+ for (; it != itEnd; ++it) {
+ QKeySequence::SequenceMatch match = entry.keySequence.matches(it->keySequence);
+ qCDebug(lcShortcutMap) << " -" << match << "for shortcut" << it->keySequence;
+
+ // If we got a valid match, there might still be more keys to check against,
+ // but if we get no match, we know that there are no more possible matches.
+ if (match == QKeySequence::NoMatch)
break;
- tempRes = matches(entry.keyseq, (*it).keyseq);
- oneKSResult = qMax(oneKSResult, tempRes);
- if (tempRes != QKeySequence::NoMatch && (*it).correctContext()) {
- if (tempRes == QKeySequence::ExactMatch) {
+
+ bestMatchForEntry = qMax(bestMatchForEntry, match);
+
+ if ((*it).correctContext()) {
+ if (match == QKeySequence::ExactMatch) {
if ((*it).enabled)
d->identicals.append(&*it);
else
identicalDisabledFound = true;
- } else if (tempRes == QKeySequence::PartialMatch) {
+ } else if (match == QKeySequence::PartialMatch) {
// We don't need partials, if we have identicals
if (d->identicals.size())
break;
@@ -435,20 +442,18 @@ QKeySequence::SequenceMatch QShortcutMap::find(QKeyEvent *e, int ignoredModifier
// key events when all partials are disabled!
partialFound |= (*it).enabled;
}
+ } else {
+ qCDebug(lcShortcutMap) << " - But context was not correct";
}
- ++it;
- // If we got a valid match on this run, there might still be more keys to check against,
- // so we'll loop once more. If we get NoMatch, there's guaranteed no more possible
- // matches in the shortcutmap.
- } while (tempRes != QKeySequence::NoMatch);
+ }
// If the type of match improves (ergo, NoMatch->Partial, or Partial->Exact), clear the
// previous list. If this match is equal or better than the last match, append to the list
- if (oneKSResult > result) {
+ if (bestMatchForEntry > result) {
okEntries.clear();
qCDebug(lcShortcutMap) << "Found better match (" << d->newEntries << "), clearing key sequence list";
}
- if (oneKSResult && oneKSResult >= result) {
+ if (bestMatchForEntry && bestMatchForEntry >= result) {
okEntries << d->newEntries.at(i);
qCDebug(lcShortcutMap) << "Added ok key sequence" << d->newEntries;
}
@@ -467,7 +472,7 @@ QKeySequence::SequenceMatch QShortcutMap::find(QKeyEvent *e, int ignoredModifier
if (result != QKeySequence::NoMatch)
d->currentSequences = okEntries;
qCDebug(lcShortcutMap) << "Returning shortcut match == " << result;
- return QKeySequence::SequenceMatch(result);
+ return result;
}
/*! \internal
@@ -488,14 +493,14 @@ void QShortcutMap::clearSequence(QList<QKeySequence> &ksl)
void QShortcutMap::createNewSequences(QKeyEvent *e, QList<QKeySequence> &ksl, int ignoredModifiers)
{
Q_D(QShortcutMap);
- QList<int> possibleKeys = QKeyMapper::possibleKeys(e);
+ QList<QKeyCombination> possibleKeys = QKeyMapper::possibleKeys(e);
qCDebug(lcShortcutMap) << "Creating new sequences for" << e
<< "with ignoredModifiers=" << Qt::KeyboardModifiers(ignoredModifiers);
- int pkTotal = possibleKeys.count();
+ int pkTotal = possibleKeys.size();
if (!pkTotal)
return;
- int ssActual = d->currentSequences.count();
+ int ssActual = d->currentSequences.size();
int ssTotal = qMax(1, ssActual);
// Resize to possible permutations of the current sequence(s).
ksl.resize(pkTotal * ssTotal);
@@ -517,46 +522,13 @@ void QShortcutMap::createNewSequences(QKeyEvent *e, QList<QKeySequence> &ksl, in
curKsl.setKey(QKeyCombination::fromCombined(0), 2);
curKsl.setKey(QKeyCombination::fromCombined(0), 3);
}
- curKsl.setKey(QKeyCombination::fromCombined(possibleKeys.at(pkNum) & ~ignoredModifiers), index);
+ const int key = possibleKeys.at(pkNum).toCombined();
+ curKsl.setKey(QKeyCombination::fromCombined(key & ~ignoredModifiers), index);
}
}
}
/*! \internal
- Basically the same function as QKeySequence::matches(const QKeySequence &seq) const
- only that is specially handles Key_hyphen as Key_Minus, as people mix these up all the time and
- they conceptually the same.
-*/
-QKeySequence::SequenceMatch QShortcutMap::matches(const QKeySequence &seq1,
- const QKeySequence &seq2) const
-{
- uint userN = seq1.count(),
- seqN = seq2.count();
-
- if (userN > seqN)
- return QKeySequence::NoMatch;
-
- // If equal in length, we have a potential ExactMatch sequence,
- // else we already know it can only be partial.
- QKeySequence::SequenceMatch match = (userN == seqN
- ? QKeySequence::ExactMatch
- : QKeySequence::PartialMatch);
-
- for (uint i = 0; i < userN; ++i) {
- int userKey = seq1[i].toCombined(),
- sequenceKey = seq2[i].toCombined();
- if ((userKey & Qt::Key_unknown) == Qt::Key_hyphen)
- userKey = (userKey & Qt::KeyboardModifierMask) | Qt::Key_Minus;
- if ((sequenceKey & Qt::Key_unknown) == Qt::Key_hyphen)
- sequenceKey = (sequenceKey & Qt::KeyboardModifierMask) | Qt::Key_Minus;
- if (userKey != sequenceKey)
- return QKeySequence::NoMatch;
- }
- return match;
-}
-
-
-/*! \internal
Converts keyboard button states into modifier states
*/
int QShortcutMap::translateModifiers(Qt::KeyboardModifiers modifiers)
@@ -591,7 +563,7 @@ void QShortcutMap::dispatchEvent(QKeyEvent *e)
if (!d->identicals.size())
return;
- const QKeySequence &curKey = d->identicals.at(0)->keyseq;
+ const QKeySequence &curKey = d->identicals.at(0)->keySequence;
if (d->prevSequence != curKey) {
d->ambigCount = 0;
d->prevSequence = curKey;
@@ -621,16 +593,16 @@ void QShortcutMap::dispatchEvent(QKeyEvent *e)
if (lcShortcutMap().isDebugEnabled()) {
if (ambiguousShortcuts.size() > 1) {
qCDebug(lcShortcutMap) << "The following shortcuts are about to be activated ambiguously:";
- for (const QShortcutEntry *entry : qAsConst(ambiguousShortcuts))
- qCDebug(lcShortcutMap).nospace() << "- " << entry->keyseq << " (belonging to " << entry->owner << ")";
+ for (const QShortcutEntry *entry : std::as_const(ambiguousShortcuts))
+ qCDebug(lcShortcutMap).nospace() << "- " << entry->keySequence << " (belonging to " << entry->owner << ")";
}
qCDebug(lcShortcutMap).nospace()
<< "QShortcutMap::dispatchEvent(): Sending QShortcutEvent(\""
- << next->keyseq.toString() << "\", " << next->id << ", "
+ << next->keySequence.toString() << "\", " << next->id << ", "
<< static_cast<bool>(enabledShortcuts>1) << ") to object(" << next->owner << ')';
}
- QShortcutEvent se(next->keyseq, next->id, enabledShortcuts>1);
+ QShortcutEvent se(next->keySequence, next->id, enabledShortcuts > 1);
QCoreApplication::sendEvent(const_cast<QObject *>(next->owner), &se);
}
@@ -638,7 +610,7 @@ QList<QKeySequence> QShortcutMap::keySequences(bool getAll) const
{
Q_D(const QShortcutMap);
QList<QKeySequence> keys;
- for (auto sequence : d->sequences) {
+ for (auto sequence : d->shortcuts) {
bool addSequence = false;
if (sequence.enabled) {
if (getAll || sequence.context == Qt::ApplicationShortcut ||
@@ -667,7 +639,7 @@ QList<QKeySequence> QShortcutMap::keySequences(bool getAll) const
}
}
if (addSequence)
- keys << sequence.keyseq;
+ keys << sequence.keySequence;
}
}
return keys;
@@ -682,8 +654,8 @@ QList<QKeySequence> QShortcutMap::keySequences(bool getAll) const
void QShortcutMap::dumpMap() const
{
Q_D(const QShortcutMap);
- for (int i = 0; i < d->sequences.size(); ++i)
- qDebug().nospace() << &(d->sequences.at(i));
+ for (int i = 0; i < d->shortcuts.size(); ++i)
+ qDebug().nospace() << &(d->shortcuts.at(i));
}
#endif
diff --git a/src/gui/kernel/qshortcutmap_p.h b/src/gui/kernel/qshortcutmap_p.h
index 194738f0ac..26d2b5301c 100644
--- a/src/gui/kernel/qshortcutmap_p.h
+++ b/src/gui/kernel/qshortcutmap_p.h
@@ -62,7 +62,6 @@ private:
void dispatchEvent(QKeyEvent *e);
QKeySequence::SequenceMatch find(QKeyEvent *e, int ignoredModifiers = 0);
- QKeySequence::SequenceMatch matches(const QKeySequence &seq1, const QKeySequence &seq2) const;
QList<const QShortcutEntry *> matches() const;
void createNewSequences(QKeyEvent *e, QList<QKeySequence> &ksl, int ignoredModifiers);
void clearSequence(QList<QKeySequence> &ksl);
diff --git a/src/gui/kernel/qsimpledrag.cpp b/src/gui/kernel/qsimpledrag.cpp
index a88955b99a..a90e8dd33c 100644
--- a/src/gui/kernel/qsimpledrag.cpp
+++ b/src/gui/kernel/qsimpledrag.cpp
@@ -34,9 +34,10 @@ Q_LOGGING_CATEGORY(lcDnd, "qt.gui.dnd")
static QWindow* topLevelAt(const QPoint &pos)
{
- QWindowList list = QGuiApplication::topLevelWindows();
- for (int i = list.count()-1; i >= 0; --i) {
- QWindow *w = list.at(i);
+ const QWindowList list = QGuiApplication::topLevelWindows();
+ const auto crend = list.crend();
+ for (auto it = list.crbegin(); it != crend; ++it) {
+ QWindow *w = *it;
if (w->isVisible() && w->handle() && w->geometry().contains(pos) && !qobject_cast<QShapedPixmapWindow*>(w))
return w;
}
@@ -124,6 +125,7 @@ bool QBasicDrag::eventFilter(QObject *o, QEvent *e)
}
case QEvent::MouseButtonRelease:
{
+ QPointer<QObject> objGuard(o);
disableEventFilter();
if (canDrop()) {
QPoint nativePosition = getNativeMousePos(e, m_drag_icon_window);
@@ -133,6 +135,8 @@ bool QBasicDrag::eventFilter(QObject *o, QEvent *e)
cancel();
}
exitDndEventLoop();
+ if (!objGuard)
+ return true;
// If a QShapedPixmapWindow (drag feedback) is being dragged along, the
// mouse event's localPos() will be relative to that, which is useless.
@@ -306,7 +310,7 @@ static inline QPoint fromNativeGlobalPixels(const QPoint &point)
#ifndef QT_NO_HIGHDPISCALING
QPoint res = point;
if (QHighDpiScaling::isActive()) {
- for (const QScreen *s : qAsConst(QGuiApplicationPrivate::screen_list)) {
+ for (const QScreen *s : std::as_const(QGuiApplicationPrivate::screen_list)) {
if (s->handle()->geometry().contains(point)) {
res = QHighDpi::fromNativePixels(point, s);
break;
diff --git a/src/gui/kernel/qstylehints.cpp b/src/gui/kernel/qstylehints.cpp
index 042f793a27..5029701f24 100644
--- a/src/gui/kernel/qstylehints.cpp
+++ b/src/gui/kernel/qstylehints.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qstylehints.h>
+#include "qstylehints_p.h"
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformtheme.h>
#include <private/qguiapplication_p.h>
@@ -43,25 +44,6 @@ static inline QVariant themeableHint(QPlatformTheme::ThemeHint th)
return QPlatformTheme::defaultThemeHint(th);
}
-class QStyleHintsPrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QStyleHints)
-public:
- int m_mouseDoubleClickInterval = -1;
- int m_mousePressAndHoldInterval = -1;
- int m_startDragDistance = -1;
- int m_startDragTime = -1;
- int m_keyboardInputInterval = -1;
- int m_cursorFlashTime = -1;
- int m_tabFocusBehavior = -1;
- int m_uiEffects = -1;
- int m_showShortcutsInContextMenus = -1;
- int m_wheelScrollLines = -1;
- int m_mouseQuickSelectionThreshold = -1;
- int m_mouseDoubleClickDistance = -1;
- int m_touchDoubleTapDistance = -1;
-};
-
/*!
\class QStyleHints
\since 5.0
@@ -140,6 +122,18 @@ int QStyleHints::touchDoubleTapDistance() const
}
/*!
+ \property QStyleHints::colorScheme
+ \brief the color scheme of the platform theme.
+ \sa Qt::ColorScheme
+ \since 6.5
+*/
+Qt::ColorScheme QStyleHints::colorScheme() const
+{
+ Q_D(const QStyleHints);
+ return d->colorScheme();
+}
+
+/*!
Sets the \a mousePressAndHoldInterval.
\internal
\sa mousePressAndHoldInterval()
@@ -285,15 +279,29 @@ int QStyleHints::keyboardInputInterval() const
themeableHint(QPlatformTheme::KeyboardInputInterval, QPlatformIntegration::KeyboardInputInterval).toInt();
}
+#if QT_DEPRECATED_SINCE(6, 5)
/*!
\property QStyleHints::keyboardAutoRepeatRate
\brief the rate, in events per second, in which additional repeated key
presses will automatically be generated if a key is being held down.
+ \deprecated [6.5] Use keyboardAutoRepeatRateF() instead
*/
int QStyleHints::keyboardAutoRepeatRate() const
{
return themeableHint(QPlatformTheme::KeyboardAutoRepeatRate, QPlatformIntegration::KeyboardAutoRepeatRate).toInt();
}
+#endif
+
+/*!
+ \property QStyleHints::keyboardAutoRepeatRateF
+ \since 6.5
+ \brief the rate, in events per second, in which additional repeated key
+ presses will automatically be generated if a key is being held down.
+*/
+qreal QStyleHints::keyboardAutoRepeatRateF() const
+{
+ return themeableHint(QPlatformTheme::KeyboardAutoRepeatRate, QPlatformIntegration::KeyboardAutoRepeatRate).toReal();
+}
/*!
Sets the \a cursorFlashTime.
@@ -369,6 +377,8 @@ bool QStyleHints::showIsMaximized() const
Since Qt 5.13, the setShowShortcutsInContextMenus() function can be used to
override the platform default.
+
+ \sa Qt::AA_DontShowShortcutsInContextMenus
*/
bool QStyleHints::showShortcutsInContextMenus() const
{
@@ -439,7 +449,7 @@ bool QStyleHints::useRtlExtensions() const
*/
bool QStyleHints::setFocusOnTouchRelease() const
{
- return hint(QPlatformIntegration::SetFocusOnTouchRelease).toBool();
+ return themeableHint(QPlatformTheme::SetFocusOnTouchRelease, QPlatformIntegration::SetFocusOnTouchRelease).toBool();
}
/*!
@@ -583,6 +593,31 @@ int QStyleHints::mouseQuickSelectionThreshold() const
return themeableHint(QPlatformTheme::MouseQuickSelectionThreshold, QPlatformIntegration::MouseQuickSelectionThreshold).toInt();
}
+/*!
+ \internal
+ QStyleHintsPrivate::updateColorScheme - set a new color scheme.
+
+ This function is called by the QPA plugin when the system theme changes. This in
+ turn might be the result of an explicit request of a color scheme via setColorScheme.
+
+ Set \a colorScheme as the new color scheme of the QStyleHints.
+ The colorSchemeChanged signal will be emitted if present and new color scheme differ.
+ */
+void QStyleHintsPrivate::updateColorScheme(Qt::ColorScheme colorScheme)
+{
+ if (m_colorScheme == colorScheme)
+ return;
+ m_colorScheme = colorScheme;
+ Q_Q(QStyleHints);
+ emit q->colorSchemeChanged(colorScheme);
+}
+
+QStyleHintsPrivate *QStyleHintsPrivate::get(QStyleHints *q)
+{
+ Q_ASSERT(q);
+ return q->d_func();
+}
+
QT_END_NAMESPACE
#include "moc_qstylehints.cpp"
diff --git a/src/gui/kernel/qstylehints.h b/src/gui/kernel/qstylehints.h
index 8e54a76475..969bec4b35 100644
--- a/src/gui/kernel/qstylehints.h
+++ b/src/gui/kernel/qstylehints.h
@@ -19,7 +19,10 @@ class Q_GUI_EXPORT QStyleHints : public QObject
Q_DECLARE_PRIVATE(QStyleHints)
Q_PROPERTY(int cursorFlashTime READ cursorFlashTime NOTIFY cursorFlashTimeChanged FINAL)
Q_PROPERTY(qreal fontSmoothingGamma READ fontSmoothingGamma STORED false CONSTANT FINAL)
+#if QT_DEPRECATED_SINCE(6, 5)
Q_PROPERTY(int keyboardAutoRepeatRate READ keyboardAutoRepeatRate STORED false CONSTANT FINAL)
+#endif
+ Q_PROPERTY(qreal keyboardAutoRepeatRateF READ keyboardAutoRepeatRateF STORED false CONSTANT FINAL)
Q_PROPERTY(int keyboardInputInterval READ keyboardInputInterval
NOTIFY keyboardInputIntervalChanged FINAL)
Q_PROPERTY(int mouseDoubleClickInterval READ mouseDoubleClickInterval
@@ -49,6 +52,7 @@ class Q_GUI_EXPORT QStyleHints : public QObject
Q_PROPERTY(int mouseDoubleClickDistance READ mouseDoubleClickDistance STORED false CONSTANT
FINAL)
Q_PROPERTY(int touchDoubleTapDistance READ touchDoubleTapDistance STORED false CONSTANT FINAL)
+ Q_PROPERTY(Qt::ColorScheme colorScheme READ colorScheme NOTIFY colorSchemeChanged FINAL)
public:
void setMouseDoubleClickInterval(int mouseDoubleClickInterval);
@@ -64,7 +68,11 @@ public:
int startDragVelocity() const;
void setKeyboardInputInterval(int keyboardInputInterval);
int keyboardInputInterval() const;
+#if QT_DEPRECATED_SINCE(6, 5)
+ QT_DEPRECATED_VERSION_X_6_5("Use keyboardAutoRepeatRateF() instead")
int keyboardAutoRepeatRate() const;
+#endif
+ qreal keyboardAutoRepeatRateF() const;
void setCursorFlashTime(int cursorFlashTime);
int cursorFlashTime() const;
bool showIsFullScreen() const;
@@ -85,6 +93,7 @@ public:
void setWheelScrollLines(int scrollLines);
void setMouseQuickSelectionThreshold(int threshold);
int mouseQuickSelectionThreshold() const;
+ Qt::ColorScheme colorScheme() const;
Q_SIGNALS:
void cursorFlashTimeChanged(int cursorFlashTime);
@@ -98,6 +107,7 @@ Q_SIGNALS:
void showShortcutsInContextMenusChanged(bool);
void wheelScrollLinesChanged(int scrollLines);
void mouseQuickSelectionThresholdChanged(int threshold);
+ void colorSchemeChanged(Qt::ColorScheme colorScheme);
private:
friend class QGuiApplication;
diff --git a/src/gui/kernel/qstylehints_p.h b/src/gui/kernel/qstylehints_p.h
new file mode 100644
index 0000000000..2b3979512a
--- /dev/null
+++ b/src/gui/kernel/qstylehints_p.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSTYLEHINTS_P_H
+#define QSTYLEHINTS_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 <qpa/qplatformintegration.h>
+#include <QPalette>
+#include <private/qguiapplication_p.h>
+#include "qstylehints.h"
+
+QT_BEGIN_NAMESPACE
+
+class QStyleHintsPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QStyleHints)
+public:
+ int m_mouseDoubleClickInterval = -1;
+ int m_mousePressAndHoldInterval = -1;
+ int m_startDragDistance = -1;
+ int m_startDragTime = -1;
+ int m_keyboardInputInterval = -1;
+ int m_cursorFlashTime = -1;
+ int m_tabFocusBehavior = -1;
+ int m_uiEffects = -1;
+ int m_showShortcutsInContextMenus = -1;
+ int m_wheelScrollLines = -1;
+ int m_mouseQuickSelectionThreshold = -1;
+ int m_mouseDoubleClickDistance = -1;
+ int m_touchDoubleTapDistance = -1;
+
+ Qt::ColorScheme colorScheme() const { return m_colorScheme; }
+ void updateColorScheme(Qt::ColorScheme colorScheme);
+
+ static QStyleHintsPrivate *get(QStyleHints *q);
+
+private:
+ Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/kernel/qsurface.cpp b/src/gui/kernel/qsurface.cpp
index b8083641c2..c00cf55a09 100644
--- a/src/gui/kernel/qsurface.cpp
+++ b/src/gui/kernel/qsurface.cpp
@@ -30,13 +30,12 @@ QT_IMPL_METATYPE_EXTERN_TAGGED(QSurface*, QSurface_ptr)
\value Offscreen The surface is an instance of QOffscreenSurface.
*/
-
/*!
\enum QSurface::SurfaceType
The SurfaceType enum describes what type of surface this is.
- \value RasterSurface The surface is is composed of pixels and can be rendered to using
+ \value RasterSurface The surface is composed of pixels and can be rendered to using
a software rasterizer like Qt's raster paint engine.
\value OpenGLSurface The surface is an OpenGL compatible surface and can be used
in conjunction with QOpenGLContext.
@@ -55,7 +54,6 @@ QT_IMPL_METATYPE_EXTERN_TAGGED(QSurface*, QSurface_ptr)
surface type is only supported on Windows.
*/
-
/*!
\fn QSurfaceFormat QSurface::format() const
diff --git a/src/gui/kernel/qsurfaceformat.cpp b/src/gui/kernel/qsurfaceformat.cpp
index 9227b47907..74add6e973 100644
--- a/src/gui/kernel/qsurfaceformat.cpp
+++ b/src/gui/kernel/qsurfaceformat.cpp
@@ -717,8 +717,10 @@ void QSurfaceFormat::setColorSpace(const QColorSpace &colorSpace)
}
}
+#if QT_DEPRECATED_SINCE(6, 0)
/*!
\overload
+ \deprecated [6.0] Use setColorSpace(QColorSpace) instead.
Sets the colorspace to one of the predefined values.
@@ -737,6 +739,7 @@ void QSurfaceFormat::setColorSpace(ColorSpace colorSpace)
break;
}
}
+#endif // QT_DEPRECATED_SINCE(6, 0)
/*!
\return the color space.
@@ -762,7 +765,7 @@ Q_GLOBAL_STATIC(QSurfaceFormat, qt_default_surface_format)
question's own setFormat() function. However, it is often more convenient to
set the format for all windows once at the start of the application. It also
guarantees proper behavior in cases where shared contexts are required,
- because settings the format via this function guarantees that all contexts
+ because setting the format via this function guarantees that all contexts
and surfaces, even the ones created internally by Qt, will use the same
format.
diff --git a/src/gui/kernel/qtestsupport_gui.cpp b/src/gui/kernel/qtestsupport_gui.cpp
index f8c4631f05..869eddce49 100644
--- a/src/gui/kernel/qtestsupport_gui.cpp
+++ b/src/gui/kernel/qtestsupport_gui.cpp
@@ -18,11 +18,18 @@ QT_BEGIN_NAMESPACE
/*!
\since 5.0
- Waits for \a timeout milliseconds or until the \a window is active.
+ Returns \c true, if \a window is active within \a timeout milliseconds. Otherwise returns \c false.
- Returns \c true if \c window is active within \a timeout milliseconds, otherwise returns \c false.
+ The method is useful in tests that call QWindow::show() and rely on the window actually being
+ active (i.e. being visible and having focus) before proceeding.
- \sa qWaitForWindowExposed(), QWindow::isActive()
+ \note The method will time out and return \c false if another window prevents \a window from
+ becoming active.
+
+ \note Since focus is an exclusive property, \a window may loose its focus to another window at
+ any time - even after the method has returned \c true.
+
+ \sa qWaitForWindowExposed(), qWaitForWindowFocused(), QWindow::isActive()
*/
Q_GUI_EXPORT bool QTest::qWaitForWindowActive(QWindow *window, int timeout)
{
@@ -38,17 +45,37 @@ Q_GUI_EXPORT bool QTest::qWaitForWindowActive(QWindow *window, int timeout)
}
/*!
+ \since 6.7
+
+ Returns \c true, if \a window is the focus window within \a timeout. Otherwise returns \c false.
+
+ The method is useful in tests that call QWindow::show() and rely on the window
+ having focus (for receiving keyboard events e.g.) before proceeding.
+
+ \note The method will time out and return \c false if another window prevents \a window from
+ becoming focused.
+
+ \note Since focus is an exclusive property, \a window may loose its focus to another window at
+ any time - even after the method has returned \c true.
+
+ \sa qWaitForWindowExposed(), qWaitForWindowActive(), QGuiApplication::focusWindow()
+*/
+Q_GUI_EXPORT bool QTest::qWaitForWindowFocused(QWindow *window, QDeadlineTimer timeout)
+{
+ return QTest::qWaitFor([&]() { return qGuiApp->focusWindow() == window; }, timeout);
+}
+
+/*!
\since 5.0
- Waits for \a timeout milliseconds or until the \a window is exposed.
- Returns \c true if \c window is exposed within \a timeout milliseconds, otherwise returns \c false.
+ Returns \c true, if \a window is exposed within \a timeout milliseconds. Otherwise returns \c false.
- This is mainly useful for asynchronous systems like X11, where a window will be mapped to screen some
- time after being asked to show itself on the screen.
+ The method is useful in tests that call QWindow::show() and rely on the window actually being
+ being visible before proceeding.
- Note that a window that is mapped to screen may still not be considered exposed if the window client
- area is completely covered by other windows, or if the window is otherwise not visible. This function
- will then time out when waiting for such a window.
+ \note A window mapped to screen may still not be considered exposed, if the window client area is
+ not visible, e.g. because it is completely covered by other windows.
+ In such cases, the method will time out and return \c false.
\sa qWaitForWindowActive(), QWindow::isExposed()
*/
@@ -62,7 +89,7 @@ namespace QTest {
QTouchEventSequence::~QTouchEventSequence()
{
if (commitWhenDestroyed)
- commit();
+ QTouchEventSequence::commit();
}
QTouchEventSequence& QTouchEventSequence::press(int touchId, const QPoint &pt, QWindow *window)
{
@@ -92,17 +119,19 @@ QTouchEventSequence& QTouchEventSequence::stationary(int touchId)
return *this;
}
-void QTouchEventSequence::commit(bool processEvents)
+bool QTouchEventSequence::commit(bool processEvents)
{
if (points.isEmpty())
- return;
- QThread::msleep(1);
+ return false;
+ QThread::sleep(std::chrono::milliseconds{1});
+ bool ret = false;
if (targetWindow)
- qt_handleTouchEvent(targetWindow, device, points.values());
+ ret = qt_handleTouchEventv2(targetWindow, device, points.values());
if (processEvents)
QCoreApplication::processEvents();
previousPoints = points;
points.clear();
+ return ret;
}
QTouchEventSequence::QTouchEventSequence(QWindow *window, QPointingDevice *aDevice, bool autoCommit)
diff --git a/src/gui/kernel/qtestsupport_gui.h b/src/gui/kernel/qtestsupport_gui.h
index 2a756034ba..e5b2a88455 100644
--- a/src/gui/kernel/qtestsupport_gui.h
+++ b/src/gui/kernel/qtestsupport_gui.h
@@ -12,13 +12,18 @@ QT_BEGIN_NAMESPACE
class QWindow;
-Q_GUI_EXPORT void qt_handleTouchEvent(QWindow *w, const QPointingDevice *device,
+Q_GUI_EXPORT void qt_handleTouchEvent(QWindow *w, const QPointingDevice *device,
+ const QList<QEventPoint> &points,
+ Qt::KeyboardModifiers mods = Qt::NoModifier);
+
+Q_GUI_EXPORT bool qt_handleTouchEventv2(QWindow *w, const QPointingDevice *device,
const QList<QEventPoint> &points,
Qt::KeyboardModifiers mods = Qt::NoModifier);
namespace QTest {
[[nodiscard]] Q_GUI_EXPORT bool qWaitForWindowActive(QWindow *window, int timeout = 5000);
+[[nodiscard]] Q_GUI_EXPORT bool qWaitForWindowFocused(QWindow *widget, QDeadlineTimer timeout = std::chrono::seconds{5});
[[nodiscard]] Q_GUI_EXPORT bool qWaitForWindowExposed(QWindow *window, int timeout = 5000);
Q_GUI_EXPORT QPointingDevice * createTouchDevice(QInputDevice::DeviceType devType = QInputDevice::DeviceType::TouchScreen,
@@ -33,7 +38,7 @@ public:
QTouchEventSequence& release(int touchId, const QPoint &pt, QWindow *window = nullptr);
virtual QTouchEventSequence& stationary(int touchId);
- virtual void commit(bool processEvents = true);
+ virtual bool commit(bool processEvents = true);
protected:
QTouchEventSequence(QWindow *window, QPointingDevice *aDevice, bool autoCommit);
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp
index e06951071f..b40fd7e8e8 100644
--- a/src/gui/kernel/qwindow.cpp
+++ b/src/gui/kernel/qwindow.cpp
@@ -128,7 +128,7 @@ QWindow::QWindow(QScreen *targetScreen)
, QSurface(QSurface::Window)
{
Q_D(QWindow);
- d->init(targetScreen);
+ d->init(nullptr, targetScreen);
}
static QWindow *nonDesktopParent(QWindow *parent)
@@ -169,11 +169,11 @@ QWindow::QWindow(QWindow *parent)
\sa setParent()
*/
QWindow::QWindow(QWindowPrivate &dd, QWindow *parent)
- : QObject(dd, nonDesktopParent(parent))
+ : QObject(dd, nullptr)
, QSurface(QSurface::Window)
{
Q_D(QWindow);
- d->init();
+ d->init(nonDesktopParent(parent));
}
/*!
@@ -183,6 +183,8 @@ QWindow::~QWindow()
{
Q_D(QWindow);
d->destroy();
+ // Decouple from parent before window goes under
+ setParent(nullptr);
QGuiApplicationPrivate::window_list.removeAll(this);
if (!QGuiApplicationPrivate::is_app_closing)
QGuiApplicationPrivate::instance()->modalWindowList.removeOne(this);
@@ -206,15 +208,19 @@ QWindowPrivate::QWindowPrivate()
QWindowPrivate::~QWindowPrivate()
= default;
-void QWindowPrivate::init(QScreen *targetScreen)
+void QWindowPrivate::init(QWindow *parent, QScreen *targetScreen)
{
Q_Q(QWindow);
+ q->QObject::setParent(parent);
+
isWindow = true;
parentWindow = static_cast<QWindow *>(q->QObject::parent());
+ QScreen *connectScreen = targetScreen ? targetScreen : QGuiApplication::primaryScreen();
+
if (!parentWindow)
- connectToScreen(targetScreen ? targetScreen : QGuiApplication::primaryScreen());
+ connectToScreen(connectScreen);
// If your application aborts here, you are probably creating a QWindow
// before the screen list is populated.
@@ -224,6 +230,27 @@ void QWindowPrivate::init(QScreen *targetScreen)
QGuiApplicationPrivate::window_list.prepend(q);
requestedFormat = QSurfaceFormat::defaultFormat();
+ devicePixelRatio = connectScreen->devicePixelRatio();
+
+ QObject::connect(q, &QWindow::screenChanged, q, [q, this](QScreen *){
+ // We may have changed scaling; trigger resize event if needed,
+ // except on Windows, where we send resize events during WM_DPICHANGED
+ // event handling. FIXME: unify DPI change handling across all platforms.
+#ifndef Q_OS_WIN
+ if (q->handle()) {
+ QWindowSystemInterfacePrivate::GeometryChangeEvent gce(q, QHighDpi::fromNativePixels(q->handle()->geometry(), q));
+ QGuiApplicationPrivate::processGeometryChangeEvent(&gce);
+ }
+#else
+ Q_UNUSED(q);
+#endif
+ updateDevicePixelRatio();
+ });
+
+ if (parentWindow) {
+ QChildWindowEvent childAddedEvent(QEvent::ChildWindowAdded, q);
+ QCoreApplication::sendEvent(parentWindow, &childAddedEvent);
+ }
}
/*!
@@ -428,14 +455,14 @@ void QWindowPrivate::updateSiblingPosition(SiblingPosition position)
QObjectList &siblings = q->parent()->d_ptr->children;
- const int siblingCount = siblings.size() - 1;
+ const qsizetype siblingCount = siblings.size() - 1;
if (siblingCount == 0)
return;
- const int currentPosition = siblings.indexOf(q);
+ const qsizetype currentPosition = siblings.indexOf(q);
Q_ASSERT(currentPosition >= 0);
- const int targetPosition = position == PositionTop ? siblingCount : 0;
+ const qsizetype targetPosition = position == PositionTop ? siblingCount : 0;
if (currentPosition == targetPosition)
return;
@@ -494,7 +521,9 @@ void QWindowPrivate::setTopLevelScreen(QScreen *newScreen, bool recreate)
}
}
-void QWindowPrivate::create(bool recursive, WId nativeHandle)
+static constexpr auto kForeignWindowId = "_q_foreignWinId";
+
+void QWindowPrivate::create(bool recursive)
{
Q_Q(QWindow);
if (platformWindow)
@@ -508,6 +537,13 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle)
if (q->parent())
q->parent()->create();
+ if (platformWindow) {
+ // Creating the parent window will end up creating any child window
+ // that was already visible, via setVisible. If this applies to us,
+ // we will already have a platform window at this point.
+ return;
+ }
+
// QPlatformWindow will poll geometry() during construction below. Set the
// screen here so that high-dpi scaling will use the correct scale factor.
if (q->isTopLevel()) {
@@ -515,6 +551,8 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle)
setTopLevelScreen(screen, false);
}
+ const WId nativeHandle = q->property(kForeignWindowId).value<WId>();
+
QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
platformWindow = nativeHandle ? platformIntegration->createForeignWindow(q, nativeHandle)
: platformIntegration->createPlatformWindow(q);
@@ -550,6 +588,8 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle)
QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
QGuiApplication::sendEvent(q, &e);
+ updateDevicePixelRatio();
+
if (needsUpdate)
q->requestUpdate();
}
@@ -568,12 +608,11 @@ QRectF QWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const
}
void QWindowPrivate::setMinOrMaxSize(QSize *oldSizeMember, const QSize &size,
- std::function<void()> funcWidthChanged,
- std::function<void()> funcHeightChanged)
+ qxp::function_ref<void()> funcWidthChanged,
+ qxp::function_ref<void()> funcHeightChanged)
{
Q_Q(QWindow);
Q_ASSERT(oldSizeMember);
- Q_ASSERT(funcWidthChanged && funcHeightChanged);
const QSize adjustedSize =
size.expandedTo(QSize(0, 0)).boundedTo(QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX));
if (*oldSizeMember == adjustedSize)
@@ -593,7 +632,9 @@ void QWindowPrivate::setMinOrMaxSize(QSize *oldSizeMember, const QSize &size,
// resize window if current size is outside of min and max limits
if (minimumSize.width() <= maximumSize.width()
|| minimumSize.height() <= maximumSize.height()) {
- q->resize(q->geometry().size().expandedTo(minimumSize).boundedTo(maximumSize));
+ const QSize currentSize = q->size();
+ const QSize boundedSize = currentSize.expandedTo(minimumSize).boundedTo(maximumSize);
+ q->resize(boundedSize);
}
}
@@ -666,7 +707,7 @@ bool QWindow::isVisible() const
into an actual native surface. However, the window remains hidden until setVisible() is called.
Note that it is not usually necessary to call this function directly, as it will be implicitly
- called by show(), setVisible(), and other functions that require access to the platform
+ called by show(), setVisible(), winId(), and other functions that require access to the platform
resources.
Call destroy() to free the platform resources if necessary.
@@ -682,6 +723,9 @@ void QWindow::create()
/*!
Returns the window's platform id.
+ \note This function will cause the platform window to be created if it is not already.
+ Returns 0, if the platform window creation failed.
+
For platforms where this id might be useful, the value returned
will uniquely represent the window inside the corresponding screen.
@@ -694,6 +738,9 @@ WId QWindow::winId() const
if (!d->platformWindow)
const_cast<QWindow *>(this)->create();
+ if (!d->platformWindow)
+ return 0;
+
return d->platformWindow->winId();
}
@@ -737,6 +784,10 @@ void QWindow::setParent(QWindow *parent)
return;
}
+ QEvent parentAboutToChangeEvent(QEvent::ParentWindowAboutToChange);
+ QCoreApplication::sendEvent(this, &parentAboutToChangeEvent);
+
+ const auto previousParent = d->parentWindow;
QObject::setParent(parent);
d->parentWindow = parent;
@@ -759,6 +810,19 @@ void QWindow::setParent(QWindow *parent)
}
QGuiApplicationPrivate::updateBlockedStatus(this);
+
+ if (previousParent) {
+ QChildWindowEvent childRemovedEvent(QEvent::ChildWindowRemoved, this);
+ QCoreApplication::sendEvent(previousParent, &childRemovedEvent);
+ }
+
+ if (parent) {
+ QChildWindowEvent childAddedEvent(QEvent::ChildWindowAdded, this);
+ QCoreApplication::sendEvent(parent, &childAddedEvent);
+ }
+
+ QEvent parentChangedEvent(QEvent::ParentWindowChange);
+ QCoreApplication::sendEvent(this, &parentChangedEvent);
}
/*!
@@ -1250,6 +1314,8 @@ bool QWindow::isExposed() const
Typically active windows should appear active from a style perspective.
To get the window that currently has focus, use QGuiApplication::focusWindow().
+
+ \sa requestActivate()
*/
bool QWindow::isActive() const
{
@@ -1323,14 +1389,31 @@ Qt::ScreenOrientation QWindow::contentOrientation() const
qreal QWindow::devicePixelRatio() const
{
Q_D(const QWindow);
+ return d->devicePixelRatio;
+}
+
+/*
+ Updates the cached devicePixelRatio value by polling for a new value.
+ Sends QEvent::DevicePixelRatioChange to the window if the DPR has changed.
+ Returns true if the DPR was changed.
+*/
+bool QWindowPrivate::updateDevicePixelRatio()
+{
+ Q_Q(QWindow);
// If there is no platform window use the associated screen's devicePixelRatio,
// which typically is the primary screen and will be correct for single-display
// systems (a very common case).
- if (!d->platformWindow)
- return screen()->devicePixelRatio();
+ const qreal newDevicePixelRatio = platformWindow ?
+ platformWindow->devicePixelRatio() * QHighDpiScaling::factor(q) : q->screen()->devicePixelRatio();
+
+ if (newDevicePixelRatio == devicePixelRatio)
+ return false;
- return d->platformWindow->devicePixelRatio() * QHighDpiScaling::factor(this);
+ devicePixelRatio = newDevicePixelRatio;
+ QEvent dprChangeEvent(QEvent::DevicePixelRatioChange);
+ QGuiApplication::sendEvent(q, &dprChangeEvent);
+ return true;
}
Qt::WindowState QWindowPrivate::effectiveState(Qt::WindowStates state)
@@ -1385,8 +1468,13 @@ void QWindow::setWindowStates(Qt::WindowStates state)
if (d->platformWindow)
d->platformWindow->setWindowState(state);
+
+ auto originalEffectiveState = QWindowPrivate::effectiveState(d->windowState);
d->windowState = state;
- emit windowStateChanged(QWindowPrivate::effectiveState(d->windowState));
+ auto newEffectiveState = QWindowPrivate::effectiveState(d->windowState);
+ if (newEffectiveState != originalEffectiveState)
+ emit windowStateChanged(newEffectiveState);
+
d->updateVisibility();
}
@@ -1596,20 +1684,18 @@ void QWindow::setY(int arg)
\property QWindow::width
\brief the width of the window's geometry
*/
-void QWindow::setWidth(int arg)
+void QWindow::setWidth(int w)
{
- if (width() != arg)
- resize(arg, height());
+ resize(w, height());
}
/*!
\property QWindow::height
\brief the height of the window's geometry
*/
-void QWindow::setHeight(int arg)
+void QWindow::setHeight(int h)
{
- if (height() != arg)
- resize(width(), arg);
+ resize(width(), h);
}
/*!
@@ -1869,6 +1955,10 @@ void QWindow::setFramePosition(const QPoint &point)
For interactively moving windows, see startSystemMove(). For interactively
resizing windows, see startSystemResize().
+ \note Not all windowing systems support setting or querying top level window positions.
+ On such a system, programmatically moving windows may not have any effect, and artificial
+ values may be returned for the current positions, such as \c QPoint(0, 0).
+
\sa position(), startSystemMove()
*/
void QWindow::setPosition(const QPoint &pt)
@@ -1892,6 +1982,10 @@ void QWindow::setPosition(int posx, int posy)
\fn QPoint QWindow::position() const
\brief Returns the position of the window on the desktop excluding any window frame
+ \note Not all windowing systems support setting or querying top level window positions.
+ On such a system, programmatically moving windows may not have any effect, and artificial
+ values may be returned for the current positions, such as \c QPoint(0, 0).
+
\sa setPosition()
*/
@@ -1923,12 +2017,16 @@ void QWindow::resize(int w, int h)
void QWindow::resize(const QSize &newSize)
{
Q_D(QWindow);
+
+ const QSize oldSize = size();
+ if (newSize == oldSize)
+ return;
+
d->positionPolicy = QWindowPrivate::WindowFrameExclusive;
if (d->platformWindow) {
d->platformWindow->setGeometry(
QHighDpi::toNativeWindowGeometry(QRect(position(), newSize), this));
} else {
- const QSize oldSize = d->geometry.size();
d->geometry.setSize(newSize);
if (newSize.width() != oldSize.width())
emit widthChanged(newSize.width());
@@ -1965,6 +2063,16 @@ void QWindowPrivate::destroy()
QObject *object = childrenWindows.at(i);
if (object->isWindowType()) {
QWindow *w = static_cast<QWindow*>(object);
+ auto *childPlatformWindow = w->handle();
+ if (!childPlatformWindow)
+ continue;
+
+ // Decouple the foreign window from this window,
+ // so that destroying our native handle doesn't
+ // bring down the foreign window as well.
+ if (childPlatformWindow->isForeignWindow())
+ childPlatformWindow->setParent(nullptr);
+
qt_window_private(w)->destroy();
}
}
@@ -2137,20 +2245,26 @@ QObject *QWindow::focusObject() const
/*!
Shows the window.
- This is equivalent to calling showFullScreen(), showMaximized(), or showNormal(),
+ For child windows, this is equivalent to calling showNormal().
+ Otherwise, it is equivalent to calling showFullScreen(), showMaximized(), or showNormal(),
depending on the platform's default behavior for the window type and flags.
\sa showFullScreen(), showMaximized(), showNormal(), hide(), QStyleHints::showIsFullScreen(), flags()
*/
void QWindow::show()
{
- Qt::WindowState defaultState = QGuiApplicationPrivate::platformIntegration()->defaultWindowState(d_func()->windowFlags);
- if (defaultState == Qt::WindowFullScreen)
- showFullScreen();
- else if (defaultState == Qt::WindowMaximized)
- showMaximized();
- else
+ if (parent()) {
showNormal();
+ } else {
+ const auto *platformIntegration = QGuiApplicationPrivate::platformIntegration();
+ Qt::WindowState defaultState = platformIntegration->defaultWindowState(d_func()->windowFlags);
+ if (defaultState == Qt::WindowFullScreen)
+ showFullScreen();
+ else if (defaultState == Qt::WindowMaximized)
+ showMaximized();
+ else
+ showNormal();
+ }
}
/*!
@@ -2502,13 +2616,6 @@ bool QWindow::event(QEvent *ev)
setIcon(icon());
break;
- case QEvent::WindowStateChange: {
- Q_D(QWindow);
- emit windowStateChanged(QWindowPrivate::effectiveState(d->windowState));
- d->updateVisibility();
- break;
- }
-
#if QT_CONFIG(tabletevent)
case QEvent::TabletPress:
case QEvent::TabletMove:
@@ -2568,11 +2675,13 @@ bool QWindow::event(QEvent *ev)
/*!
Schedules a QEvent::UpdateRequest event to be delivered to this window.
- The event is delivered in sync with the display vsync on platforms
- where this is possible. Otherwise, the event is delivered after a
- delay of 5 ms. The additional time is there to give the event loop
- a bit of idle time to gather system events, and can be overridden
- using the QT_QPA_UPDATE_IDLE_TIME environment variable.
+ The event is delivered in sync with the display vsync on platforms where
+ this is possible. Otherwise, the event is delivered after a delay of at
+ most 5 ms. If the window's associated screen reports a
+ \l{QScreen::refreshRate()}{refresh rate} higher than 60 Hz, the interval is
+ scaled down to a value smaller than 5. The additional time is there to give
+ the event loop a bit of idle time to gather system events, and can be
+ overridden using the QT_QPA_UPDATE_IDLE_TIME environment variable.
When driving animations, this function should be called once after drawing
has completed. Calling this function multiple times will result in a single
@@ -2761,7 +2870,12 @@ QPointF QWindow::mapToGlobal(const QPointF &pos) const
// Map the position (and the window's global position) to native coordinates, perform
// the addition, and then map back to device independent coordinates.
QPointF nativeLocalPos = QHighDpi::toNativeLocalPosition(pos, this);
- QPointF nativeWindowGlobalPos = QHighDpi::toNativeGlobalPosition(QPointF(d->globalPosition()), this);
+ // Get the native window position directly from the platform window
+ // if available (it can be null if the window hasn't been shown yet),
+ // or fall back to scaling the QWindow position.
+ QPointF nativeWindowGlobalPos = d->platformWindow
+ ? d->platformWindow->mapToGlobal(QPoint(0,0)).toPointF()
+ : QHighDpi::toNativeGlobalPosition(QPointF(d->globalPosition()), this);
QPointF nativeGlobalPos = nativeLocalPos + nativeWindowGlobalPos;
QPointF deviceIndependentGlobalPos = QHighDpi::fromNativeGlobalPosition(nativeGlobalPos, this);
return deviceIndependentGlobalPos;
@@ -2799,7 +2913,12 @@ QPointF QWindow::mapFromGlobal(const QPointF &pos) const
// Calculate local position in the native coordinate system. (See comment for the
// corresponding mapToGlobal() code above).
QPointF nativeGlobalPos = QHighDpi::toNativeGlobalPosition(pos, this);
- QPointF nativeWindowGlobalPos = QHighDpi::toNativeGlobalPosition(QPointF(d->globalPosition()), this);
+ // Get the native window position directly from the platform window
+ // if available (it can be null if the window hasn't been shown yet),
+ // or fall back to scaling the QWindow position.
+ QPointF nativeWindowGlobalPos = d->platformWindow
+ ? d->platformWindow->mapToGlobal(QPoint(0,0)).toPointF()
+ : QHighDpi::toNativeGlobalPosition(QPointF(d->globalPosition()), this);
QPointF nativeLocalPos = nativeGlobalPos - nativeWindowGlobalPos;
QPointF deviceIndependentLocalPos = QHighDpi::fromNativeLocalPosition(nativeLocalPos, this);
return deviceIndependentLocalPos;
@@ -2881,7 +3000,11 @@ QWindow *QWindow::fromWinId(WId id)
}
QWindow *window = new QWindow;
- qt_window_private(window)->create(false, id);
+
+ // Persist the winId in a private property so that we
+ // can recreate the window after being destroyed.
+ window->setProperty(kForeignWindowId, id);
+ window->create();
if (!window->handle()) {
delete window;
@@ -3030,6 +3153,14 @@ void *QWindow::resolveInterface(const char *name, int revision) const
QT_NATIVE_INTERFACE_RETURN_IF(QCocoaWindow, platformWindow);
#endif
+#if QT_CONFIG(wayland)
+ QT_NATIVE_INTERFACE_RETURN_IF(QWaylandWindow, platformWindow);
+#endif
+
+#if defined(Q_OS_WASM)
+ QT_NATIVE_INTERFACE_RETURN_IF(QWasmWindow, platformWindow);
+#endif
+
return nullptr;
}
@@ -3072,7 +3203,7 @@ QDebug operator<<(QDebug debug, const QWindow *window)
}
#endif // !QT_NO_DEBUG_STREAM
-#if QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(vulkan) || defined(Q_QDOC)
/*!
Associates this window with the specified Vulkan \a instance.
diff --git a/src/gui/kernel/qwindow.h b/src/gui/kernel/qwindow.h
index 12d2af8319..a5ec47708a 100644
--- a/src/gui/kernel/qwindow.h
+++ b/src/gui/kernel/qwindow.h
@@ -55,7 +55,7 @@ class QWindowContainer;
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
#endif
-#if QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(vulkan) || defined(Q_QDOC)
class QVulkanInstance;
#endif
@@ -248,7 +248,7 @@ public:
static QWindow *fromWinId(WId id);
-#if QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(vulkan) || defined(Q_QDOC)
void setVulkanInstance(QVulkanInstance *instance);
QVulkanInstance *vulkanInstance() const;
#endif
diff --git a/src/gui/kernel/qwindow_p.h b/src/gui/kernel/qwindow_p.h
index 3212a1d6fa..a9716847a1 100644
--- a/src/gui/kernel/qwindow_p.h
+++ b/src/gui/kernel/qwindow_p.h
@@ -22,7 +22,11 @@
#include <QtCore/private/qobject_p.h>
#include <QtCore/qelapsedtimer.h>
-#include <QtGui/QIcon>
+#include <QtCore/qxpfunctional.h>
+#include <QtGui/qicon.h>
+#include <QtGui/qpalette.h>
+
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -40,7 +44,7 @@ public:
QWindowPrivate();
~QWindowPrivate() override;
- void init(QScreen *targetScreen = nullptr);
+ void init(QWindow *parent, QScreen *targetScreen = nullptr);
#ifndef QT_NO_CURSOR
void setCursor(const QCursor *c = nullptr);
@@ -52,6 +56,7 @@ public:
QWindow *topLevelWindow(QWindow::AncestorMode mode = QWindow::IncludeTransients) const;
virtual QWindow *eventReceiver() { Q_Q(QWindow); return q; }
+ virtual QPalette windowPalette() const { return QPalette(); }
virtual void setVisible(bool visible);
void updateVisibility();
@@ -61,7 +66,7 @@ public:
void updateSiblingPosition(SiblingPosition);
bool windowRecreationRequired(QScreen *newScreen) const;
- void create(bool recursive, WId nativeHandle = 0);
+ void create(bool recursive);
void destroy();
void setTopLevelScreen(QScreen *newScreen, bool recreate);
void connectToScreen(QScreen *topLevelScreen);
@@ -74,8 +79,8 @@ public:
virtual QRectF closestAcceptableGeometry(const QRectF &rect) const;
void setMinOrMaxSize(QSize *oldSizeMember, const QSize &size,
- std::function<void()> funcWidthChanged,
- std::function<void()> funcHeightChanged);
+ qxp::function_ref<void()> funcWidthChanged,
+ qxp::function_ref<void()> funcHeightChanged);
virtual void processSafeAreaMarginsChanged() {}
@@ -86,6 +91,8 @@ public:
void setAutomaticPositionAndResizeEnabled(bool a)
{ positionAutomatic = resizeAutomatic = a; }
+ bool updateDevicePixelRatio();
+
static QWindowPrivate *get(QWindow *window) { return window->d_func(); }
static Qt::WindowState effectiveState(Qt::WindowStates);
@@ -103,6 +110,7 @@ public:
QString windowFilePath;
QIcon windowIcon;
QRect geometry;
+ qreal devicePixelRatio = 1.0;
Qt::WindowStates windowState = Qt::WindowNoState;
QWindow::Visibility visibility = QWindow::Hidden;
bool resizeEventPending = true;
@@ -137,7 +145,6 @@ public:
bool hasCursor = false;
#endif
- bool compositing = false;
QElapsedTimer lastComposeTime;
#if QT_CONFIG(vulkan)
diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp
index ef43e50c61..1875594300 100644
--- a/src/gui/kernel/qwindowsysteminterface.cpp
+++ b/src/gui/kernel/qwindowsysteminterface.cpp
@@ -26,12 +26,12 @@ using namespace Qt::StringLiterals;
Q_LOGGING_CATEGORY(lcQpaInputDevices, "qt.qpa.input.devices")
-QElapsedTimer QWindowSystemInterfacePrivate::eventTime;
+Q_CONSTINIT QElapsedTimer QWindowSystemInterfacePrivate::eventTime;
bool QWindowSystemInterfacePrivate::synchronousWindowSystemEvents = false;
bool QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse = true;
QWaitCondition QWindowSystemInterfacePrivate::eventsFlushed;
-QMutex QWindowSystemInterfacePrivate::flushEventMutex;
-QAtomicInt QWindowSystemInterfacePrivate::eventAccepted;
+Q_CONSTINIT QMutex QWindowSystemInterfacePrivate::flushEventMutex;
+Q_CONSTINIT QAtomicInt QWindowSystemInterfacePrivate::eventAccepted;
QWindowSystemEventHandler *QWindowSystemInterfacePrivate::eventHandler;
QWindowSystemInterfacePrivate::WindowSystemEventList QWindowSystemInterfacePrivate::windowSystemEventQueue;
@@ -98,7 +98,12 @@ bool QWindowSystemHelper<QWindowSystemInterface::SynchronousDelivery>::handleEve
if (QThread::currentThread() == QGuiApplication::instance()->thread()) {
EventType event(args...);
// Process the event immediately on the Gui thread and return the accepted state
- QGuiApplicationPrivate::processWindowSystemEvent(&event);
+ if (QWindowSystemInterfacePrivate::eventHandler) {
+ if (!QWindowSystemInterfacePrivate::eventHandler->sendEvent(&event))
+ return false;
+ } else {
+ QGuiApplicationPrivate::processWindowSystemEvent(&event);
+ }
return event.eventAccepted;
} else {
// Post the event on the Qt main thread queue and flush the queue.
@@ -133,7 +138,7 @@ static bool handleWindowSystemEvent(Args ...args)
return QWindowSystemHelper<Delivery>::template handleEvent<EventType>(args...);
}
-int QWindowSystemInterfacePrivate::windowSystemEventsQueued()
+qsizetype QWindowSystemInterfacePrivate::windowSystemEventsQueued()
{
return windowSystemEventQueue.count();
}
@@ -235,9 +240,9 @@ void QWindowSystemInterface::handleEnterLeaveEvent(QWindow *enter, QWindow *leav
handleEnterEvent(enter, local, global);
}
-QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowActivated, QWindow *window, Qt::FocusReason r)
+QT_DEFINE_QPA_EVENT_HANDLER(void, handleFocusWindowChanged, QWindow *window, Qt::FocusReason r)
{
- handleWindowSystemEvent<QWindowSystemInterfacePrivate::ActivatedWindowEvent, Delivery>(window, r);
+ handleWindowSystemEvent<QWindowSystemInterfacePrivate::FocusWindowEvent, Delivery>(window, r);
}
QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowStateChanged, QWindow *window, Qt::WindowStates newState, int oldState)
@@ -254,6 +259,12 @@ QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowScreenChanged, QWindow *window, QS
handleWindowSystemEvent<QWindowSystemInterfacePrivate::WindowScreenChangedEvent, Delivery>(window, screen);
}
+QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowDevicePixelRatioChanged, QWindow *window)
+{
+ handleWindowSystemEvent<QWindowSystemInterfacePrivate::WindowDevicePixelRatioChangedEvent, Delivery>(window);
+}
+
+
QT_DEFINE_QPA_EVENT_HANDLER(void, handleSafeAreaMarginsChanged, QWindow *window)
{
handleWindowSystemEvent<QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent, Delivery>(window);
@@ -378,61 +389,34 @@ QT_DEFINE_QPA_EVENT_HANDLER(bool, handleMouseEvent, QWindow *window, ulong times
Qt::MouseButton button, QEvent::Type type, Qt::KeyboardModifiers mods,
Qt::MouseEventSource source)
{
- Q_ASSERT_X(type != QEvent::MouseButtonDblClick && type != QEvent::NonClientAreaMouseButtonDblClick,
- "QWindowSystemInterface::handleMouseEvent",
- "QTBUG-71263: Native double clicks are not implemented.");
- auto localPos = QHighDpi::fromNativeLocalPosition(local, window);
- auto globalPos = QHighDpi::fromNativeGlobalPosition(global, window);
- return handleWindowSystemEvent<QWindowSystemInterfacePrivate::MouseEvent, Delivery>(window,
- timestamp, localPos, globalPos, state, mods, button, type, source, false, device);
-}
-
-bool QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *window,
- const QPointF &local, const QPointF &global,
- Qt::MouseButtons state,
- Qt::MouseButton button, QEvent::Type type,
- Qt::KeyboardModifiers mods,
- Qt::MouseEventSource source)
-{
- const unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed();
- return handleFrameStrutMouseEvent(window, time, local, global, state, button, type, mods, source);
-}
+ bool isNonClientArea = {};
-bool QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *window, const QPointingDevice *device,
- const QPointF &local, const QPointF &global,
- Qt::MouseButtons state,
- Qt::MouseButton button, QEvent::Type type,
- Qt::KeyboardModifiers mods,
- Qt::MouseEventSource source)
-{
- const unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed();
- return handleFrameStrutMouseEvent(window, time, device, local, global, state, button, type, mods, source);
-}
-
-bool QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *window, ulong timestamp,
- const QPointF &local, const QPointF &global,
- Qt::MouseButtons state,
- Qt::MouseButton button, QEvent::Type type,
- Qt::KeyboardModifiers mods,
- Qt::MouseEventSource source)
-{
- return handleFrameStrutMouseEvent(window, timestamp, QPointingDevice::primaryPointingDevice(),
- local, global, state, button, type, mods, source);
-}
+ switch (type) {
+ case QEvent::MouseButtonDblClick:
+ case QEvent::NonClientAreaMouseButtonDblClick:
+ Q_ASSERT_X(false, "QWindowSystemInterface::handleMouseEvent",
+ "QTBUG-71263: Native double clicks are not implemented.");
+ return false;
+ case QEvent::MouseMove:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ isNonClientArea = false;
+ break;
+ case QEvent::NonClientAreaMouseMove:
+ case QEvent::NonClientAreaMouseButtonPress:
+ case QEvent::NonClientAreaMouseButtonRelease:
+ isNonClientArea = true;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
-bool QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *window, ulong timestamp, const QPointingDevice *device,
- const QPointF &local, const QPointF &global,
- Qt::MouseButtons state,
- Qt::MouseButton button, QEvent::Type type,
- Qt::KeyboardModifiers mods,
- Qt::MouseEventSource source)
-{
auto localPos = QHighDpi::fromNativeLocalPosition(local, window);
auto globalPos = QHighDpi::fromNativeGlobalPosition(global, window);
- return handleWindowSystemEvent<QWindowSystemInterfacePrivate::MouseEvent>(window,
- timestamp, localPos, globalPos, state, mods, button, type, source, true, device);
+ return handleWindowSystemEvent<QWindowSystemInterfacePrivate::MouseEvent, Delivery>(window,
+ timestamp, localPos, globalPos, state, mods, button, type, source, isNonClientArea, device);
}
bool QWindowSystemInterface::handleShortcutEvent(QWindow *window, ulong timestamp, int keyCode, Qt::KeyboardModifiers modifiers, quint32 nativeScanCode,
@@ -485,11 +469,6 @@ QT_DEFINE_QPA_EVENT_HANDLER(bool, handleKeyEvent, QWindow *window, QEvent::Type
QT_DEFINE_QPA_EVENT_HANDLER(bool, handleKeyEvent, QWindow *window, ulong timestamp, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text, bool autorep, ushort count)
{
-#if defined(Q_OS_MACOS)
- if (t == QEvent::KeyPress && QWindowSystemInterface::handleShortcutEvent(window, timestamp, k, mods, 0, 0, 0, text, autorep, count))
- return true;
-#endif
-
return handleWindowSystemEvent<QWindowSystemInterfacePrivate::KeyEvent, Delivery>(window,
timestamp, t, k, mods, text, autorep, count);
}
@@ -498,11 +477,11 @@ bool QWindowSystemInterface::handleExtendedKeyEvent(QWindow *window, QEvent::Typ
quint32 nativeScanCode, quint32 nativeVirtualKey,
quint32 nativeModifiers,
const QString& text, bool autorep,
- ushort count, bool tryShortcutOverride)
+ ushort count)
{
unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed();
return handleExtendedKeyEvent(window, time, type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers,
- text, autorep, count, tryShortcutOverride);
+ text, autorep, count);
}
bool QWindowSystemInterface::handleExtendedKeyEvent(QWindow *window, ulong timestamp, QEvent::Type type, int key,
@@ -510,17 +489,8 @@ bool QWindowSystemInterface::handleExtendedKeyEvent(QWindow *window, ulong times
quint32 nativeScanCode, quint32 nativeVirtualKey,
quint32 nativeModifiers,
const QString& text, bool autorep,
- ushort count, bool tryShortcutOverride)
+ ushort count)
{
-#if defined(Q_OS_MACOS)
- if (tryShortcutOverride && type == QEvent::KeyPress && QWindowSystemInterface::handleShortcutEvent(window,
- timestamp, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count)) {
- return true;
- }
-#else
- Q_UNUSED(tryShortcutOverride);
-#endif
-
return handleWindowSystemEvent<QWindowSystemInterfacePrivate::KeyEvent>(window,
timestamp, type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
}
@@ -644,7 +614,7 @@ QList<QEventPoint>
QList<QEventPoint> touchPoints;
QEventPoint::States states;
- touchPoints.reserve(points.count());
+ touchPoints.reserve(points.size());
QList<QWindowSystemInterface::TouchPoint>::const_iterator point = points.constBegin();
QList<QWindowSystemInterface::TouchPoint>::const_iterator end = points.constEnd();
while (point != end) {
@@ -741,9 +711,9 @@ QT_DEFINE_QPA_EVENT_HANDLER(bool, handleTouchCancelEvent, QWindow *window, ulong
The screen should be deleted by calling QWindowSystemInterface::handleScreenRemoved().
*/
-void QWindowSystemInterface::handleScreenAdded(QPlatformScreen *ps, bool isPrimary)
+void QWindowSystemInterface::handleScreenAdded(QPlatformScreen *platformScreen, bool isPrimary)
{
- QScreen *screen = new QScreen(ps);
+ QScreen *screen = new QScreen(platformScreen);
if (isPrimary)
QGuiApplicationPrivate::screen_list.prepend(screen);
@@ -752,7 +722,7 @@ void QWindowSystemInterface::handleScreenAdded(QPlatformScreen *ps, bool isPrima
QGuiApplicationPrivate::resetCachedDevicePixelRatio();
QHighDpiScaling::updateHighDpiScaling();
- screen->d_func()->updateHighDpi();
+ screen->d_func()->updateGeometry();
emit qGuiApp->screenAdded(screen);
@@ -770,9 +740,45 @@ void QWindowSystemInterface::handleScreenAdded(QPlatformScreen *ps, bool isPrima
*/
void QWindowSystemInterface::handleScreenRemoved(QPlatformScreen *platformScreen)
{
- // Important to keep this order since the QSceen doesn't own the platform screen.
- // The QScreen destructor will take care changing the primary screen, so no need here.
- delete platformScreen->screen();
+ QScreen *screen = platformScreen->screen();
+
+ // Remove screen
+ const bool wasPrimary = QGuiApplication::primaryScreen() == screen;
+ QGuiApplicationPrivate::screen_list.removeOne(screen);
+ QGuiApplicationPrivate::resetCachedDevicePixelRatio();
+
+ if (qGuiApp) {
+ QScreen *newPrimaryScreen = QGuiApplication::primaryScreen();
+ if (wasPrimary && newPrimaryScreen)
+ emit qGuiApp->primaryScreenChanged(newPrimaryScreen);
+
+ // Allow clients to manage windows that are affected by the screen going
+ // away, before we fall back to moving them to the primary screen.
+ emit qApp->screenRemoved(screen);
+
+ if (!QGuiApplication::closingDown()) {
+ bool movingFromVirtualSibling = newPrimaryScreen
+ && newPrimaryScreen->handle()->virtualSiblings().contains(platformScreen);
+
+ // Move any leftover windows to the primary screen
+ const auto allWindows = QGuiApplication::allWindows();
+ for (QWindow *window : allWindows) {
+ if (!window->isTopLevel() || window->screen() != screen)
+ continue;
+
+ const bool wasVisible = window->isVisible();
+ window->setScreen(newPrimaryScreen);
+
+ // Re-show window if moved from a virtual sibling screen. Otherwise
+ // leave it up to the application developer to show the window.
+ if (movingFromVirtualSibling)
+ window->setVisible(wasVisible);
+ }
+ }
+ }
+
+ // Important to keep this order since the QSceen doesn't own the platform screen
+ delete screen;
delete platformScreen;
}
@@ -785,7 +791,7 @@ void QWindowSystemInterface::handleScreenRemoved(QPlatformScreen *platformScreen
void QWindowSystemInterface::handlePrimaryScreenChanged(QPlatformScreen *newPrimary)
{
QScreen *newPrimaryScreen = newPrimary->screen();
- int indexOfScreen = QGuiApplicationPrivate::screen_list.indexOf(newPrimaryScreen);
+ qsizetype indexOfScreen = QGuiApplicationPrivate::screen_list.indexOf(newPrimaryScreen);
Q_ASSERT(indexOfScreen >= 0);
if (indexOfScreen == 0)
return;
@@ -808,6 +814,11 @@ void QWindowSystemInterface::handleScreenGeometryChange(QScreen *screen, const Q
void QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(QScreen *screen, qreal dpiX, qreal dpiY)
{
+ // Keep QHighDpiScaling::m_active in sync with platform screen state, in
+ // order to make scaling calls made during DPI change use the new state.
+ // FIXME: Remove when QHighDpiScaling::m_active has been removed.
+ QHighDpiScaling::updateHighDpiScaling();
+
const QDpi effectiveDpi = QPlatformScreen::overrideDpi(QDpi{dpiX, dpiY});
handleWindowSystemEvent<QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInchEvent>(screen,
effectiveDpi.first, effectiveDpi.second);
@@ -1052,7 +1063,7 @@ Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QWindowSystemInterface::TouchPo
*/
bool QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
{
- const int count = QWindowSystemInterfacePrivate::windowSystemEventQueue.count();
+ const qsizetype count = QWindowSystemInterfacePrivate::windowSystemEventQueue.count();
if (!count)
return false;
if (!QGuiApplication::instance()) {
@@ -1147,8 +1158,18 @@ Q_GUI_EXPORT void qt_handleMouseEvent(QWindow *window, const QPointF &local, con
timestamp, nativeLocal, nativeGlobal, state, button, type, mods);
}
+/*
+ Used by QTest::simulateEvent() to synthesize key events during testing
+*/
Q_GUI_EXPORT void qt_handleKeyEvent(QWindow *window, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1)
{
+#if defined(Q_OS_MACOS)
+ // FIXME: Move into QTest::simulateEvent() and align with QGuiApplicationPrivate::processKeyEvent()
+ auto timestamp = QWindowSystemInterfacePrivate::eventTime.elapsed();
+ if (t == QEvent::KeyPress && QWindowSystemInterface::handleShortcutEvent(window, timestamp, k, mods, 0, 0, 0, text, autorep, count))
+ return;
+#endif
+
QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(window, t, k, mods, text, autorep, count);
}
@@ -1189,10 +1210,17 @@ Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int
#endif
}
+Q_GUI_EXPORT void qt_handleWheelEvent(QWindow *window, const QPointF &local, const QPointF &global,
+ QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods,
+ Qt::ScrollPhase phase)
+{
+ QWindowSystemInterface::handleWheelEvent(window, local, global, pixelDelta, angleDelta, mods, phase);
+}
+
namespace QTest
{
- Q_GUI_EXPORT QPointingDevice * createTouchDevice(QInputDevice::DeviceType devType = QInputDevice::DeviceType::TouchScreen,
- QInputDevice::Capabilities caps = QInputDevice::Capability::Position)
+ Q_GUI_EXPORT QPointingDevice * createTouchDevice(QInputDevice::DeviceType devType,
+ QInputDevice::Capabilities caps)
{
static qint64 nextId = 0x100000000;
QPointingDevice *ret = new QPointingDevice("test touch device"_L1, nextId++,
@@ -1203,13 +1231,19 @@ namespace QTest
}
}
-Q_GUI_EXPORT void qt_handleTouchEvent(QWindow *window, const QPointingDevice *device,
+Q_GUI_EXPORT bool qt_handleTouchEventv2(QWindow *window, const QPointingDevice *device,
const QList<QEventPoint> &points,
- Qt::KeyboardModifiers mods = Qt::NoModifier)
+ Qt::KeyboardModifiers mods)
{
- QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(window, device,
+ return QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(window, device,
QWindowSystemInterfacePrivate::toNativeTouchPoints(points, window), mods);
}
+Q_GUI_EXPORT void qt_handleTouchEvent(QWindow *window, const QPointingDevice *device,
+ const QList<QEventPoint> &points,
+ Qt::KeyboardModifiers mods)
+{
+ qt_handleTouchEventv2(window, device, points, mods);
+}
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qwindowsysteminterface.h b/src/gui/kernel/qwindowsysteminterface.h
index bdbf8b4839..4fc61a475d 100644
--- a/src/gui/kernel/qwindowsysteminterface.h
+++ b/src/gui/kernel/qwindowsysteminterface.h
@@ -64,31 +64,6 @@ public:
Qt::KeyboardModifiers mods = Qt::NoModifier,
Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
- static bool handleFrameStrutMouseEvent(QWindow *window, const QPointF &local,
- const QPointF &global, Qt::MouseButtons state,
- Qt::MouseButton button, QEvent::Type type,
- Qt::KeyboardModifiers mods = Qt::NoModifier,
- Qt::MouseEventSource source =
- Qt::MouseEventNotSynthesized);
- static bool handleFrameStrutMouseEvent(QWindow *window, const QPointingDevice *device,
- const QPointF &local, const QPointF &global,
- Qt::MouseButtons state,
- Qt::MouseButton button, QEvent::Type type,
- Qt::KeyboardModifiers mods = Qt::NoModifier,
- Qt::MouseEventSource source =
- Qt::MouseEventNotSynthesized);
- static bool handleFrameStrutMouseEvent(QWindow *window, ulong timestamp, const QPointF &local,
- const QPointF &global, Qt::MouseButtons state,
- Qt::MouseButton button, QEvent::Type type,
- Qt::KeyboardModifiers mods = Qt::NoModifier,
- Qt::MouseEventSource source =
- Qt::MouseEventNotSynthesized);
- static bool handleFrameStrutMouseEvent(QWindow *window, ulong timestamp, const QPointingDevice *device,
- const QPointF &local, const QPointF &global, Qt::MouseButtons state,
- Qt::MouseButton button, QEvent::Type type,
- Qt::KeyboardModifiers mods = Qt::NoModifier,
- Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
-
static bool handleShortcutEvent(QWindow *window, ulong timestamp, int k, Qt::KeyboardModifiers mods, quint32 nativeScanCode,
quint32 nativeVirtualKey, quint32 nativeModifiers, const QString & text = QString(), bool autorep = false, ushort count = 1);
@@ -101,18 +76,18 @@ public:
quint32 nativeScanCode, quint32 nativeVirtualKey,
quint32 nativeModifiers,
const QString& text = QString(), bool autorep = false,
- ushort count = 1, bool tryShortcutOverride = true);
+ ushort count = 1);
static bool handleExtendedKeyEvent(QWindow *window, ulong timestamp, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers,
quint32 nativeScanCode, quint32 nativeVirtualKey,
quint32 nativeModifiers,
const QString& text = QString(), bool autorep = false,
- ushort count = 1, bool tryShortcutOverride = true);
+ ushort count = 1);
static bool handleExtendedKeyEvent(QWindow *window, ulong timestamp, const QInputDevice *device,
QEvent::Type type, int key, Qt::KeyboardModifiers modifiers,
quint32 nativeScanCode, quint32 nativeVirtualKey,
quint32 nativeModifiers,
const QString& text = QString(), bool autorep = false,
- ushort count = 1, bool tryShortcutOverride = true);
+ ushort count = 1);
static bool handleWheelEvent(QWindow *window, const QPointF &local, const QPointF &global,
QPoint pixelDelta, QPoint angleDelta,
Qt::KeyboardModifiers mods = Qt::NoModifier,
@@ -182,8 +157,9 @@ public:
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static void handleLeaveEvent(QWindow *window);
static void handleEnterLeaveEvent(QWindow *enter, QWindow *leave, const QPointF &local = QPointF(), const QPointF& global = QPointF());
+
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
- static void handleWindowActivated(QWindow *window, Qt::FocusReason r = Qt::OtherFocusReason);
+ static void handleFocusWindowChanged(QWindow *window, Qt::FocusReason r = Qt::OtherFocusReason);
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static void handleWindowStateChanged(QWindow *window, Qt::WindowStates newState, int oldState = -1);
@@ -191,6 +167,9 @@ public:
static void handleWindowScreenChanged(QWindow *window, QScreen *newScreen);
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
+ static void handleWindowDevicePixelRatioChanged(QWindow *window);
+
+ template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static void handleSafeAreaMarginsChanged(QWindow *window);
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
@@ -252,6 +231,8 @@ public:
Qt::MouseButtons buttons = {}, int xTilt = 0, int yTilt = 0,
qreal tangentialPressure = 0, qreal rotation = 0, int z = 0,
Qt::KeyboardModifiers modifiers = Qt::NoModifier);
+
+ // The following 4 functions are deprecated (QTBUG-114560)
static bool handleTabletEnterProximityEvent(ulong timestamp, int deviceType, int pointerType, qint64 uid);
static void handleTabletEnterProximityEvent(int deviceType, int pointerType, qint64 uid);
static bool handleTabletLeaveProximityEvent(ulong timestamp, int deviceType, int pointerType, qint64 uid);
diff --git a/src/gui/kernel/qwindowsysteminterface_p.h b/src/gui/kernel/qwindowsysteminterface_p.h
index 58f786b153..51ab58fc99 100644
--- a/src/gui/kernel/qwindowsysteminterface_p.h
+++ b/src/gui/kernel/qwindowsysteminterface_p.h
@@ -40,7 +40,7 @@ public:
GeometryChange = 0x02,
Enter = UserInputEvent | 0x03,
Leave = UserInputEvent | 0x04,
- ActivatedWindow = 0x05,
+ FocusWindow = 0x05,
WindowStateChanged = 0x06,
Mouse = UserInputEvent | 0x07,
Wheel = UserInputEvent | 0x09,
@@ -68,7 +68,8 @@ public:
WindowScreenChanged = 0x21,
SafeAreaMarginsChanged = 0x22,
ApplicationTermination = 0x23,
- Paint = 0x24
+ Paint = 0x24,
+ WindowDevicePixelRatioChanged = 0x25,
};
class WindowSystemEvent {
@@ -124,12 +125,12 @@ public:
QPointer<QWindow> leave;
};
- class ActivatedWindowEvent : public WindowSystemEvent {
+ class FocusWindowEvent : public WindowSystemEvent {
public:
- explicit ActivatedWindowEvent(QWindow *activatedWindow, Qt::FocusReason r)
- : WindowSystemEvent(ActivatedWindow), activated(activatedWindow), reason(r)
+ explicit FocusWindowEvent(QWindow *focusedWindow, Qt::FocusReason r)
+ : WindowSystemEvent(FocusWindow), focused(focusedWindow), reason(r)
{ }
- QPointer<QWindow> activated;
+ QPointer<QWindow> focused;
Qt::FocusReason reason;
};
@@ -154,6 +155,15 @@ public:
QPointer<QScreen> screen;
};
+ class WindowDevicePixelRatioChangedEvent : public WindowSystemEvent {
+ public:
+ WindowDevicePixelRatioChangedEvent(QWindow *w)
+ : WindowSystemEvent(WindowDevicePixelRatioChanged), window(w)
+ { }
+
+ QPointer<QWindow> window;
+ };
+
class SafeAreaMarginsChangedEvent : public WindowSystemEvent {
public:
SafeAreaMarginsChangedEvent(QWindow *w)
@@ -463,8 +473,8 @@ public:
}
void append(WindowSystemEvent *e)
{ const QMutexLocker locker(&mutex); impl.append(e); }
- int count() const
- { const QMutexLocker locker(&mutex); return impl.count(); }
+ qsizetype count() const
+ { const QMutexLocker locker(&mutex); return impl.size(); }
WindowSystemEvent *peekAtFirstOfType(EventType t) const
{
const QMutexLocker locker(&mutex);
@@ -490,7 +500,7 @@ public:
static WindowSystemEventList windowSystemEventQueue;
- static int windowSystemEventsQueued();
+ static qsizetype windowSystemEventsQueued();
static bool nonUserInputEventsQueued();
static WindowSystemEvent *getWindowSystemEvent();
static WindowSystemEvent *getNonUserInputWindowSystemEvent();
diff --git a/src/gui/math3d/qmatrix4x4.cpp b/src/gui/math3d/qmatrix4x4.cpp
index 0e5db480d7..d04a502519 100644
--- a/src/gui/math3d/qmatrix4x4.cpp
+++ b/src/gui/math3d/qmatrix4x4.cpp
@@ -100,7 +100,7 @@ QMatrix4x4::QMatrix4x4(const float *values)
*/
/*!
- \fn QGenericMatrix<N, M, float> QMatrix4x4::toGenericMatrix() const
+ \fn template <int N, int M> QGenericMatrix<N, M, float> QMatrix4x4::toGenericMatrix() const
Constructs a NxM generic matrix from the left-most N columns and
top-most M rows of this 4x4 matrix. If N or M is greater than 4,
@@ -1164,12 +1164,15 @@ void QMatrix4x4::rotate(float angle, float x, float y, float z)
/*!
\internal
*/
-void QMatrix4x4::projectedRotate(float angle, float x, float y, float z)
+void QMatrix4x4::projectedRotate(float angle, float x, float y, float z, float distanceToPlane)
{
// Used by QGraphicsRotation::applyTo() to perform a rotation
// and projection back to 2D in a single step.
+ if (qIsNull(distanceToPlane))
+ return rotate(angle, x, y, z);
if (angle == 0.0f)
return;
+
float c, s;
if (angle == 90.0f || angle == -270.0f) {
s = 1.0f;
@@ -1185,6 +1188,8 @@ void QMatrix4x4::projectedRotate(float angle, float x, float y, float z)
c = std::cos(a);
s = std::sin(a);
}
+
+ const qreal d = 1.0 / distanceToPlane;
if (x == 0.0f) {
if (y == 0.0f) {
if (z != 0.0f) {
@@ -1208,10 +1213,11 @@ void QMatrix4x4::projectedRotate(float angle, float x, float y, float z)
// Rotate around the Y axis.
if (y < 0)
s = -s;
- m[0][0] = m[0][0] * c + m[3][0] * s * inv_dist_to_plane;
- m[0][1] = m[0][1] * c + m[3][1] * s * inv_dist_to_plane;
- m[0][2] = m[0][2] * c + m[3][2] * s * inv_dist_to_plane;
- m[0][3] = m[0][3] * c + m[3][3] * s * inv_dist_to_plane;
+ s *= d;
+ m[0][0] = m[0][0] * c + m[3][0] * s;
+ m[0][1] = m[0][1] * c + m[3][1] * s;
+ m[0][2] = m[0][2] * c + m[3][2] * s;
+ m[0][3] = m[0][3] * c + m[3][3] * s;
flagBits = General;
return;
}
@@ -1219,10 +1225,11 @@ void QMatrix4x4::projectedRotate(float angle, float x, float y, float z)
// Rotate around the X axis.
if (x < 0)
s = -s;
- m[1][0] = m[1][0] * c - m[3][0] * s * inv_dist_to_plane;
- m[1][1] = m[1][1] * c - m[3][1] * s * inv_dist_to_plane;
- m[1][2] = m[1][2] * c - m[3][2] * s * inv_dist_to_plane;
- m[1][3] = m[1][3] * c - m[3][3] * s * inv_dist_to_plane;
+ s *= d;
+ m[1][0] = m[1][0] * c - m[3][0] * s;
+ m[1][1] = m[1][1] * c - m[3][1] * s;
+ m[1][2] = m[1][2] * c - m[3][2] * s;
+ m[1][3] = m[1][3] * c - m[3][3] * s;
flagBits = General;
return;
}
@@ -1249,14 +1256,24 @@ void QMatrix4x4::projectedRotate(float angle, float x, float y, float z)
rot.m[1][2] = 0.0f;
rot.m[2][2] = 1.0f;
rot.m[3][2] = 0.0f;
- rot.m[0][3] = (x * z * ic - y * s) * -inv_dist_to_plane;
- rot.m[1][3] = (y * z * ic + x * s) * -inv_dist_to_plane;
+ rot.m[0][3] = (x * z * ic - y * s) * -d;
+ rot.m[1][3] = (y * z * ic + x * s) * -d;
rot.m[2][3] = 0.0f;
rot.m[3][3] = 1.0f;
rot.flagBits = General;
*this *= rot;
}
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+/*!
+ \internal
+*/
+void QMatrix4x4::projectedRotate(float angle, float x, float y, float z)
+{
+ projectedRotate(angle, x, y, z, 1024.0);
+}
+#endif
+
/*!
\fn int QMatrix4x4::flags() const
\internal
diff --git a/src/gui/math3d/qmatrix4x4.h b/src/gui/math3d/qmatrix4x4.h
index 0af8738b61..f8e5bc4314 100644
--- a/src/gui/math3d/qmatrix4x4.h
+++ b/src/gui/math3d/qmatrix4x4.h
@@ -154,7 +154,13 @@ public:
friend Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QMatrix4x4 &m);
#endif
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ void projectedRotate(float angle, float x, float y, float z, float distanceToPlane);
+ // ### Qt7: Remove
void projectedRotate(float angle, float x, float y, float z);
+#else
+ void projectedRotate(float angle, float x, float y, float z, float distanceToPlane = 1024.0);
+#endif
// When matrices are multiplied, the flag bits are or-ed together.
// Note that the ordering of the bit values matters. (ident < t < s < r2d < r < p)
diff --git a/src/gui/math3d/qquaternion.cpp b/src/gui/math3d/qquaternion.cpp
index 018f6fb731..e7c5945208 100644
--- a/src/gui/math3d/qquaternion.cpp
+++ b/src/gui/math3d/qquaternion.cpp
@@ -26,14 +26,14 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn QQuaternion::QQuaternion()
+ \fn QQuaternion::QQuaternion() noexcept
Constructs an identity quaternion (1, 0, 0, 0), i.e. with the vector (0, 0, 0)
and scalar 1.
*/
/*!
- \fn QQuaternion::QQuaternion(Qt::Initialization)
+ \fn QQuaternion::QQuaternion(Qt::Initialization) noexcept
\since 5.5
\internal
@@ -41,7 +41,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn QQuaternion::QQuaternion(float scalar, float xpos, float ypos, float zpos)
+ \fn QQuaternion::QQuaternion(float scalar, float xpos, float ypos, float zpos) noexcept
Constructs a quaternion with the vector (\a xpos, \a ypos, \a zpos)
and \a scalar.
@@ -50,7 +50,7 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_VECTOR3D
/*!
- \fn QQuaternion::QQuaternion(float scalar, const QVector3D& vector)
+ \fn QQuaternion::QQuaternion(float scalar, const QVector3D &vector) noexcept
Constructs a quaternion vector from the specified \a vector and
\a scalar.
@@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn QVector3D QQuaternion::vector() const
+ \fn QVector3D QQuaternion::vector() const noexcept
Returns the vector component of this quaternion.
@@ -67,7 +67,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn void QQuaternion::setVector(const QVector3D& vector)
+ \fn void QQuaternion::setVector(const QVector3D &vector) noexcept
Sets the vector component of this quaternion to \a vector.
@@ -77,7 +77,7 @@ QT_BEGIN_NAMESPACE
#endif
/*!
- \fn void QQuaternion::setVector(float x, float y, float z)
+ \fn void QQuaternion::setVector(float x, float y, float z) noexcept
Sets the vector component of this quaternion to (\a x, \a y, \a z).
@@ -87,13 +87,13 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_VECTOR4D
/*!
- \fn QQuaternion::QQuaternion(const QVector4D& vector)
+ \fn QQuaternion::QQuaternion(const QVector4D &vector) noexcept
Constructs a quaternion from the components of \a vector.
*/
/*!
- \fn QVector4D QQuaternion::toVector4D() const
+ \fn QVector4D QQuaternion::toVector4D() const noexcept
Returns this quaternion as a 4D vector.
*/
@@ -101,14 +101,14 @@ QT_BEGIN_NAMESPACE
#endif
/*!
- \fn bool QQuaternion::isNull() const
+ \fn bool QQuaternion::isNull() const noexcept
Returns \c true if the x, y, z, and scalar components of this
quaternion are set to 0.0; otherwise returns \c false.
*/
/*!
- \fn bool QQuaternion::isIdentity() const
+ \fn bool QQuaternion::isIdentity() const noexcept
Returns \c true if the x, y, and z components of this
quaternion are set to 0.0, and the scalar component is set
@@ -116,7 +116,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn float QQuaternion::x() const
+ \fn float QQuaternion::x() const noexcept
Returns the x coordinate of this quaternion's vector.
@@ -124,7 +124,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn float QQuaternion::y() const
+ \fn float QQuaternion::y() const noexcept
Returns the y coordinate of this quaternion's vector.
@@ -132,7 +132,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn float QQuaternion::z() const
+ \fn float QQuaternion::z() const noexcept
Returns the z coordinate of this quaternion's vector.
@@ -140,7 +140,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn float QQuaternion::scalar() const
+ \fn float QQuaternion::scalar() const noexcept
Returns the scalar component of this quaternion.
@@ -148,7 +148,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn void QQuaternion::setX(float x)
+ \fn void QQuaternion::setX(float x) noexcept
Sets the x coordinate of this quaternion's vector to the given
\a x coordinate.
@@ -157,7 +157,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn void QQuaternion::setY(float y)
+ \fn void QQuaternion::setY(float y) noexcept
Sets the y coordinate of this quaternion's vector to the given
\a y coordinate.
@@ -166,7 +166,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn void QQuaternion::setZ(float z)
+ \fn void QQuaternion::setZ(float z) noexcept
Sets the z coordinate of this quaternion's vector to the given
\a z coordinate.
@@ -175,7 +175,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn void QQuaternion::setScalar(float scalar)
+ \fn void QQuaternion::setScalar(float scalar) noexcept
Sets the scalar component of this quaternion to \a scalar.
@@ -183,7 +183,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn float QQuaternion::dotProduct(const QQuaternion &q1, const QQuaternion &q2)
+ \fn float QQuaternion::dotProduct(const QQuaternion &q1, const QQuaternion &q2) noexcept
\since 5.5
Returns the dot product of \a q1 and \a q2.
@@ -227,8 +227,6 @@ float QQuaternion::lengthSquared() const
QQuaternion QQuaternion::normalized() const
{
const float scale = length();
- if (qFuzzyCompare(scale, 1.0f))
- return *this;
if (qFuzzyIsNull(scale))
return QQuaternion(0.0f, 0.0f, 0.0f, 0.0f);
return *this / scale;
@@ -243,7 +241,7 @@ QQuaternion QQuaternion::normalized() const
void QQuaternion::normalize()
{
const float len = length();
- if (qFuzzyCompare(len, 1.0f) || qFuzzyIsNull(len))
+ if (qFuzzyIsNull(len))
return;
xp /= len;
@@ -253,7 +251,7 @@ void QQuaternion::normalize()
}
/*!
- \fn QQuaternion QQuaternion::inverted() const
+ \fn QQuaternion QQuaternion::inverted() const noexcept
\since 5.5
Returns the inverse of this quaternion.
@@ -263,7 +261,7 @@ void QQuaternion::normalize()
*/
/*!
- \fn QQuaternion QQuaternion::conjugated() const
+ \fn QQuaternion QQuaternion::conjugated() const noexcept
\since 5.5
Returns the conjugate of this quaternion, which is
@@ -280,13 +278,13 @@ void QQuaternion::normalize()
\snippet code/src_gui_math3d_qquaternion.cpp 1
*/
-QVector3D QQuaternion::rotatedVector(const QVector3D& vector) const
+QVector3D QQuaternion::rotatedVector(const QVector3D &vector) const
{
return (*this * QQuaternion(0, vector) * conjugated()).vector();
}
/*!
- \fn QQuaternion &QQuaternion::operator+=(const QQuaternion &quaternion)
+ \fn QQuaternion &QQuaternion::operator+=(const QQuaternion &quaternion) noexcept
Adds the given \a quaternion to this quaternion and returns a reference to
this quaternion.
@@ -295,7 +293,7 @@ QVector3D QQuaternion::rotatedVector(const QVector3D& vector) const
*/
/*!
- \fn QQuaternion &QQuaternion::operator-=(const QQuaternion &quaternion)
+ \fn QQuaternion &QQuaternion::operator-=(const QQuaternion &quaternion) noexcept
Subtracts the given \a quaternion from this quaternion and returns a
reference to this quaternion.
@@ -304,7 +302,7 @@ QVector3D QQuaternion::rotatedVector(const QVector3D& vector) const
*/
/*!
- \fn QQuaternion &QQuaternion::operator*=(float factor)
+ \fn QQuaternion &QQuaternion::operator*=(float factor) noexcept
Multiplies this quaternion's components by the given \a factor, and
returns a reference to this quaternion.
@@ -313,7 +311,7 @@ QVector3D QQuaternion::rotatedVector(const QVector3D& vector) const
*/
/*!
- \fn QQuaternion &QQuaternion::operator*=(const QQuaternion &quaternion)
+ \fn QQuaternion &QQuaternion::operator*=(const QQuaternion &quaternion) noexcept
Multiplies this quaternion by \a quaternion and returns a reference
to this quaternion.
@@ -331,7 +329,7 @@ QVector3D QQuaternion::rotatedVector(const QVector3D& vector) const
#ifndef QT_NO_VECTOR3D
/*!
- \fn void QQuaternion::getAxisAndAngle(QVector3D *axis, float *angle) const
+ \fn void QQuaternion::getAxisAndAngle(QVector3D *axis, float *angle) const noexcept
\since 5.5
\overload
@@ -347,7 +345,7 @@ QVector3D QQuaternion::rotatedVector(const QVector3D& vector) const
\sa getAxisAndAngle()
*/
-QQuaternion QQuaternion::fromAxisAndAngle(const QVector3D& axis, float angle)
+QQuaternion QQuaternion::fromAxisAndAngle(const QVector3D &axis, float angle)
{
// Algorithm from:
// http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56
@@ -388,7 +386,7 @@ void QQuaternion::getAxisAndAngle(float *x, float *y, float *z, float *angle) co
*y = yp / length;
*z = zp / length;
}
- *angle = qRadiansToDegrees(2.0f * std::acos(wp));
+ *angle = qRadiansToDegrees(2.0f * std::atan2(length, wp));
} else {
// angle is 0 (mod 2*pi), so any axis will fit
*x = *y = *z = *angle = 0.0f;
@@ -740,21 +738,21 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
#endif // QT_NO_VECTOR3D
/*!
- \fn bool QQuaternion::operator==(const QQuaternion &q1, const QQuaternion &q2)
+ \fn bool QQuaternion::operator==(const QQuaternion &q1, const QQuaternion &q2) noexcept
Returns \c true if \a q1 is equal to \a q2; otherwise returns \c false.
This operator uses an exact floating-point comparison.
*/
/*!
- \fn bool QQuaternion::operator!=(const QQuaternion &q1, const QQuaternion &q2)
+ \fn bool QQuaternion::operator!=(const QQuaternion &q1, const QQuaternion &q2) noexcept
Returns \c true if \a q1 is not equal to \a q2; otherwise returns \c false.
This operator uses an exact floating-point comparison.
*/
/*!
- \fn const QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2)
+ \fn const QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2) noexcept
\relates QQuaternion
Returns a QQuaternion object that is the sum of the given quaternions,
@@ -764,7 +762,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
*/
/*!
- \fn const QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2)
+ \fn const QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2) noexcept
\relates QQuaternion
Returns a QQuaternion object that is formed by subtracting
@@ -774,7 +772,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
*/
/*!
- \fn const QQuaternion operator*(float factor, const QQuaternion &quaternion)
+ \fn const QQuaternion operator*(float factor, const QQuaternion &quaternion) noexcept
\relates QQuaternion
Returns a copy of the given \a quaternion, multiplied by the
@@ -784,7 +782,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
*/
/*!
- \fn const QQuaternion operator*(const QQuaternion &quaternion, float factor)
+ \fn const QQuaternion operator*(const QQuaternion &quaternion, float factor) noexcept
\relates QQuaternion
Returns a copy of the given \a quaternion, multiplied by the
@@ -794,7 +792,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
*/
/*!
- \fn const QQuaternion operator*(const QQuaternion &q1, const QQuaternion& q2)
+ \fn const QQuaternion operator*(const QQuaternion &q1, const QQuaternion &q2) noexcept
\relates QQuaternion
Multiplies \a q1 and \a q2 using quaternion multiplication.
@@ -805,7 +803,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
*/
/*!
- \fn const QQuaternion operator-(const QQuaternion &quaternion)
+ \fn const QQuaternion operator-(const QQuaternion &quaternion) noexcept
\relates QQuaternion
\overload
@@ -828,7 +826,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
#ifndef QT_NO_VECTOR3D
/*!
- \fn QVector3D operator*(const QQuaternion &quaternion, const QVector3D &vec)
+ \fn QVector3D operator*(const QQuaternion &quaternion, const QVector3D &vec) noexcept
\since 5.5
\relates QQuaternion
@@ -838,7 +836,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
#endif
/*!
- \fn bool qFuzzyCompare(const QQuaternion& q1, const QQuaternion& q2)
+ \fn bool qFuzzyCompare(const QQuaternion &q1, const QQuaternion &q2) noexcept
\relates QQuaternion
Returns \c true if \a q1 and \a q2 are equal, allowing for a small
@@ -857,7 +855,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
\sa nlerp()
*/
QQuaternion QQuaternion::slerp
- (const QQuaternion& q1, const QQuaternion& q2, float t)
+ (const QQuaternion &q1, const QQuaternion &q2, float t)
{
// Handle the easy cases first.
if (t <= 0.0f)
@@ -906,7 +904,7 @@ QQuaternion QQuaternion::slerp
\sa slerp()
*/
QQuaternion QQuaternion::nlerp
- (const QQuaternion& q1, const QQuaternion& q2, float t)
+ (const QQuaternion &q1, const QQuaternion &q2, float t)
{
// Handle the easy cases first.
if (t <= 0.0f)
diff --git a/src/gui/math3d/qquaternion.h b/src/gui/math3d/qquaternion.h
index 226a077d4c..7cfbd9b818 100644
--- a/src/gui/math3d/qquaternion.h
+++ b/src/gui/math3d/qquaternion.h
@@ -17,117 +17,123 @@ QT_BEGIN_NAMESPACE
class QMatrix4x4;
class QVariant;
-class Q_GUI_EXPORT QQuaternion
+class QT6_ONLY(Q_GUI_EXPORT) QQuaternion
{
public:
- QQuaternion();
- explicit QQuaternion(Qt::Initialization) {}
- QQuaternion(float scalar, float xpos, float ypos, float zpos);
+ constexpr QQuaternion() noexcept;
+ explicit QQuaternion(Qt::Initialization) noexcept {}
+ constexpr QQuaternion(float scalar, float xpos, float ypos, float zpos) noexcept;
#ifndef QT_NO_VECTOR3D
- QQuaternion(float scalar, const QVector3D& vector);
+ constexpr QQuaternion(float scalar, const QVector3D &vector) noexcept;
#endif
#ifndef QT_NO_VECTOR4D
- explicit QQuaternion(const QVector4D& vector);
+ constexpr explicit QQuaternion(const QVector4D &vector) noexcept;
#endif
- bool isNull() const;
- bool isIdentity() const;
+ constexpr bool isNull() const noexcept;
+ constexpr bool isIdentity() const noexcept;
#ifndef QT_NO_VECTOR3D
- QVector3D vector() const;
- void setVector(const QVector3D& vector);
+ constexpr QVector3D vector() const noexcept;
+ constexpr void setVector(const QVector3D &vector) noexcept;
#endif
- void setVector(float x, float y, float z);
+ constexpr void setVector(float x, float y, float z) noexcept;
- float x() const;
- float y() const;
- float z() const;
- float scalar() const;
+ constexpr float x() const noexcept;
+ constexpr float y() const noexcept;
+ constexpr float z() const noexcept;
+ constexpr float scalar() const noexcept;
- void setX(float x);
- void setY(float y);
- void setZ(float z);
- void setScalar(float scalar);
+ constexpr void setX(float x) noexcept;
+ constexpr void setY(float y) noexcept;
+ constexpr void setZ(float z) noexcept;
+ constexpr void setScalar(float scalar) noexcept;
- constexpr static inline float dotProduct(const QQuaternion &q1, const QQuaternion &q2);
+ constexpr static float dotProduct(const QQuaternion &q1, const QQuaternion &q2) noexcept;
- float length() const;
- float lengthSquared() const;
+ // ### Qt 7: make the next four constexpr
+ // (perhaps using std::hypot, constexpr in C++26, or constexpr qHypot)
+ QT7_ONLY(Q_GUI_EXPORT) float length() const;
+ QT7_ONLY(Q_GUI_EXPORT) float lengthSquared() const;
- [[nodiscard]] QQuaternion normalized() const;
- void normalize();
+ [[nodiscard]] QT7_ONLY(Q_GUI_EXPORT) QQuaternion normalized() const;
+ QT7_ONLY(Q_GUI_EXPORT) void normalize();
- inline QQuaternion inverted() const;
+ constexpr QQuaternion inverted() const noexcept;
- [[nodiscard]] QQuaternion conjugated() const;
+ [[nodiscard]] constexpr QQuaternion conjugated() const noexcept;
- QVector3D rotatedVector(const QVector3D& vector) const;
+ QT7_ONLY(Q_GUI_EXPORT) QVector3D rotatedVector(const QVector3D &vector) const;
- QQuaternion &operator+=(const QQuaternion &quaternion);
- QQuaternion &operator-=(const QQuaternion &quaternion);
- QQuaternion &operator*=(float factor);
- QQuaternion &operator*=(const QQuaternion &quaternion);
- QQuaternion &operator/=(float divisor);
+ constexpr QQuaternion &operator+=(const QQuaternion &quaternion) noexcept;
+ constexpr QQuaternion &operator-=(const QQuaternion &quaternion) noexcept;
+ constexpr QQuaternion &operator*=(float factor) noexcept;
+ constexpr QQuaternion &operator*=(const QQuaternion &quaternion) noexcept;
+ constexpr QQuaternion &operator/=(float divisor);
QT_WARNING_PUSH
QT_WARNING_DISABLE_FLOAT_COMPARE
- friend inline bool operator==(const QQuaternion &q1, const QQuaternion &q2) noexcept
+ friend constexpr bool operator==(const QQuaternion &q1, const QQuaternion &q2) noexcept
{
return q1.wp == q2.wp && q1.xp == q2.xp && q1.yp == q2.yp && q1.zp == q2.zp;
}
- friend inline bool operator!=(const QQuaternion &q1, const QQuaternion &q2) noexcept
+ friend constexpr bool operator!=(const QQuaternion &q1, const QQuaternion &q2) noexcept
{
return !(q1 == q2);
}
QT_WARNING_POP
- friend inline const QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2);
- friend inline const QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2);
- friend inline const QQuaternion operator*(float factor, const QQuaternion &quaternion);
- friend inline const QQuaternion operator*(const QQuaternion &quaternion, float factor);
- friend inline const QQuaternion operator*(const QQuaternion &q1, const QQuaternion& q2);
- friend inline const QQuaternion operator-(const QQuaternion &quaternion);
- friend inline const QQuaternion operator/(const QQuaternion &quaternion, float divisor);
+ friend constexpr QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2) noexcept;
+ friend constexpr QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2) noexcept;
+ friend constexpr QQuaternion operator*(float factor, const QQuaternion &quaternion) noexcept;
+ friend constexpr QQuaternion operator*(const QQuaternion &quaternion, float factor) noexcept;
+ friend constexpr QQuaternion operator*(const QQuaternion &q1, const QQuaternion &q2) noexcept;
+ friend constexpr QQuaternion operator-(const QQuaternion &quaternion) noexcept;
+ friend constexpr QQuaternion operator/(const QQuaternion &quaternion, float divisor);
- friend inline bool qFuzzyCompare(const QQuaternion& q1, const QQuaternion& q2);
+ friend constexpr bool qFuzzyCompare(const QQuaternion &q1, const QQuaternion &q2) noexcept;
#ifndef QT_NO_VECTOR4D
- QVector4D toVector4D() const;
+ constexpr QVector4D toVector4D() const noexcept;
#endif
- operator QVariant() const;
+ QT7_ONLY(Q_GUI_EXPORT) operator QVariant() const;
#ifndef QT_NO_VECTOR3D
inline void getAxisAndAngle(QVector3D *axis, float *angle) const;
- static QQuaternion fromAxisAndAngle(const QVector3D& axis, float angle);
+ QT7_ONLY(Q_GUI_EXPORT) static QQuaternion fromAxisAndAngle(const QVector3D &axis, float angle);
#endif
- void getAxisAndAngle(float *x, float *y, float *z, float *angle) const;
- static QQuaternion fromAxisAndAngle
- (float x, float y, float z, float angle);
+ QT7_ONLY(Q_GUI_EXPORT) void getAxisAndAngle(float *x, float *y, float *z, float *angle) const;
+ QT7_ONLY(Q_GUI_EXPORT) static QQuaternion fromAxisAndAngle(float x, float y, float z,
+ float angle);
#ifndef QT_NO_VECTOR3D
inline QVector3D toEulerAngles() const;
- static inline QQuaternion fromEulerAngles(const QVector3D &eulerAngles);
+ QT7_ONLY(Q_GUI_EXPORT) static inline QQuaternion fromEulerAngles(const QVector3D &eulerAngles);
#endif
- void getEulerAngles(float *pitch, float *yaw, float *roll) const;
- static QQuaternion fromEulerAngles(float pitch, float yaw, float roll);
+ QT7_ONLY(Q_GUI_EXPORT) void getEulerAngles(float *pitch, float *yaw, float *roll) const;
+ QT7_ONLY(Q_GUI_EXPORT) static QQuaternion fromEulerAngles(float pitch, float yaw, float roll);
- QMatrix3x3 toRotationMatrix() const;
- static QQuaternion fromRotationMatrix(const QMatrix3x3 &rot3x3);
+ QT7_ONLY(Q_GUI_EXPORT) QMatrix3x3 toRotationMatrix() const;
+ QT7_ONLY(Q_GUI_EXPORT) static QQuaternion fromRotationMatrix(const QMatrix3x3 &rot3x3);
#ifndef QT_NO_VECTOR3D
- void getAxes(QVector3D *xAxis, QVector3D *yAxis, QVector3D *zAxis) const;
- static QQuaternion fromAxes(const QVector3D &xAxis, const QVector3D &yAxis, const QVector3D &zAxis);
+ QT7_ONLY(Q_GUI_EXPORT) void getAxes(QVector3D *xAxis, QVector3D *yAxis, QVector3D *zAxis) const;
+ QT7_ONLY(Q_GUI_EXPORT) static QQuaternion fromAxes(const QVector3D &xAxis,
+ const QVector3D &yAxis,
+ const QVector3D &zAxis);
- static QQuaternion fromDirection(const QVector3D &direction, const QVector3D &up);
+ QT7_ONLY(Q_GUI_EXPORT) static QQuaternion fromDirection(const QVector3D &direction,
+ const QVector3D &up);
- static QQuaternion rotationTo(const QVector3D &from, const QVector3D &to);
+ QT7_ONLY(Q_GUI_EXPORT) static QQuaternion rotationTo(const QVector3D &from,
+ const QVector3D &to);
#endif
- static QQuaternion slerp
- (const QQuaternion& q1, const QQuaternion& q2, float t);
- static QQuaternion nlerp
- (const QQuaternion& q1, const QQuaternion& q2, float t);
+ QT7_ONLY(Q_GUI_EXPORT) static QQuaternion slerp(const QQuaternion &q1, const QQuaternion &q2,
+ float t);
+ QT7_ONLY(Q_GUI_EXPORT) static QQuaternion nlerp(const QQuaternion &q1, const QQuaternion &q2,
+ float t);
private:
float wp, xp, yp, zp;
@@ -135,40 +141,41 @@ private:
Q_DECLARE_TYPEINFO(QQuaternion, Q_PRIMITIVE_TYPE);
-inline QQuaternion::QQuaternion() : wp(1.0f), xp(0.0f), yp(0.0f), zp(0.0f) {}
+constexpr QQuaternion::QQuaternion() noexcept : wp(1.0f), xp(0.0f), yp(0.0f), zp(0.0f) {}
-inline QQuaternion::QQuaternion(float aScalar, float xpos, float ypos, float zpos) : wp(aScalar), xp(xpos), yp(ypos), zp(zpos) {}
+constexpr QQuaternion::QQuaternion(float aScalar, float xpos, float ypos, float zpos) noexcept
+ : wp(aScalar), xp(xpos), yp(ypos), zp(zpos) {}
QT_WARNING_PUSH
QT_WARNING_DISABLE_FLOAT_COMPARE
-inline bool QQuaternion::isNull() const
+constexpr bool QQuaternion::isNull() const noexcept
{
return wp == 0.0f && xp == 0.0f && yp == 0.0f && zp == 0.0f;
}
-inline bool QQuaternion::isIdentity() const
+constexpr bool QQuaternion::isIdentity() const noexcept
{
return wp == 1.0f && xp == 0.0f && yp == 0.0f && zp == 0.0f;
}
QT_WARNING_POP
-inline float QQuaternion::x() const { return xp; }
-inline float QQuaternion::y() const { return yp; }
-inline float QQuaternion::z() const { return zp; }
-inline float QQuaternion::scalar() const { return wp; }
+constexpr float QQuaternion::x() const noexcept { return xp; }
+constexpr float QQuaternion::y() const noexcept { return yp; }
+constexpr float QQuaternion::z() const noexcept { return zp; }
+constexpr float QQuaternion::scalar() const noexcept { return wp; }
-inline void QQuaternion::setX(float aX) { xp = aX; }
-inline void QQuaternion::setY(float aY) { yp = aY; }
-inline void QQuaternion::setZ(float aZ) { zp = aZ; }
-inline void QQuaternion::setScalar(float aScalar) { wp = aScalar; }
+constexpr void QQuaternion::setX(float aX) noexcept { xp = aX; }
+constexpr void QQuaternion::setY(float aY) noexcept { yp = aY; }
+constexpr void QQuaternion::setZ(float aZ) noexcept { zp = aZ; }
+constexpr void QQuaternion::setScalar(float aScalar) noexcept { wp = aScalar; }
-constexpr inline float QQuaternion::dotProduct(const QQuaternion &q1, const QQuaternion &q2)
+constexpr float QQuaternion::dotProduct(const QQuaternion &q1, const QQuaternion &q2) noexcept
{
return q1.wp * q2.wp + q1.xp * q2.xp + q1.yp * q2.yp + q1.zp * q2.zp;
}
-inline QQuaternion QQuaternion::inverted() const
+constexpr QQuaternion QQuaternion::inverted() const noexcept
{
// Need some extra precision if the length is very small.
double len = double(wp) * double(wp) +
@@ -181,12 +188,12 @@ inline QQuaternion QQuaternion::inverted() const
return QQuaternion(0.0f, 0.0f, 0.0f, 0.0f);
}
-inline QQuaternion QQuaternion::conjugated() const
+constexpr QQuaternion QQuaternion::conjugated() const noexcept
{
return QQuaternion(wp, -xp, -yp, -zp);
}
-inline QQuaternion &QQuaternion::operator+=(const QQuaternion &quaternion)
+constexpr QQuaternion &QQuaternion::operator+=(const QQuaternion &quaternion) noexcept
{
wp += quaternion.wp;
xp += quaternion.xp;
@@ -195,7 +202,7 @@ inline QQuaternion &QQuaternion::operator+=(const QQuaternion &quaternion)
return *this;
}
-inline QQuaternion &QQuaternion::operator-=(const QQuaternion &quaternion)
+constexpr QQuaternion &QQuaternion::operator-=(const QQuaternion &quaternion) noexcept
{
wp -= quaternion.wp;
xp -= quaternion.xp;
@@ -204,7 +211,7 @@ inline QQuaternion &QQuaternion::operator-=(const QQuaternion &quaternion)
return *this;
}
-inline QQuaternion &QQuaternion::operator*=(float factor)
+constexpr QQuaternion &QQuaternion::operator*=(float factor) noexcept
{
wp *= factor;
xp *= factor;
@@ -213,7 +220,7 @@ inline QQuaternion &QQuaternion::operator*=(float factor)
return *this;
}
-inline const QQuaternion operator*(const QQuaternion &q1, const QQuaternion& q2)
+constexpr QQuaternion operator*(const QQuaternion &q1, const QQuaternion &q2) noexcept
{
float yy = (q1.wp - q1.yp) * (q2.wp + q2.zp);
float zz = (q1.wp + q1.yp) * (q2.wp - q2.zp);
@@ -229,13 +236,13 @@ inline const QQuaternion operator*(const QQuaternion &q1, const QQuaternion& q2)
return QQuaternion(w, x, y, z);
}
-inline QQuaternion &QQuaternion::operator*=(const QQuaternion &quaternion)
+constexpr QQuaternion &QQuaternion::operator*=(const QQuaternion &quaternion) noexcept
{
*this = *this * quaternion;
return *this;
}
-inline QQuaternion &QQuaternion::operator/=(float divisor)
+constexpr QQuaternion &QQuaternion::operator/=(float divisor)
{
wp /= divisor;
xp /= divisor;
@@ -244,37 +251,37 @@ inline QQuaternion &QQuaternion::operator/=(float divisor)
return *this;
}
-inline const QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2)
+constexpr QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2) noexcept
{
return QQuaternion(q1.wp + q2.wp, q1.xp + q2.xp, q1.yp + q2.yp, q1.zp + q2.zp);
}
-inline const QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2)
+constexpr QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2) noexcept
{
return QQuaternion(q1.wp - q2.wp, q1.xp - q2.xp, q1.yp - q2.yp, q1.zp - q2.zp);
}
-inline const QQuaternion operator*(float factor, const QQuaternion &quaternion)
+constexpr QQuaternion operator*(float factor, const QQuaternion &quaternion) noexcept
{
return QQuaternion(quaternion.wp * factor, quaternion.xp * factor, quaternion.yp * factor, quaternion.zp * factor);
}
-inline const QQuaternion operator*(const QQuaternion &quaternion, float factor)
+constexpr QQuaternion operator*(const QQuaternion &quaternion, float factor) noexcept
{
return QQuaternion(quaternion.wp * factor, quaternion.xp * factor, quaternion.yp * factor, quaternion.zp * factor);
}
-inline const QQuaternion operator-(const QQuaternion &quaternion)
+constexpr QQuaternion operator-(const QQuaternion &quaternion) noexcept
{
return QQuaternion(-quaternion.wp, -quaternion.xp, -quaternion.yp, -quaternion.zp);
}
-inline const QQuaternion operator/(const QQuaternion &quaternion, float divisor)
+constexpr QQuaternion operator/(const QQuaternion &quaternion, float divisor)
{
return QQuaternion(quaternion.wp / divisor, quaternion.xp / divisor, quaternion.yp / divisor, quaternion.zp / divisor);
}
-inline bool qFuzzyCompare(const QQuaternion& q1, const QQuaternion& q2)
+constexpr bool qFuzzyCompare(const QQuaternion &q1, const QQuaternion &q2) noexcept
{
return qFuzzyCompare(q1.wp, q2.wp) &&
qFuzzyCompare(q1.xp, q2.xp) &&
@@ -284,17 +291,17 @@ inline bool qFuzzyCompare(const QQuaternion& q1, const QQuaternion& q2)
#ifndef QT_NO_VECTOR3D
-inline QQuaternion::QQuaternion(float aScalar, const QVector3D& aVector)
+constexpr QQuaternion::QQuaternion(float aScalar, const QVector3D &aVector) noexcept
: wp(aScalar), xp(aVector.x()), yp(aVector.y()), zp(aVector.z()) {}
-inline void QQuaternion::setVector(const QVector3D& aVector)
+constexpr void QQuaternion::setVector(const QVector3D &aVector) noexcept
{
xp = aVector.x();
yp = aVector.y();
zp = aVector.z();
}
-inline QVector3D QQuaternion::vector() const
+constexpr QVector3D QQuaternion::vector() const noexcept
{
return QVector3D(xp, yp, zp);
}
@@ -325,7 +332,7 @@ inline QQuaternion QQuaternion::fromEulerAngles(const QVector3D &eulerAngles)
#endif
-inline void QQuaternion::setVector(float aX, float aY, float aZ)
+constexpr void QQuaternion::setVector(float aX, float aY, float aZ) noexcept
{
xp = aX;
yp = aY;
@@ -334,10 +341,10 @@ inline void QQuaternion::setVector(float aX, float aY, float aZ)
#ifndef QT_NO_VECTOR4D
-inline QQuaternion::QQuaternion(const QVector4D& aVector)
+constexpr QQuaternion::QQuaternion(const QVector4D &aVector) noexcept
: wp(aVector.w()), xp(aVector.x()), yp(aVector.y()), zp(aVector.z()) {}
-inline QVector4D QQuaternion::toVector4D() const
+constexpr QVector4D QQuaternion::toVector4D() const noexcept
{
return QVector4D(xp, yp, zp, wp);
}
diff --git a/src/gui/math3d/qvectornd.h b/src/gui/math3d/qvectornd.h
index b5985a8d11..a8adf12801 100644
--- a/src/gui/math3d/qvectornd.h
+++ b/src/gui/math3d/qvectornd.h
@@ -10,8 +10,13 @@
#include <QtCore/qrect.h>
#include <QtCore/qmath.h>
+#include <QtCore/q20type_traits.h>
+#include <QtCore/q23utility.h>
+
QT_BEGIN_NAMESPACE
+// QT_ENABLE_P0846_SEMANTICS_FOR(get) // from qpoint.h
+
class QVector2D;
class QVector3D;
class QVector4D;
@@ -145,13 +150,10 @@ private:
template <std::size_t I,
typename V,
std::enable_if_t<(I < 2), bool> = true,
- std::enable_if_t<std::is_same_v<std::decay_t<V>, QVector2D>, bool> = true>
+ std::enable_if_t<std::is_same_v<q20::remove_cvref_t<V>, QVector2D>, bool> = true>
friend constexpr decltype(auto) get(V &&vec) noexcept
{
- if constexpr (I == 0)
- return (std::forward<V>(vec).v[0]);
- else if constexpr (I == 1)
- return (std::forward<V>(vec).v[1]);
+ return q23::forward_like<V>(vec.v[I]);
}
};
@@ -307,15 +309,10 @@ private:
template <std::size_t I,
typename V,
std::enable_if_t<(I < 3), bool> = true,
- std::enable_if_t<std::is_same_v<std::decay_t<V>, QVector3D>, bool> = true>
+ std::enable_if_t<std::is_same_v<q20::remove_cvref_t<V>, QVector3D>, bool> = true>
friend constexpr decltype(auto) get(V &&vec) noexcept
{
- if constexpr (I == 0)
- return (std::forward<V>(vec).v[0]);
- else if constexpr (I == 1)
- return (std::forward<V>(vec).v[1]);
- else if constexpr (I == 2)
- return (std::forward<V>(vec).v[2]);
+ return q23::forward_like<V>(vec.v[I]);
}
};
@@ -464,17 +461,10 @@ private:
template <std::size_t I,
typename V,
std::enable_if_t<(I < 4), bool> = true,
- std::enable_if_t<std::is_same_v<std::decay_t<V>, QVector4D>, bool> = true>
+ std::enable_if_t<std::is_same_v<q20::remove_cvref_t<V>, QVector4D>, bool> = true>
friend constexpr decltype(auto) get(V &&vec) noexcept
{
- if constexpr (I == 0)
- return (std::forward<V>(vec).v[0]);
- else if constexpr (I == 1)
- return (std::forward<V>(vec).v[1]);
- else if constexpr (I == 2)
- return (std::forward<V>(vec).v[2]);
- else if constexpr (I == 3)
- return (std::forward<V>(vec).v[3]);
+ return q23::forward_like<V>(vec.v[I]);
}
};
diff --git a/src/gui/opengl/platform/egl/qeglconvenience.cpp b/src/gui/opengl/platform/egl/qeglconvenience.cpp
index 3d5b99c38c..4af50d72f2 100644
--- a/src/gui/opengl/platform/egl/qeglconvenience.cpp
+++ b/src/gui/opengl/platform/egl/qeglconvenience.cpp
@@ -88,13 +88,12 @@ QList<EGLint> q_createConfigAttributesFromFormat(const QSurfaceFormat &format)
bool q_reduceConfigAttributes(QList<EGLint> *configAttributes)
{
- int i = -1;
// Reduce the complexity of a configuration request to ask for less
// because the previous request did not result in success. Returns
// true if the complexity was reduced, or false if no further
// reductions in complexity are possible.
- i = configAttributes->indexOf(EGL_SWAP_BEHAVIOR);
+ qsizetype i = configAttributes->indexOf(EGL_SWAP_BEHAVIOR);
if (i >= 0) {
configAttributes->remove(i,2);
}
@@ -216,14 +215,17 @@ EGLConfig QEglConfigChooser::chooseConfig()
configureAttributes.append(EGL_OPENVG_BIT);
break;
#ifdef EGL_VERSION_1_4
- case QSurfaceFormat::DefaultRenderableType:
+ case QSurfaceFormat::DefaultRenderableType: {
#ifndef QT_NO_OPENGL
- if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL)
+ // NVIDIA EGL only provides desktop GL for development purposes, and recommends against using it.
+ const char *vendor = eglQueryString(display(), EGL_VENDOR);
+ if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL && (!vendor || !strstr(vendor, "NVIDIA")))
configureAttributes.append(EGL_OPENGL_BIT);
else
#endif // QT_NO_OPENGL
needsES2Plus = true;
break;
+ }
case QSurfaceFormat::OpenGL:
configureAttributes.append(EGL_OPENGL_BIT);
break;
@@ -255,7 +257,7 @@ EGLConfig QEglConfigChooser::chooseConfig()
// Fetch all of the matching configurations and find the
// first that matches the pixel format we wanted.
- int i = configureAttributes.indexOf(EGL_RED_SIZE);
+ qsizetype i = configureAttributes.indexOf(EGL_RED_SIZE);
m_confAttrRed = configureAttributes.at(i+1);
i = configureAttributes.indexOf(EGL_GREEN_SIZE);
m_confAttrGreen = configureAttributes.at(i+1);
@@ -265,7 +267,8 @@ EGLConfig QEglConfigChooser::chooseConfig()
m_confAttrAlpha = i == -1 ? 0 : configureAttributes.at(i+1);
QList<EGLConfig> configs(matching);
- eglChooseConfig(display(), configureAttributes.constData(), configs.data(), configs.size(), &matching);
+ eglChooseConfig(display(), configureAttributes.constData(), configs.data(),
+ EGLint(configs.size()), &matching);
if (!cfg && matching > 0)
cfg = configs.first();
@@ -353,6 +356,7 @@ QSurfaceFormat q_glFormatFromConfig(EGLDisplay display, const EGLConfig config,
else if (referenceFormat.renderableType() == QSurfaceFormat::DefaultRenderableType
#ifndef QT_NO_OPENGL
&& QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL
+ && !strstr(eglQueryString(display, EGL_VENDOR), "NVIDIA")
#endif
&& (renderableType & EGL_OPENGL_BIT))
format.setRenderableType(QSurfaceFormat::OpenGL);
diff --git a/src/gui/opengl/platform/egl/qeglplatformcontext_p.h b/src/gui/opengl/platform/egl/qeglplatformcontext_p.h
index 556fbabee4..b1e9c4b4c2 100644
--- a/src/gui/opengl/platform/egl/qeglplatformcontext_p.h
+++ b/src/gui/opengl/platform/egl/qeglplatformcontext_p.h
@@ -69,12 +69,14 @@ public:
QSurfaceFormat format() const override;
bool isSharing() const override { return m_shareContext != EGL_NO_CONTEXT; }
- bool isValid() const override { return m_eglContext != EGL_NO_CONTEXT; }
+ bool isValid() const override { return m_eglContext != EGL_NO_CONTEXT && !m_markedInvalid; }
EGLContext nativeContext() const override { return eglContext(); }
EGLConfig config() const override { return eglConfig(); }
EGLDisplay display() const override { return eglDisplay(); }
+ virtual void invalidateContext() override { m_markedInvalid = true; }
+
EGLContext eglContext() const;
EGLDisplay eglDisplay() const;
EGLConfig eglConfig() const;
@@ -102,6 +104,8 @@ private:
Flags m_flags;
bool m_ownsContext = false;
QList<EGLint> m_contextAttrs;
+
+ bool m_markedInvalid = false;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QEGLPlatformContext::Flags)
diff --git a/src/gui/opengl/platform/egl/qeglstreamconvenience_p.h b/src/gui/opengl/platform/egl/qeglstreamconvenience_p.h
index d7c9012feb..edf73fe981 100644
--- a/src/gui/opengl/platform/egl/qeglstreamconvenience_p.h
+++ b/src/gui/opengl/platform/egl/qeglstreamconvenience_p.h
@@ -113,6 +113,10 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMEROUTPUTEXTPROC) (EGLDisplay
#define EGL_PLATFORM_X11_KHR 0x31D5
#endif
+#ifndef EGL_PLATFORM_XCB_KHR
+#define EGL_PLATFORM_XCB_KHR 0x31DC
+#endif
+
#ifndef EGL_NV_stream_attrib
typedef EGLStreamKHR (EGLAPIENTRYP PFNEGLCREATESTREAMATTRIBNVPROC) (EGLDisplay dpy, const EGLAttrib *attrib_list);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLSETSTREAMATTRIBNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLAttrib value);
diff --git a/src/gui/opengl/platform/unix/qglxconvenience.cpp b/src/gui/opengl/platform/unix/qglxconvenience.cpp
index a756caf343..ce70818042 100644
--- a/src/gui/opengl/platform/unix/qglxconvenience.cpp
+++ b/src/gui/opengl/platform/unix/qglxconvenience.cpp
@@ -124,7 +124,7 @@ struct QXcbSoftwareOpenGLEnforcer {
}
if (forceSoftwareOpenGL)
- qputenv("LIBGL_ALWAYS_SOFTWARE", QByteArrayLiteral("1"));
+ qputenv("LIBGL_ALWAYS_SOFTWARE", "1");
}
~QXcbSoftwareOpenGLEnforcer() {
diff --git a/src/gui/opengl/qopengl.cpp b/src/gui/opengl/qopengl.cpp
index d8b22050c1..587975085a 100644
--- a/src/gui/opengl/qopengl.cpp
+++ b/src/gui/opengl/qopengl.cpp
@@ -195,29 +195,13 @@ struct OsTypeTerm
static QString hostOs();
static QVersionNumber hostKernelVersion() { return QVersionNumber::fromString(QSysInfo::kernelVersion()); }
static QString hostOsRelease() {
- QString ver;
#ifdef Q_OS_WIN
- const auto osver = QOperatingSystemVersion::current();
-#define Q_WINVER(major, minor) (major << 8 | minor)
- switch (Q_WINVER(osver.majorVersion(), osver.minorVersion())) {
- case Q_WINVER(6, 1):
- ver = QStringLiteral("7");
- break;
- case Q_WINVER(6, 2):
- ver = QStringLiteral("8");
- break;
- case Q_WINVER(6, 3):
- ver = QStringLiteral("8.1");
- break;
- case Q_WINVER(10, 0):
- ver = QStringLiteral("10");
- break;
- default:
- break;
- }
-#undef Q_WINVER
+ if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11)
+ return u"11"_s;
+ return u"10"_s;
+#else
+ return {};
#endif
- return ver;
}
bool isNull() const { return type.isEmpty(); }
@@ -401,7 +385,8 @@ static bool readGpuFeatures(const QOpenGLConfig::Gpu &gpu,
QJsonParseError error;
const QJsonDocument document = QJsonDocument::fromJson(jsonAsciiData, &error);
if (document.isNull()) {
- const int lineNumber = 1 + jsonAsciiData.left(error.offset).count('\n');
+ const qsizetype lineNumber =
+ QByteArrayView(jsonAsciiData).left(error.offset).count('\n') + 1;
QTextStream str(errorMessage);
str << "Failed to parse data: \"" << error.errorString()
<< "\" at line " << lineNumber << " (offset: "
diff --git a/src/gui/opengl/qopengl.h b/src/gui/opengl/qopengl.h
index 7e5b8e6961..e9a11080ce 100644
--- a/src/gui/opengl/qopengl.h
+++ b/src/gui/opengl/qopengl.h
@@ -8,9 +8,19 @@
#ifndef QT_NO_OPENGL
-// Windows always needs this to ensure that APIENTRY gets defined
+// On Windows we need to ensure that APIENTRY and WINGDIAPI are defined before
+// we can include gl.h. But we do not want to include <windows.h> in this public
+// Qt header, as it pollutes the global namespace with macros.
#if defined(Q_OS_WIN)
-# include <QtCore/qt_windows.h>
+# ifndef APIENTRY
+# define APIENTRY __stdcall
+# define Q_UNDEF_APIENTRY
+# endif // APIENTRY
+# ifndef WINGDIAPI
+# define WINGDIAPI __declspec(dllimport)
+# define Q_UNDEF_WINGDIAPI
+# endif // WINGDIAPI
+# define QT_APIENTRY __stdcall
#endif
// Note: Apple is a "controlled platform" for OpenGL ABI so we
@@ -128,11 +138,11 @@ typedef char GLchar;
// OS X 10.6 doesn't define these which are needed below
// OS X 10.7 and later define them in gl3.h
-#ifndef APIENTRY
-#define APIENTRY
+#ifndef QT_APIENTRY
+#define QT_APIENTRY
#endif
-#ifndef APIENTRYP
-#define APIENTRYP APIENTRY *
+#ifndef QT_APIENTRYP
+#define QT_APIENTRYP QT_APIENTRY *
#endif
#ifndef GLAPI
#define GLAPI extern
@@ -231,15 +241,15 @@ struct _cl_event;
#endif
#ifndef GL_ARB_debug_output
-typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const GLvoid *userParam);
+typedef void (QT_APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const GLvoid *userParam);
#endif
#ifndef GL_AMD_debug_output
-typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,GLvoid *userParam);
+typedef void (QT_APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,GLvoid *userParam);
#endif
#ifndef GL_KHR_debug
-typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const GLvoid *userParam);
+typedef void (QT_APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const GLvoid *userParam);
#endif
#ifndef GL_NV_vdpau_interop
@@ -256,8 +266,8 @@ typedef ptrdiff_t qopengl_GLintptr;
typedef ptrdiff_t qopengl_GLsizeiptr;
-#if defined(APIENTRY) && !defined(QOPENGLF_APIENTRY)
-# define QOPENGLF_APIENTRY APIENTRY
+#if defined(QT_APIENTRY) && !defined(QOPENGLF_APIENTRY)
+# define QOPENGLF_APIENTRY QT_APIENTRY
#endif
# ifndef QOPENGLF_APIENTRYP
@@ -271,6 +281,15 @@ typedef ptrdiff_t qopengl_GLsizeiptr;
QT_END_NAMESPACE
+#ifdef Q_UNDEF_WINGDIAPI
+# undef WINGDIAPI
+# undef Q_UNDEF_WINGDIAPI
+#endif
+#ifdef Q_UNDEF_APIENTRY
+# undef APIENTRY
+# undef Q_UNDEF_APIENTRY
+#endif
+
#endif // QT_NO_OPENGL
#endif // QOPENGL_H
diff --git a/src/gui/opengl/qopenglextensions_p.h b/src/gui/opengl/qopenglextensions_p.h
index 2924754e3a..a6c4a68c6c 100644
--- a/src/gui/opengl/qopenglextensions_p.h
+++ b/src/gui/opengl/qopenglextensions_p.h
@@ -58,7 +58,10 @@ public:
TextureSwizzle = 0x01000000,
StandardDerivatives = 0x02000000,
ASTCTextureCompression = 0x04000000,
- ETC2TextureCompression = 0x08000000
+ ETC2TextureCompression = 0x08000000,
+ HalfFloatVertex = 0x10000000,
+ MultiView = 0x20000000,
+ MultiViewExtended = 0x40000000
};
Q_DECLARE_FLAGS(OpenGLExtensions, OpenGLExtension)
@@ -67,9 +70,9 @@ public:
GLvoid *glMapBuffer(GLenum target, GLenum access);
void glGetBufferSubData(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, GLvoid *data);
- void glDiscardFramebufferEXT (GLenum target, GLsizei numAttachments, const GLenum *attachments);
void flushShared();
+ void discardFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments);
QOpenGLExtensionsPrivate *d() const;
@@ -114,14 +117,6 @@ inline void QOpenGLExtensions::glGetBufferSubData(GLenum target, qopengl_GLintpt
Q_OPENGL_FUNCTIONS_DEBUG
}
-
-inline void QOpenGLExtensions::glDiscardFramebufferEXT (GLenum target, GLsizei numAttachments, const GLenum *attachments)
-{
- Q_D(QOpenGLExtensions);
- Q_ASSERT(QOpenGLExtensions::isInitialized(d));
- d->DiscardFramebuffer(target,numAttachments, attachments);
- Q_OPENGL_FUNCTIONS_DEBUG
-}
QT_END_NAMESPACE
#endif // QOPENGL_EXTENSIONS_P_H
diff --git a/src/gui/opengl/qopenglfunctions.cpp b/src/gui/opengl/qopenglfunctions.cpp
index d8c8e4704d..c9352fbc31 100644
--- a/src/gui/opengl/qopenglfunctions.cpp
+++ b/src/gui/opengl/qopenglfunctions.cpp
@@ -346,6 +346,12 @@ static int qt_gl_resolve_extensions()
extensions |= QOpenGLExtensions::TextureSwizzle;
if (extensionMatcher.match("GL_OES_standard_derivatives"))
extensions |= QOpenGLExtensions::StandardDerivatives;
+ if (extensionMatcher.match("GL_ARB_half_float_vertex"))
+ extensions |= QOpenGLExtensions::HalfFloatVertex;
+ if (extensionMatcher.match("GL_OVR_multiview"))
+ extensions |= QOpenGLExtensions::MultiView;
+ if (extensionMatcher.match("GL_OVR_multiview2"))
+ extensions |= QOpenGLExtensions::MultiViewExtended;
if (ctx->isOpenGLES()) {
if (format.majorVersion() >= 2)
@@ -359,8 +365,10 @@ static int qt_gl_resolve_extensions()
| QOpenGLExtensions::FramebufferBlit
| QOpenGLExtensions::FramebufferMultisample
| QOpenGLExtensions::Sized8Formats
+ | QOpenGLExtensions::DiscardFramebuffer
| QOpenGLExtensions::StandardDerivatives
- | QOpenGLExtensions::ETC2TextureCompression;
+ | QOpenGLExtensions::ETC2TextureCompression
+ | QOpenGLExtensions::HalfFloatVertex;
#ifndef Q_OS_WASM
// WebGL 2.0 specification explicitly does not support texture swizzles
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.19
@@ -443,6 +451,9 @@ static int qt_gl_resolve_extensions()
if (format.version() >= qMakePair(3, 3))
extensions |= QOpenGLExtensions::TextureSwizzle;
+ if (format.version() >= qMakePair(4, 3) || extensionMatcher.match("GL_ARB_invalidate_subdata"))
+ extensions |= QOpenGLExtensions::DiscardFramebuffer;
+
if (extensionMatcher.match("GL_ARB_map_buffer_range"))
extensions |= QOpenGLExtensions::MapBufferRange;
@@ -5044,7 +5055,23 @@ QOpenGLExtensionsPrivate::QOpenGLExtensionsPrivate(QOpenGLContext *ctx)
MapBuffer = RESOLVE(MapBuffer);
GetBufferSubData = RESOLVE(GetBufferSubData);
DiscardFramebuffer = RESOLVE(DiscardFramebuffer);
- }
+}
+
+void QOpenGLExtensions::discardFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments)
+{
+ Q_D(QOpenGLExtensions);
+ Q_ASSERT(QOpenGLExtensions::isInitialized(d));
+ Q_ASSERT(d->f.InvalidateFramebuffer || d->DiscardFramebuffer);
+
+ // On GLES >= 3 we prefer glInvalidateFramebuffer, even if the
+ // discard extension is present
+ if (d->f.InvalidateFramebuffer)
+ d->f.InvalidateFramebuffer(target, numAttachments, attachments);
+ else
+ d->DiscardFramebuffer(target, numAttachments, attachments);
+
+ Q_OPENGL_FUNCTIONS_DEBUG
+}
void QOpenGLExtensions::flushShared()
{
diff --git a/src/gui/opengl/qopenglfunctions.h b/src/gui/opengl/qopenglfunctions.h
index a3d100b847..05435903fb 100644
--- a/src/gui/opengl/qopenglfunctions.h
+++ b/src/gui/opengl/qopenglfunctions.h
@@ -191,7 +191,7 @@ struct QOpenGLFunctionsPrivate;
#undef glTexLevelParameteriv
-#if defined(Q_CLANG_QDOC)
+#if defined(Q_QDOC)
#undef GLbitfield
typedef unsigned int GLbitfield;
#undef GLchar
diff --git a/src/gui/opengl/qopenglprogrambinarycache_p.h b/src/gui/opengl/qopenglprogrambinarycache_p.h
index 0553121a22..c3850bdee3 100644
--- a/src/gui/opengl/qopenglprogrambinarycache_p.h
+++ b/src/gui/opengl/qopenglprogrambinarycache_p.h
@@ -20,7 +20,7 @@
#include <QtCore/qmutex.h>
#include <QtCore/QLoggingCategory>
#include <QtGui/private/qopenglcontext_p.h>
-#include <QtGui/private/qshader_p.h>
+#include <rhi/qshader.h>
QT_BEGIN_NAMESPACE
@@ -28,7 +28,7 @@ QT_BEGIN_NAMESPACE
// therefore stay independent from QOpenGLShader(Program). Must rely only on
// QOpenGLContext/Functions.
-Q_GUI_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcOpenGLProgramDiskCache)
+Q_DECLARE_EXPORTED_LOGGING_CATEGORY(lcOpenGLProgramDiskCache, Q_GUI_EXPORT)
class Q_GUI_EXPORT QOpenGLProgramBinaryCache
{
diff --git a/src/gui/opengl/qt_attribution.json b/src/gui/opengl/qt_attribution.json
index d3ff10d803..44310980e2 100644
--- a/src/gui/opengl/qt_attribution.json
+++ b/src/gui/opengl/qt_attribution.json
@@ -5,7 +5,7 @@
"QDocModule": "qtgui",
"Description": "OpenGL header generated from the Khronos OpenGL / OpenGL ES XML API Registry.",
"QtUsage": "Used on Windows and Linux in the OpenGL related headers of Qt GUI.",
- "Path": "qopenglext.h",
+ "Files": "qopenglext.h",
"Homepage": "https://www.khronos.org/",
"Version": "Revision 27684",
@@ -20,7 +20,7 @@
"QDocModule": "qtgui",
"Description": "OpenGL ES 2 header generated from the Khronos OpenGL / OpenGL ES XML API Registry.",
"QtUsage": "Used on Windows and Linux in the OpenGL related headers of Qt GUI.",
- "Path": "qopengles2ext.h",
+ "Files": "qopengles2ext.h",
"Homepage": "https://www.khronos.org/",
"Version": "Revision 27673",
diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp
index f609cddd3c..2304ee2256 100644
--- a/src/gui/painting/qbackingstore.cpp
+++ b/src/gui/painting/qbackingstore.cpp
@@ -230,7 +230,8 @@ void QBackingStore::flush(const QRegion &region, QWindow *window, const QPoint &
void QBackingStore::resize(const QSize &size)
{
d_ptr->size = size;
- handle()->resize(QHighDpi::scale(size, d_ptr->deviceIndependentToNativeFactor()), d_ptr->staticContents);
+ const qreal factor = d_ptr->deviceIndependentToNativeFactor();
+ handle()->resize(QHighDpi::scale(size, factor), QHighDpi::scale(d_ptr->staticContents, factor));
}
/*!
@@ -266,6 +267,13 @@ bool QBackingStore::scroll(const QRegion &area, int dx, int dy)
*/
void QBackingStore::setStaticContents(const QRegion &region)
{
+ [[maybe_unused]] static const bool didCheckPlatformSupport = []{
+ const auto *integration = QGuiApplicationPrivate::platformIntegration();
+ if (!integration->hasCapability(QPlatformIntegration::BackingStoreStaticContents))
+ qWarning("QBackingStore::setStaticContents(): Platform does not support static contents");
+ return true;
+ }();
+
d_ptr->staticContents = region;
}
diff --git a/src/gui/painting/qbackingstoredefaultcompositor.cpp b/src/gui/painting/qbackingstoredefaultcompositor.cpp
index bb5fe58aa8..c1452ca768 100644
--- a/src/gui/painting/qbackingstoredefaultcompositor.cpp
+++ b/src/gui/painting/qbackingstoredefaultcompositor.cpp
@@ -17,18 +17,14 @@ QBackingStoreDefaultCompositor::~QBackingStoreDefaultCompositor()
void QBackingStoreDefaultCompositor::reset()
{
- delete m_psNoBlend;
- m_psNoBlend = nullptr;
- delete m_psBlend;
- m_psBlend = nullptr;
- delete m_psPremulBlend;
- m_psPremulBlend = nullptr;
- delete m_sampler;
- m_sampler = nullptr;
- delete m_vbuf;
- m_vbuf = nullptr;
- delete m_texture;
- m_texture = nullptr;
+ m_rhi = nullptr;
+ m_psNoBlend.reset();
+ m_psBlend.reset();
+ m_psPremulBlend.reset();
+ m_samplerNearest.reset();
+ m_samplerLinear.reset();
+ m_vbuf.reset();
+ m_texture.reset();
m_widgetQuadData.reset();
for (PerQuadData &d : m_textureQuadData)
d.reset();
@@ -71,9 +67,7 @@ QRhiTexture *QBackingStoreDefaultCompositor::toTexture(const QImage &sourceImage
Q_FALLTHROUGH();
case QImage::Format_RGB32:
case QImage::Format_ARGB32:
-#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
*flags |= QPlatformBackingStore::TextureSwizzle;
-#endif
break;
case QImage::Format_RGBA8888_Premultiplied:
*flags |= QPlatformBackingStore::TexturePremultiplied;
@@ -101,7 +95,7 @@ QRhiTexture *QBackingStoreDefaultCompositor::toTexture(const QImage &sourceImage
const bool resized = !m_texture || m_texture->pixelSize() != image.size();
if (dirtyRegion.isEmpty() && !resized)
- return m_texture;
+ return m_texture.get();
if (needsConversion)
image = image.convertToFormat(QImage::Format_RGBA8888);
@@ -110,11 +104,11 @@ QRhiTexture *QBackingStoreDefaultCompositor::toTexture(const QImage &sourceImage
if (resized) {
if (!m_texture)
- m_texture = rhi->newTexture(QRhiTexture::RGBA8, image.size());
+ m_texture.reset(rhi->newTexture(QRhiTexture::RGBA8, image.size()));
else
m_texture->setPixelSize(image.size());
m_texture->create();
- resourceUpdates->uploadTexture(m_texture, image);
+ resourceUpdates->uploadTexture(m_texture.get(), image);
} else {
QRect imageRect = image.rect();
QRect rect = dirtyRegion.boundingRect() & imageRect;
@@ -123,10 +117,10 @@ QRhiTexture *QBackingStoreDefaultCompositor::toTexture(const QImage &sourceImage
subresDesc.setSourceSize(rect.size());
subresDesc.setDestinationTopLeft(rect.topLeft());
QRhiTextureUploadDescription uploadDesc(QRhiTextureUploadEntry(0, 0, subresDesc));
- resourceUpdates->uploadTexture(m_texture, uploadDesc);
+ resourceUpdates->uploadTexture(m_texture.get(), uploadDesc);
}
- return m_texture;
+ return m_texture.get();
}
static inline QRect scaledRect(const QRect &rect, qreal factor)
@@ -150,7 +144,7 @@ static QRegion scaledRegion(const QRegion &region, qreal factor, const QPoint &o
rects.append(scaledRect(rect.translated(offset), factor));
QRegion deviceRegion;
- deviceRegion.setRects(rects.constData(), rects.count());
+ deviceRegion.setRects(rects.constData(), rects.size());
return deviceRegion;
}
@@ -172,7 +166,7 @@ static QMatrix4x4 targetTransform(const QRectF &target, const QRect &viewport, b
matrix(1,3) = y_translate;
matrix(0,0) = x_scale;
- matrix(1,1) = y_scale;
+ matrix(1,1) = (invertY ? -1.0 : 1.0) * y_scale;
return matrix;
}
@@ -281,7 +275,7 @@ enum class PipelineBlend {
static QRhiGraphicsPipeline *createGraphicsPipeline(QRhi *rhi,
QRhiShaderResourceBindings *srb,
- QRhiSwapChain *swapchain,
+ QRhiRenderPassDescriptor *rpDesc,
PipelineBlend blend)
{
QRhiGraphicsPipeline *ps = rhi->newGraphicsPipeline();
@@ -325,7 +319,7 @@ static QRhiGraphicsPipeline *createGraphicsPipeline(QRhi *rhi,
});
ps->setVertexInputLayout(inputLayout);
ps->setShaderResourceBindings(srb);
- ps->setRenderPassDescriptor(swapchain->renderPassDescriptor());
+ ps->setRenderPassDescriptor(rpDesc);
if (!ps->create()) {
qWarning("QBackingStoreDefaultCompositor: Failed to build graphics pipeline");
@@ -337,7 +331,7 @@ static QRhiGraphicsPipeline *createGraphicsPipeline(QRhi *rhi,
static const int UBUF_SIZE = 120;
-QBackingStoreDefaultCompositor::PerQuadData QBackingStoreDefaultCompositor::createPerQuadData(QRhiTexture *texture)
+QBackingStoreDefaultCompositor::PerQuadData QBackingStoreDefaultCompositor::createPerQuadData(QRhiTexture *texture, QRhiTexture *textureExtra)
{
PerQuadData d;
@@ -348,47 +342,72 @@ QBackingStoreDefaultCompositor::PerQuadData QBackingStoreDefaultCompositor::crea
d.srb = m_rhi->newShaderResourceBindings();
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf, 0, UBUF_SIZE),
- QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, m_sampler)
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, m_samplerNearest.get())
});
if (!d.srb->create())
qWarning("QBackingStoreDefaultCompositor: Failed to create srb");
-
d.lastUsedTexture = texture;
+ if (textureExtra) {
+ d.srbExtra = m_rhi->newShaderResourceBindings();
+ d.srbExtra->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf, 0, UBUF_SIZE),
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, textureExtra, m_samplerNearest.get())
+ });
+ if (!d.srbExtra->create())
+ qWarning("QBackingStoreDefaultCompositor: Failed to create srb");
+ }
+
+ d.lastUsedTextureExtra = textureExtra;
+
return d;
}
-void QBackingStoreDefaultCompositor::updatePerQuadData(PerQuadData *d, QRhiTexture *texture)
+void QBackingStoreDefaultCompositor::updatePerQuadData(PerQuadData *d, QRhiTexture *texture, QRhiTexture *textureExtra,
+ UpdateQuadDataOptions options)
{
// This whole check-if-texture-ptr-is-different is needed because there is
// nothing saying a QPlatformTextureList cannot return a different
// QRhiTexture* from the same index in a subsequent flush.
- if (d->lastUsedTexture == texture || !d->srb)
+ const QRhiSampler::Filter filter = options.testFlag(NeedsLinearFiltering) ? QRhiSampler::Linear : QRhiSampler::Nearest;
+ if ((d->lastUsedTexture == texture && d->lastUsedFilter == filter) || !d->srb)
return;
+ QRhiSampler *sampler = filter == QRhiSampler::Linear ? m_samplerLinear.get() : m_samplerNearest.get();
d->srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d->ubuf, 0, UBUF_SIZE),
- QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, m_sampler)
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, sampler)
});
d->srb->updateResources(QRhiShaderResourceBindings::BindingsAreSorted);
-
d->lastUsedTexture = texture;
+ d->lastUsedFilter = filter;
+
+ if (textureExtra) {
+ d->srbExtra->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d->ubuf, 0, UBUF_SIZE),
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, textureExtra, sampler)
+ });
+
+ d->srbExtra->updateResources(QRhiShaderResourceBindings::BindingsAreSorted);
+ d->lastUsedTextureExtra = textureExtra;
+ }
}
void QBackingStoreDefaultCompositor::updateUniforms(PerQuadData *d, QRhiResourceUpdateBatch *resourceUpdates,
- const QMatrix4x4 &target, const QMatrix3x3 &source, bool needsRedBlueSwap)
+ const QMatrix4x4 &target, const QMatrix3x3 &source,
+ UpdateUniformOptions options)
{
resourceUpdates->updateDynamicBuffer(d->ubuf, 0, 64, target.constData());
updateMatrix3x3(resourceUpdates, d->ubuf, source);
float opacity = 1.0f;
resourceUpdates->updateDynamicBuffer(d->ubuf, 112, 4, &opacity);
- qint32 swapRedBlue = needsRedBlueSwap ? 1 : 0;
- resourceUpdates->updateDynamicBuffer(d->ubuf, 116, 4, &swapRedBlue);
+ qint32 textureSwizzle = options;
+ resourceUpdates->updateDynamicBuffer(d->ubuf, 116, 4, &textureSwizzle);
}
-void QBackingStoreDefaultCompositor::ensureResources(QRhiSwapChain *swapchain, QRhiResourceUpdateBatch *resourceUpdates)
+void QBackingStoreDefaultCompositor::ensureResources(QRhiResourceUpdateBatch *resourceUpdates, QRhiRenderPassDescriptor *rpDesc)
{
static const float vertexData[] = {
-1, -1, 0, 0, 0,
@@ -400,30 +419,37 @@ void QBackingStoreDefaultCompositor::ensureResources(QRhiSwapChain *swapchain, Q
};
if (!m_vbuf) {
- m_vbuf = m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData));
+ m_vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData)));
if (m_vbuf->create())
- resourceUpdates->uploadStaticBuffer(m_vbuf, vertexData);
+ resourceUpdates->uploadStaticBuffer(m_vbuf.get(), vertexData);
else
qWarning("QBackingStoreDefaultCompositor: Failed to create vertex buffer");
}
- if (!m_sampler) {
- m_sampler = m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
- QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
- if (!m_sampler->create())
- qWarning("QBackingStoreDefaultCompositor: Failed to create sampler");
+ if (!m_samplerNearest) {
+ m_samplerNearest.reset(m_rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
+ QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
+ if (!m_samplerNearest->create())
+ qWarning("QBackingStoreDefaultCompositor: Failed to create sampler (Nearest filtering)");
+ }
+
+ if (!m_samplerLinear) {
+ m_samplerLinear.reset(m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
+ QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
+ if (!m_samplerLinear->create())
+ qWarning("QBackingStoreDefaultCompositor: Failed to create sampler (Linear filtering)");
}
if (!m_widgetQuadData.isValid())
- m_widgetQuadData = createPerQuadData(m_texture);
+ m_widgetQuadData = createPerQuadData(m_texture.get());
QRhiShaderResourceBindings *srb = m_widgetQuadData.srb; // just for the layout
if (!m_psNoBlend)
- m_psNoBlend = createGraphicsPipeline(m_rhi, srb, swapchain, PipelineBlend::None);
+ m_psNoBlend.reset(createGraphicsPipeline(m_rhi, srb, rpDesc, PipelineBlend::None));
if (!m_psBlend)
- m_psBlend = createGraphicsPipeline(m_rhi, srb, swapchain, PipelineBlend::Alpha);
+ m_psBlend.reset(createGraphicsPipeline(m_rhi, srb, rpDesc, PipelineBlend::Alpha));
if (!m_psPremulBlend)
- m_psPremulBlend = createGraphicsPipeline(m_rhi, srb, swapchain, PipelineBlend::PremulAlpha);
+ m_psPremulBlend.reset(createGraphicsPipeline(m_rhi, srb, rpDesc, PipelineBlend::PremulAlpha));
}
QPlatformBackingStore::FlushResult QBackingStoreDefaultCompositor::flush(QPlatformBackingStore *backingStore,
@@ -436,6 +462,9 @@ QPlatformBackingStore::FlushResult QBackingStoreDefaultCompositor::flush(QPlatfo
QPlatformTextureList *textures,
bool translucentBackground)
{
+ if (!rhi)
+ return QPlatformBackingStore::FlushFailed;
+
Q_ASSERT(textures); // may be empty if there are no render-to-texture widgets at all, but null it cannot be
if (!m_rhi) {
@@ -489,9 +518,16 @@ QPlatformBackingStore::FlushResult QBackingStoreDefaultCompositor::flush(QPlatfo
if (!gotTextureFromGraphicsBuffer)
toTexture(backingStore, rhi, resourceUpdates, scaledRegion(region, sourceDevicePixelRatio, offset), &flags);
- ensureResources(swapchain, resourceUpdates);
+ ensureResources(resourceUpdates, swapchain->renderPassDescriptor());
- const bool needsRedBlueSwap = (flags & QPlatformBackingStore::TextureSwizzle) != 0;
+ UpdateUniformOptions uniformOptions;
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ if (flags & QPlatformBackingStore::TextureSwizzle)
+ uniformOptions |= NeedsRedBlueSwap;
+#else
+ if (flags & QPlatformBackingStore::TextureSwizzle)
+ uniformOptions |= NeedsAlphaRotate;
+#endif
const bool premultiplied = (flags & QPlatformBackingStore::TexturePremultiplied) != 0;
SourceTransformOrigin origin = SourceTransformOrigin::TopLeft;
if (flags & QPlatformBackingStore::TextureFlip)
@@ -499,24 +535,33 @@ QPlatformBackingStore::FlushResult QBackingStoreDefaultCompositor::flush(QPlatfo
const qreal dpr = window->devicePixelRatio();
const QRect deviceWindowRect = scaledRect(QRect(QPoint(), window->size()), dpr);
- const QPoint deviceWindowOffset = scaledOffset(offset, dpr);
+ const QRect sourceWindowRect = scaledRect(QRect(QPoint(), window->size()), sourceDevicePixelRatio);
+ // If sourceWindowRect is larger than deviceWindowRect, we are doing high
+ // DPI downscaling. In that case Linear filtering is a must, whereas for the
+ // 1:1 case Nearest must be used for Qt 5 visual compatibility.
+ const bool needsLinearSampler = sourceWindowRect.width() > deviceWindowRect.width()
+ && sourceWindowRect.height() > deviceWindowRect.height();
+
+ const bool invertTargetY = !rhi->isYUpInNDC();
+ const bool invertSource = !rhi->isYUpInFramebuffer();
- const bool invertTargetY = rhi->clipSpaceCorrMatrix().data()[5] < 0.0f;
- const bool invertSource = rhi->isYUpInFramebuffer() != rhi->isYUpInNDC();
if (m_texture) {
- // The backingstore is for the entire tlw.
- // In case of native children offset tells the position relative to the tlw.
- const QRect textureRect = QRect(QPoint(), m_texture->pixelSize());
- const QRect srcRect = toBottomLeftRect(textureRect.translated(deviceWindowOffset), m_texture->pixelSize().height());
+ // The backingstore is for the entire tlw. In case of native children, offset tells the position
+ // relative to the tlw. The window rect is scaled by the source device pixel ratio to get
+ // the source rect.
+ const QPoint sourceWindowOffset = scaledOffset(offset, sourceDevicePixelRatio);
+ const QRect srcRect = toBottomLeftRect(sourceWindowRect.translated(sourceWindowOffset), m_texture->pixelSize().height());
const QMatrix3x3 source = sourceTransform(srcRect, m_texture->pixelSize(), origin);
QMatrix4x4 target; // identity
if (invertTargetY)
target.data()[5] = -1.0f;
- updateUniforms(&m_widgetQuadData, resourceUpdates, target, source, needsRedBlueSwap);
+ updateUniforms(&m_widgetQuadData, resourceUpdates, target, source, uniformOptions);
+ if (needsLinearSampler)
+ updatePerQuadData(&m_widgetQuadData, m_texture.get(), nullptr, NeedsLinearFiltering);
}
const int textureWidgetCount = textures->count();
- const int oldTextureQuadDataCount = m_textureQuadData.count();
+ const int oldTextureQuadDataCount = m_textureQuadData.size();
if (oldTextureQuadDataCount != textureWidgetCount) {
for (int i = textureWidgetCount; i < oldTextureQuadDataCount; ++i)
m_textureQuadData[i].reset();
@@ -524,21 +569,27 @@ QPlatformBackingStore::FlushResult QBackingStoreDefaultCompositor::flush(QPlatfo
}
for (int i = 0; i < textureWidgetCount; ++i) {
+ const bool invertSourceForTextureWidget = textures->flags(i).testFlag(QPlatformTextureList::MirrorVertically)
+ ? !invertSource : invertSource;
QMatrix4x4 target;
QMatrix3x3 source;
if (!prepareDrawForRenderToTextureWidget(textures, i, window, deviceWindowRect,
- offset, invertTargetY, invertSource, &target, &source))
+ offset, invertTargetY, invertSourceForTextureWidget,
+ &target, &source))
{
m_textureQuadData[i].reset();
continue;
}
QRhiTexture *t = textures->texture(i);
+ QRhiTexture *tExtra = textures->textureExtra(i);
if (t) {
if (!m_textureQuadData[i].isValid())
- m_textureQuadData[i] = createPerQuadData(t);
+ m_textureQuadData[i] = createPerQuadData(t, tExtra);
else
- updatePerQuadData(&m_textureQuadData[i], t);
- updateUniforms(&m_textureQuadData[i], resourceUpdates, target, source, false);
+ updatePerQuadData(&m_textureQuadData[i], t, tExtra);
+ updateUniforms(&m_textureQuadData[i], resourceUpdates, target, source);
+ if (needsLinearSampler)
+ updatePerQuadData(&m_textureQuadData[i], t, tExtra, NeedsLinearFiltering);
} else {
m_textureQuadData[i].reset();
}
@@ -548,47 +599,74 @@ QPlatformBackingStore::FlushResult QBackingStoreDefaultCompositor::flush(QPlatfo
QRhiCommandBuffer *cb = swapchain->currentFrameCommandBuffer();
const QSize outputSizeInPixels = swapchain->currentPixelSize();
QColor clearColor = translucentBackground ? Qt::transparent : Qt::black;
- cb->beginPass(swapchain->currentFrameRenderTarget(), clearColor, { 1.0f, 0 }, resourceUpdates);
- cb->setGraphicsPipeline(m_psNoBlend);
- cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
- QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf, 0);
- cb->setVertexInput(0, 1, &vbufBinding);
+ cb->resourceUpdate(resourceUpdates);
- // Textures for renderToTexture widgets.
- for (int i = 0; i < textureWidgetCount; ++i) {
- if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) {
- if (m_textureQuadData[i].isValid()) {
- cb->setShaderResources(m_textureQuadData[i].srb);
- cb->draw(6);
+ auto render = [&](std::optional<QRhiSwapChain::StereoTargetBuffer> buffer = std::nullopt) {
+ QRhiRenderTarget* target = nullptr;
+ if (buffer.has_value())
+ target = swapchain->currentFrameRenderTarget(buffer.value());
+ else
+ target = swapchain->currentFrameRenderTarget();
+
+ cb->beginPass(target, clearColor, { 1.0f, 0 });
+
+ cb->setGraphicsPipeline(m_psNoBlend.get());
+ cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
+ QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf.get(), 0);
+ cb->setVertexInput(0, 1, &vbufBinding);
+
+ // Textures for renderToTexture widgets.
+ for (int i = 0; i < textureWidgetCount; ++i) {
+ if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) {
+ if (m_textureQuadData[i].isValid()) {
+
+ QRhiShaderResourceBindings* srb = m_textureQuadData[i].srb;
+ if (buffer == QRhiSwapChain::RightBuffer && m_textureQuadData[i].srbExtra)
+ srb = m_textureQuadData[i].srbExtra;
+
+ cb->setShaderResources(srb);
+ cb->draw(6);
+ }
}
}
- }
- cb->setGraphicsPipeline(premultiplied ? m_psPremulBlend : m_psBlend);
+ cb->setGraphicsPipeline(premultiplied ? m_psPremulBlend.get() : m_psBlend.get());
- // Backingstore texture with the normal widgets.
- if (m_texture) {
- cb->setShaderResources(m_widgetQuadData.srb);
- cb->draw(6);
- }
+ // Backingstore texture with the normal widgets.
+ if (m_texture) {
+ cb->setShaderResources(m_widgetQuadData.srb);
+ cb->draw(6);
+ }
- // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set.
- for (int i = 0; i < textureWidgetCount; ++i) {
- const QPlatformTextureList::Flags flags = textures->flags(i);
- if (flags.testFlag(QPlatformTextureList::StacksOnTop)) {
- if (m_textureQuadData[i].isValid()) {
- if (flags.testFlag(QPlatformTextureList::NeedsPremultipliedAlphaBlending))
- cb->setGraphicsPipeline(m_psPremulBlend);
- else
- cb->setGraphicsPipeline(m_psBlend);
- cb->setShaderResources(m_textureQuadData[i].srb);
- cb->draw(6);
+ // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set.
+ for (int i = 0; i < textureWidgetCount; ++i) {
+ const QPlatformTextureList::Flags flags = textures->flags(i);
+ if (flags.testFlag(QPlatformTextureList::StacksOnTop)) {
+ if (m_textureQuadData[i].isValid()) {
+ if (flags.testFlag(QPlatformTextureList::NeedsPremultipliedAlphaBlending))
+ cb->setGraphicsPipeline(m_psPremulBlend.get());
+ else
+ cb->setGraphicsPipeline(m_psBlend.get());
+
+ QRhiShaderResourceBindings* srb = m_textureQuadData[i].srb;
+ if (buffer == QRhiSwapChain::RightBuffer && m_textureQuadData[i].srbExtra)
+ srb = m_textureQuadData[i].srbExtra;
+
+ cb->setShaderResources(srb);
+ cb->draw(6);
+ }
}
}
- }
- cb->endPass();
+ cb->endPass();
+ };
+
+ if (swapchain->window()->format().stereo()) {
+ render(QRhiSwapChain::LeftBuffer);
+ render(QRhiSwapChain::RightBuffer);
+ } else
+ render();
rhi->endFrame(swapchain);
diff --git a/src/gui/painting/qbackingstoredefaultcompositor_p.h b/src/gui/painting/qbackingstoredefaultcompositor_p.h
index d3b91674a6..c5a8ffd328 100644
--- a/src/gui/painting/qbackingstoredefaultcompositor_p.h
+++ b/src/gui/painting/qbackingstoredefaultcompositor_p.h
@@ -16,7 +16,7 @@
//
#include <qpa/qplatformbackingstore.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
@@ -44,7 +44,17 @@ public:
bool translucentBackground);
private:
- void ensureResources(QRhiSwapChain *swapchain, QRhiResourceUpdateBatch *resourceUpdates);
+ enum UpdateUniformOption {
+ NeedsRedBlueSwap = 1 << 0,
+ NeedsAlphaRotate = 1 << 1
+ };
+ Q_DECLARE_FLAGS(UpdateUniformOptions, UpdateUniformOption)
+ enum UpdateQuadDataOption {
+ NeedsLinearFiltering = 1 << 0
+ };
+ Q_DECLARE_FLAGS(UpdateQuadDataOptions, UpdateQuadDataOption)
+
+ void ensureResources(QRhiResourceUpdateBatch *resourceUpdates, QRhiRenderPassDescriptor *rpDesc);
QRhiTexture *toTexture(const QImage &image,
QRhi *rhi,
QRhiResourceUpdateBatch *resourceUpdates,
@@ -52,35 +62,47 @@ private:
QPlatformBackingStore::TextureFlags *flags) const;
mutable QRhi *m_rhi = nullptr;
- mutable QRhiTexture *m_texture = nullptr;
+ mutable std::unique_ptr<QRhiTexture> m_texture;
- QRhiBuffer *m_vbuf = nullptr;
- QRhiSampler *m_sampler = nullptr;
- QRhiGraphicsPipeline *m_psNoBlend = nullptr;
- QRhiGraphicsPipeline *m_psBlend = nullptr;
- QRhiGraphicsPipeline *m_psPremulBlend = nullptr;
+ std::unique_ptr<QRhiBuffer> m_vbuf;
+ std::unique_ptr<QRhiSampler> m_samplerNearest;
+ std::unique_ptr<QRhiSampler> m_samplerLinear;
+ std::unique_ptr<QRhiGraphicsPipeline> m_psNoBlend;
+ std::unique_ptr<QRhiGraphicsPipeline> m_psBlend;
+ std::unique_ptr<QRhiGraphicsPipeline> m_psPremulBlend;
struct PerQuadData {
QRhiBuffer *ubuf = nullptr;
// All srbs are layout-compatible.
QRhiShaderResourceBindings *srb = nullptr;
+ QRhiShaderResourceBindings *srbExtra = nullptr; // may be null (used for stereo)
QRhiTexture *lastUsedTexture = nullptr;
+ QRhiTexture *lastUsedTextureExtra = nullptr; // may be null (used for stereo)
+ QRhiSampler::Filter lastUsedFilter = QRhiSampler::None;
bool isValid() const { return ubuf && srb; }
void reset() {
delete ubuf;
ubuf = nullptr;
delete srb;
srb = nullptr;
+ if (srbExtra) {
+ delete srbExtra;
+ srbExtra = nullptr;
+ }
lastUsedTexture = nullptr;
+ lastUsedTextureExtra = nullptr;
+ lastUsedFilter = QRhiSampler::None;
}
};
PerQuadData m_widgetQuadData;
QVarLengthArray<PerQuadData, 8> m_textureQuadData;
- PerQuadData createPerQuadData(QRhiTexture *texture);
- void updatePerQuadData(PerQuadData *d, QRhiTexture *texture);
+ PerQuadData createPerQuadData(QRhiTexture *texture, QRhiTexture *textureExtra = nullptr);
+ void updatePerQuadData(PerQuadData *d, QRhiTexture *texture, QRhiTexture *textureExtra = nullptr,
+ UpdateQuadDataOptions options = {});
void updateUniforms(PerQuadData *d, QRhiResourceUpdateBatch *resourceUpdates,
- const QMatrix4x4 &target, const QMatrix3x3 &source, bool needsRedBlueSwap);
+ const QMatrix4x4 &target, const QMatrix3x3 &source,
+ UpdateUniformOptions options = {});
};
QT_END_NAMESPACE
diff --git a/src/gui/painting/qbackingstorerhisupport.cpp b/src/gui/painting/qbackingstorerhisupport.cpp
index 81b3f0eb7d..fe5589dc2d 100644
--- a/src/gui/painting/qbackingstorerhisupport.cpp
+++ b/src/gui/painting/qbackingstorerhisupport.cpp
@@ -8,19 +8,9 @@
#if QT_CONFIG(opengl)
#include <QtGui/qoffscreensurface.h>
#include <QtGui/private/qopenglcontext_p.h>
-#include <QtGui/private/qrhigles2_p.h>
-#endif
-
-#ifdef Q_OS_WIN
-#include <QtGui/private/qrhid3d11_p.h>
-#endif
-
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
-#include <QtGui/private/qrhimetal_p.h>
#endif
#if QT_CONFIG(vulkan)
-#include <QtGui/private/qrhivulkan_p.h>
#include <QtGui/private/qvulkandefaultinstance_p.h>
#endif
@@ -66,6 +56,19 @@ bool QBackingStoreRhiSupport::create()
QOffscreenSurface *surface = nullptr;
QRhi::Flags flags;
+ // These must be the same env.vars Qt Quick uses (as documented), in order
+ // to ensure symmetry in the behavior between a QQuickWindow and a
+ // (QRhi-based) widget top-level window.
+ if (qEnvironmentVariableIntValue("QSG_RHI_PREFER_SOFTWARE_RENDERER"))
+ flags |= QRhi::PreferSoftwareRenderer;
+ if (qEnvironmentVariableIntValue("QSG_RHI_PROFILE"))
+ flags |= QRhi::EnableDebugMarkers | QRhi::EnableTimestamps;
+
+ if (m_config.api() == QPlatformBackingStoreRhiConfig::Null) {
+ QRhiNullInitParams params;
+ rhi = QRhi::create(QRhi::Null, &params, flags);
+ }
+
#if QT_CONFIG(opengl)
if (!rhi && m_config.api() == QPlatformBackingStoreRhiConfig::OpenGL) {
surface = QRhiGles2InitParams::newFallbackSurface(m_format);
@@ -79,14 +82,32 @@ bool QBackingStoreRhiSupport::create()
#endif
#ifdef Q_OS_WIN
- if (!rhi && m_config.api() == QPlatformBackingStoreRhiConfig::D3D11) {
- QRhiD3D11InitParams params;
- params.enableDebugLayer = m_config.isDebugLayerEnabled();
- rhi = QRhi::create(QRhi::D3D11, &params, flags);
+ if (!rhi) {
+ if (m_config.api() == QPlatformBackingStoreRhiConfig::D3D11) {
+ QRhiD3D11InitParams params;
+ params.enableDebugLayer = m_config.isDebugLayerEnabled();
+ rhi = QRhi::create(QRhi::D3D11, &params, flags);
+ if (!rhi && !flags.testFlag(QRhi::PreferSoftwareRenderer)) {
+ qCDebug(lcQpaBackingStore, "Failed to create a D3D11 device with default settings; "
+ "attempting to get a software rasterizer backed device instead");
+ flags |= QRhi::PreferSoftwareRenderer;
+ rhi = QRhi::create(QRhi::D3D11, &params, flags);
+ }
+ } else if (m_config.api() == QPlatformBackingStoreRhiConfig::D3D12) {
+ QRhiD3D12InitParams params;
+ params.enableDebugLayer = m_config.isDebugLayerEnabled();
+ rhi = QRhi::create(QRhi::D3D12, &params, flags);
+ if (!rhi && !flags.testFlag(QRhi::PreferSoftwareRenderer)) {
+ qCDebug(lcQpaBackingStore, "Failed to create a D3D12 device with default settings; "
+ "attempting to get a software rasterizer backed device instead");
+ flags |= QRhi::PreferSoftwareRenderer;
+ rhi = QRhi::create(QRhi::D3D12, &params, flags);
+ }
+ }
}
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
if (!rhi && m_config.api() == QPlatformBackingStoreRhiConfig::Metal) {
QRhiMetalInitParams params;
// For parity with Qt Quick, fall back to OpenGL when there is no Metal (f.ex. in macOS virtual machines).
@@ -116,7 +137,6 @@ bool QBackingStoreRhiSupport::create()
return false;
}
params.window = m_window;
- params.deviceExtensions = m_config.deviceExtensions();
rhi = QRhi::create(QRhi::Vulkan, &params, flags);
}
#endif
@@ -176,13 +196,14 @@ QRhiSwapChain *QBackingStoreRhiSupport::swapChainForWindow(QWindow *window)
bool QBackingStoreRhiSupportWindowWatcher::eventFilter(QObject *obj, QEvent *event)
{
- if (event->type() == QEvent::PlatformSurface
- && static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed)
+ if (event->type() == QEvent::WindowAboutToChangeInternal
+ || (event->type() == QEvent::PlatformSurface
+ && static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed))
{
QWindow *window = qobject_cast<QWindow *>(obj);
auto it = m_rhiSupport->m_swapchains.find(window);
if (it != m_rhiSupport->m_swapchains.end()) {
- qCDebug(lcQpaBackingStore) << "SurfaceAboutToBeDestroyed received for tracked window" << window << "cleaning up swapchain";
+ qCDebug(lcQpaBackingStore) << event << "received for" << window << "- cleaning up swapchain";
auto data = *it;
m_rhiSupport->m_swapchains.erase(it);
data.reset(); // deletes 'this'
@@ -196,6 +217,7 @@ QSurface::SurfaceType QBackingStoreRhiSupport::surfaceTypeForConfig(const QPlatf
QSurface::SurfaceType type = QSurface::RasterSurface;
switch (config.api()) {
case QPlatformBackingStoreRhiConfig::D3D11:
+ case QPlatformBackingStoreRhiConfig::D3D12:
type = QSurface::Direct3DSurface;
break;
case QPlatformBackingStoreRhiConfig::Vulkan:
@@ -224,6 +246,8 @@ QRhi::Implementation QBackingStoreRhiSupport::apiToRhiBackend(QPlatformBackingSt
return QRhi::Vulkan;
case QPlatformBackingStoreRhiConfig::D3D11:
return QRhi::D3D11;
+ case QPlatformBackingStoreRhiConfig::D3D12:
+ return QRhi::D3D12;
case QPlatformBackingStoreRhiConfig::Null:
return QRhi::Null;
default:
@@ -232,25 +256,6 @@ QRhi::Implementation QBackingStoreRhiSupport::apiToRhiBackend(QPlatformBackingSt
return QRhi::Null;
}
-const char *QBackingStoreRhiSupport::apiName(QPlatformBackingStoreRhiConfig::Api api)
-{
- switch (api) {
- case QPlatformBackingStoreRhiConfig::OpenGL:
- return "OpenGL";
- case QPlatformBackingStoreRhiConfig::Metal:
- return "Metal";
- case QPlatformBackingStoreRhiConfig::Vulkan:
- return "Vulkan";
- case QPlatformBackingStoreRhiConfig::D3D11:
- return "D3D11";
- case QPlatformBackingStoreRhiConfig::Null:
- return "Null";
- default:
- break;
- }
- return "Unknown";
-}
-
bool QBackingStoreRhiSupport::checkForceRhi(QPlatformBackingStoreRhiConfig *outConfig, QSurface::SurfaceType *outType)
{
static QPlatformBackingStoreRhiConfig config;
@@ -267,7 +272,7 @@ bool QBackingStoreRhiSupport::checkForceRhi(QPlatformBackingStoreRhiConfig *outC
if (config.isEnabled()) {
#if defined(Q_OS_WIN)
config.setApi(QPlatformBackingStoreRhiConfig::D3D11);
-#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#elif QT_CONFIG(metal)
config.setApi(QPlatformBackingStoreRhiConfig::Metal);
#elif QT_CONFIG(opengl)
config.setApi(QPlatformBackingStoreRhiConfig::OpenGL);
@@ -284,8 +289,10 @@ bool QBackingStoreRhiSupport::checkForceRhi(QPlatformBackingStoreRhiConfig *outC
#ifdef Q_OS_WIN
if (backend == QStringLiteral("d3d11") || backend == QStringLiteral("d3d"))
config.setApi(QPlatformBackingStoreRhiConfig::D3D11);
+ if (backend == QStringLiteral("d3d12"))
+ config.setApi(QPlatformBackingStoreRhiConfig::D3D12);
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
if (backend == QStringLiteral("metal"))
config.setApi(QPlatformBackingStoreRhiConfig::Metal);
#endif
@@ -304,7 +311,7 @@ bool QBackingStoreRhiSupport::checkForceRhi(QPlatformBackingStoreRhiConfig *outC
}
qCDebug(lcQpaBackingStore) << "Check for forced use of QRhi resulted in enable"
- << config.isEnabled() << "with api" << apiName(config.api());
+ << config.isEnabled() << "with api" << QRhi::backendName(apiToRhiBackend(config.api()));
}
if (config.isEnabled()) {
diff --git a/src/gui/painting/qbackingstorerhisupport_p.h b/src/gui/painting/qbackingstorerhisupport_p.h
index c67ea6dd6a..39ce36c680 100644
--- a/src/gui/painting/qbackingstorerhisupport_p.h
+++ b/src/gui/painting/qbackingstorerhisupport_p.h
@@ -19,7 +19,7 @@
#include <QtGui/qwindow.h>
#include <QtGui/qsurfaceformat.h>
#include <QtGui/qoffscreensurface.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <qpa/qplatformbackingstore.h>
QT_BEGIN_NAMESPACE
@@ -44,7 +44,6 @@ public:
static bool checkForceRhi(QPlatformBackingStoreRhiConfig *outConfig, QSurface::SurfaceType *outType);
static QRhi::Implementation apiToRhiBackend(QPlatformBackingStoreRhiConfig::Api api);
- static const char *apiName(QPlatformBackingStoreRhiConfig::Api api);
QRhi *rhi() const { return m_rhi; }
diff --git a/src/gui/painting/qbezier_p.h b/src/gui/painting/qbezier_p.h
index 373a35b2be..6c39dd55f9 100644
--- a/src/gui/painting/qbezier_p.h
+++ b/src/gui/painting/qbezier_p.h
@@ -18,7 +18,6 @@
#include <QtGui/private/qtguiglobal_p.h>
#include "QtCore/qline.h"
#include "QtCore/qlist.h"
-#include "QtCore/qpair.h"
#include "QtCore/qpoint.h"
#include "QtCore/qrect.h"
#include "QtGui/qtransform.h"
diff --git a/src/gui/painting/qblendfunctions.cpp b/src/gui/painting/qblendfunctions.cpp
index 2a51d06204..fa9f89262c 100644
--- a/src/gui/painting/qblendfunctions.cpp
+++ b/src/gui/painting/qblendfunctions.cpp
@@ -152,7 +152,7 @@ void qt_blend_rgb16_on_rgb16(uchar *dst, int dbpl,
if (const_alpha == 256) {
int length = w << 1;
- while (h--) {
+ while (--h >= 0) {
memcpy(dst, src, length);
dst += dbpl;
src += sbpl;
@@ -162,7 +162,7 @@ void qt_blend_rgb16_on_rgb16(uchar *dst, int dbpl,
const quint16 *s = (const quint16 *) src;
quint8 a = (255 * const_alpha) >> 8;
quint8 ia = 255 - a;
- while (h--) {
+ while (--h >= 0) {
for (int x=0; x<w; ++x) {
d[x] = BYTE_MUL_RGB16(s[x], a) + BYTE_MUL_RGB16(d[x], ia);
}
diff --git a/src/gui/painting/qblendfunctions_p.h b/src/gui/painting/qblendfunctions_p.h
index 90a7dbcf64..2f5ed36c73 100644
--- a/src/gui/painting/qblendfunctions_p.h
+++ b/src/gui/painting/qblendfunctions_p.h
@@ -89,7 +89,7 @@ void qt_scale_image_16bit(uchar *destPixels, int dbpl,
if (xend < 0 || xend >= (int)(sbpl/sizeof(SRC)))
--w;
- while (h--) {
+ while (--h >= 0) {
const SRC *src = (const SRC *) (srcPixels + (srcy >> 16) * sbpl);
quint32 srcx = basex;
int x = 0;
@@ -180,7 +180,7 @@ template <typename T> void qt_scale_image_32bit(uchar *destPixels, int dbpl,
if (xend < 0 || xend >= (int)(sbpl/sizeof(quint32)))
--w;
- while (h--) {
+ while (--h >= 0) {
const uint *src = (const quint32 *) (srcPixels + (srcy >> 16) * sbpl);
quint32 srcx = basex;
int x = 0;
diff --git a/src/gui/painting/qcmyk_p.h b/src/gui/painting/qcmyk_p.h
new file mode 100644
index 0000000000..d00a4b5a6e
--- /dev/null
+++ b/src/gui/painting/qcmyk_p.h
@@ -0,0 +1,88 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCMYK_P_H
+#define QCMYK_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 <QtGui/private/qtguiglobal_p.h>
+#include <QtGui/qcolor.h>
+
+QT_BEGIN_NAMESPACE
+
+class QCmyk32
+{
+private:
+ uint m_cmyk = 0;
+
+public:
+ QCmyk32() = default;
+
+ constexpr QCmyk32(int cyan, int magenta, int yellow, int black) :
+#if QT_BYTE_ORDER == Q_BIG_ENDIAN
+ m_cmyk(cyan << 24 | magenta << 16 | yellow << 8 | black)
+#else
+ m_cmyk(cyan | magenta << 8 | yellow << 16 | black << 24)
+#endif
+ {
+ }
+
+#if QT_BYTE_ORDER == Q_BIG_ENDIAN
+ constexpr int cyan() const noexcept { return (m_cmyk >> 24) & 0xff; }
+ constexpr int magenta() const noexcept { return (m_cmyk >> 16) & 0xff; }
+ constexpr int yellow() const noexcept { return (m_cmyk >> 8) & 0xff; }
+ constexpr int black() const noexcept { return (m_cmyk ) & 0xff; }
+#else
+ constexpr int cyan() const noexcept { return (m_cmyk ) & 0xff; }
+ constexpr int magenta() const noexcept { return (m_cmyk >> 8) & 0xff; }
+ constexpr int yellow() const noexcept { return (m_cmyk >> 16) & 0xff; }
+ constexpr int black() const noexcept { return (m_cmyk >> 24) & 0xff; }
+#endif
+
+ QColor toColor() const noexcept
+ {
+ return QColor::fromCmyk(cyan(), magenta(), yellow(), black());
+ }
+
+ constexpr uint toUint() const noexcept
+ {
+ return m_cmyk;
+ }
+
+ constexpr static QCmyk32 fromCmyk32(uint cmyk) noexcept
+ {
+ QCmyk32 result;
+ result.m_cmyk = cmyk;
+ return result;
+ }
+
+ static QCmyk32 fromRgba(QRgb rgba) noexcept
+ {
+ const QColor c = QColor(rgba).toCmyk();
+ return QCmyk32(c.cyan(), c.magenta(), c.yellow(), c.black());
+ }
+
+ static QCmyk32 fromColor(const QColor &color) noexcept
+ {
+ QColor c = color.toCmyk();
+ return QCmyk32(c.cyan(), c.magenta(), c.yellow(), c.black());
+ }
+};
+
+static_assert(sizeof(QCmyk32) == sizeof(int));
+static_assert(alignof(QCmyk32) == alignof(int));
+static_assert(std::is_standard_layout_v<QCmyk32>);
+
+QT_END_NAMESPACE
+
+#endif // QCMYK_P_H
diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp
index 4a04eb3907..2af2e8cb6b 100644
--- a/src/gui/painting/qcolor.cpp
+++ b/src/gui/painting/qcolor.cpp
@@ -784,12 +784,10 @@ QColor::QColor(Spec spec) noexcept
}
}
-#if QT_DEPRECATED_SINCE(6, 6)
+// ### Qt 7: remove those after deprecating them for the last Qt 6 LTS release
/*!
\fn QColor::QColor(const QString &name)
- \deprecated [6.6] Use fromString() instead.
-
Constructs a named color in the same way as setNamedColor() using
the given \a name.
@@ -801,8 +799,6 @@ QColor::QColor(Spec spec) noexcept
/*!
\fn QColor::QColor(const char *name)
- \deprecated [6.6] Use fromString() instead.
-
Constructs a named color in the same way as setNamedColor() using
the given \a name.
@@ -813,8 +809,6 @@ QColor::QColor(Spec spec) noexcept
/*!
\fn QColor::QColor(QLatin1StringView name)
- \deprecated [6.6] Use fromString() instead.
-
Constructs a named color in the same way as setNamedColor() using
the given \a name.
@@ -822,7 +816,6 @@ QColor::QColor(Spec spec) noexcept
\since 5.8
\sa setNamedColor(), name(), isValid()
*/
-#endif // QT_DEPRECATED_SINCE(6, 6)
/*!
\fn bool QColor::isValid() const
@@ -1490,7 +1483,7 @@ void QColor::setAlpha(int alpha)
QCOLOR_INT_RANGE_CHECK("QColor::setAlpha", alpha);
if (cspec == ExtendedRgb) {
constexpr float f = 1.0f / 255;
- castF16(ct.argbExtended.alphaF16) = alpha * f;
+ castF16(ct.argbExtended.alphaF16) = qfloat16(alpha * f);
return;
}
ct.argb.alpha = alpha * 0x101;
@@ -1519,7 +1512,7 @@ void QColor::setAlphaF(float alpha)
{
QCOLOR_REAL_RANGE_CHECK("QColor::setAlphaF", alpha);
if (cspec == ExtendedRgb) {
- castF16(ct.argbExtended.alphaF16) = alpha;
+ castF16(ct.argbExtended.alphaF16) = qfloat16(alpha);
return;
}
float tmp = alpha * USHRT_MAX;
@@ -1637,7 +1630,7 @@ void QColor::setRedF(float red)
if (cspec == Rgb && red >= 0.0f && red <= 1.0f)
ct.argb.red = qRound(red * USHRT_MAX);
else if (cspec == ExtendedRgb)
- castF16(ct.argbExtended.redF16) = red;
+ castF16(ct.argbExtended.redF16) = qfloat16(red);
else
setRgbF(red, greenF(), blueF(), alphaF());
}
@@ -1669,7 +1662,7 @@ void QColor::setGreenF(float green)
if (cspec == Rgb && green >= 0.0f && green <= 1.0f)
ct.argb.green = qRound(green * USHRT_MAX);
else if (cspec == ExtendedRgb)
- castF16(ct.argbExtended.greenF16) = green;
+ castF16(ct.argbExtended.greenF16) = qfloat16(green);
else
setRgbF(redF(), green, blueF(), alphaF());
}
@@ -1699,7 +1692,7 @@ void QColor::setBlueF(float blue)
if (cspec == Rgb && blue >= 0.0f && blue <= 1.0f)
ct.argb.blue = qRound(blue * USHRT_MAX);
else if (cspec == ExtendedRgb)
- castF16(ct.argbExtended.blueF16) = blue;
+ castF16(ct.argbExtended.blueF16) = qfloat16(blue);
else
setRgbF(redF(), greenF(), blue, alphaF());
}
diff --git a/src/gui/painting/qcolor.h b/src/gui/painting/qcolor.h
index ba2621968b..2d6db1a14c 100644
--- a/src/gui/painting/qcolor.h
+++ b/src/gui/painting/qcolor.h
@@ -47,16 +47,10 @@ public:
0) {}
QColor(QRgb rgb) noexcept;
QColor(QRgba64 rgba64) noexcept;
-#if QT_DEPRECATED_SINCE(6, 6)
- QT_DEPRECATED_VERSION_X_6_6("Use QColor::fromString() instead.")
inline QColor(const QString& name);
- QT_DEPRECATED_VERSION_X_6_6("Use QColor::fromString() instead.")
explicit inline QColor(QStringView name);
- QT_DEPRECATED_VERSION_X_6_6("Use QColor::fromString() instead.")
inline QColor(const char *aname);
- QT_DEPRECATED_VERSION_X_6_6("Use QColor::fromString() instead.")
inline QColor(QLatin1StringView name);
-#endif
QColor(Spec spec) noexcept;
static QColor fromString(QAnyStringView name) noexcept;
@@ -276,7 +270,6 @@ public: // can't give friendship to a namespace, so it needs to be public
};
Q_DECLARE_TYPEINFO(QColor, Q_RELOCATABLE_TYPE);
-#if QT_DEPRECATED_SINCE(6, 6)
inline QColor::QColor(QLatin1StringView aname)
: QColor(fromString(aname)) {}
@@ -288,7 +281,6 @@ inline QColor::QColor(const QString& aname)
inline QColor::QColor(const char *aname)
: QColor(fromString(aname)) {}
-#endif
inline bool QColor::isValid() const noexcept
{ return cspec != Invalid; }
diff --git a/src/gui/painting/qcolorclut_p.h b/src/gui/painting/qcolorclut_p.h
new file mode 100644
index 0000000000..d95e9701b7
--- /dev/null
+++ b/src/gui/painting/qcolorclut_p.h
@@ -0,0 +1,127 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCOLORCLUT_H
+#define QCOLORCLUT_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 <QtCore/qlist.h>
+#include <QtGui/private/qcolormatrix_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// A 3/4-dimensional lookup table compatible with ICC lut8, lut16, mAB, and mBA formats.
+class QColorCLUT
+{
+ inline static QColorVector interpolate(const QColorVector &a, const QColorVector &b, float t)
+ {
+ return a + (b - a) * t; // faster than std::lerp by assuming no super large or non-number floats
+ }
+ inline static void interpolateIn(QColorVector &a, const QColorVector &b, float t)
+ {
+ a += (b - a) * t;
+ }
+public:
+ uint32_t gridPointsX = 0;
+ uint32_t gridPointsY = 0;
+ uint32_t gridPointsZ = 0;
+ uint32_t gridPointsW = 1;
+ QList<QColorVector> table;
+
+ bool isEmpty() const { return table.isEmpty(); }
+
+ QColorVector apply(const QColorVector &v) const
+ {
+ Q_ASSERT(table.size() == gridPointsX * gridPointsY * gridPointsZ * gridPointsW);
+ QColorVector frac;
+ const float x = std::clamp(v.x, 0.0f, 1.0f) * (gridPointsX - 1);
+ const float y = std::clamp(v.y, 0.0f, 1.0f) * (gridPointsY - 1);
+ const float z = std::clamp(v.z, 0.0f, 1.0f) * (gridPointsZ - 1);
+ const float w = std::clamp(v.w, 0.0f, 1.0f) * (gridPointsW - 1);
+ const uint32_t lox = static_cast<uint32_t>(std::floor(x));
+ const uint32_t hix = std::min(lox + 1, gridPointsX - 1);
+ const uint32_t loy = static_cast<uint32_t>(std::floor(y));
+ const uint32_t hiy = std::min(loy + 1, gridPointsY - 1);
+ const uint32_t loz = static_cast<uint32_t>(std::floor(z));
+ const uint32_t hiz = std::min(loz + 1, gridPointsZ - 1);
+ const uint32_t low = static_cast<uint32_t>(std::floor(w));
+ const uint32_t hiw = std::min(low + 1, gridPointsW - 1);
+ frac.x = x - static_cast<float>(lox);
+ frac.y = y - static_cast<float>(loy);
+ frac.z = z - static_cast<float>(loz);
+ frac.w = w - static_cast<float>(low);
+ if (gridPointsW > 1) {
+ auto index = [&](qsizetype x, qsizetype y, qsizetype z, qsizetype w) -> qsizetype {
+ return x * gridPointsW * gridPointsZ * gridPointsY
+ + y * gridPointsW * gridPointsZ
+ + z * gridPointsW
+ + w;
+ };
+ QColorVector tmp[8];
+ // interpolate over w
+ tmp[0] = interpolate(table[index(lox, loy, loz, low)],
+ table[index(lox, loy, loz, hiw)], frac.w);
+ tmp[1] = interpolate(table[index(lox, loy, hiz, low)],
+ table[index(lox, loy, hiz, hiw)], frac.w);
+ tmp[2] = interpolate(table[index(lox, hiy, loz, low)],
+ table[index(lox, hiy, loz, hiw)], frac.w);
+ tmp[3] = interpolate(table[index(lox, hiy, hiz, low)],
+ table[index(lox, hiy, hiz, hiw)], frac.w);
+ tmp[4] = interpolate(table[index(hix, loy, loz, low)],
+ table[index(hix, loy, loz, hiw)], frac.w);
+ tmp[5] = interpolate(table[index(hix, loy, hiz, low)],
+ table[index(hix, loy, hiz, hiw)], frac.w);
+ tmp[6] = interpolate(table[index(hix, hiy, loz, low)],
+ table[index(hix, hiy, loz, hiw)], frac.w);
+ tmp[7] = interpolate(table[index(hix, hiy, hiz, low)],
+ table[index(hix, hiy, hiz, hiw)], frac.w);
+ // interpolate over z
+ for (int i = 0; i < 4; ++i)
+ interpolateIn(tmp[i * 2], tmp[i * 2 + 1], frac.z);
+ // interpolate over y
+ for (int i = 0; i < 2; ++i)
+ interpolateIn(tmp[i * 4], tmp[i * 4 + 2], frac.y);
+ // interpolate over x
+ interpolateIn(tmp[0], tmp[4], frac.x);
+ return tmp[0];
+ }
+ auto index = [&](qsizetype x, qsizetype y, qsizetype z) -> qsizetype {
+ return x * gridPointsZ * gridPointsY
+ + y * gridPointsZ
+ + z;
+ };
+ QColorVector tmp[8] = {
+ table[index(lox, loy, loz)],
+ table[index(lox, loy, hiz)],
+ table[index(lox, hiy, loz)],
+ table[index(lox, hiy, hiz)],
+ table[index(hix, loy, loz)],
+ table[index(hix, loy, hiz)],
+ table[index(hix, hiy, loz)],
+ table[index(hix, hiy, hiz)]
+ };
+ // interpolate over z
+ for (int i = 0; i < 4; ++i)
+ interpolateIn(tmp[i * 2], tmp[i * 2 + 1], frac.z);
+ // interpolate over y
+ for (int i = 0; i < 2; ++i)
+ interpolateIn(tmp[i * 4], tmp[i * 4 + 2], frac.y);
+ // interpolate over x
+ interpolateIn(tmp[0], tmp[4], frac.x);
+ return tmp[0];
+ }
+};
+
+QT_END_NAMESPACE
+
+#endif // QCOLORCLUT_H
diff --git a/src/gui/painting/qcolormatrix_p.h b/src/gui/painting/qcolormatrix_p.h
index 1ce5df6264..6a9b9f4f9a 100644
--- a/src/gui/painting/qcolormatrix_p.h
+++ b/src/gui/painting/qcolormatrix_p.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QCOLORMATRIX_H
@@ -18,6 +18,7 @@
#include <QtGui/qtguiglobal.h>
#include <QtCore/qpoint.h>
#include <QtCore/private/qglobal_p.h>
+#include <QtCore/private/qsimd_p.h>
#include <cmath>
QT_BEGIN_NAMESPACE
@@ -27,25 +28,24 @@ class QColorVector
{
public:
QColorVector() = default;
- constexpr QColorVector(float x, float y, float z) : x(x), y(y), z(z) { }
- explicit constexpr QColorVector(const QPointF &chr) // from XY chromaticity
- : x(chr.x() / chr.y())
- , y(1.0f)
- , z((1.0 - chr.x() - chr.y()) / chr.y())
- { }
- float x = 0.0f; // X, x or red
- float y = 0.0f; // Y, y or green
- float z = 0.0f; // Z, Y or blue
- float _unused = 0.0f;
+ constexpr QColorVector(float x, float y, float z, float w = 0.0f) noexcept : x(x), y(y), z(z), w(w) { }
+ static constexpr QColorVector fromXYChromaticity(QPointF chr)
+ { return {float(chr.x() / chr.y()), 1.0f, float((1.0f - chr.x() - chr.y()) / chr.y())}; }
+ float x = 0.0f; // X, x, L, or red/cyan
+ float y = 0.0f; // Y, y, a, or green/magenta
+ float z = 0.0f; // Z, Y, b, or blue/yellow
+ float w = 0.0f; // unused, or black
- friend inline bool operator==(const QColorVector &v1, const QColorVector &v2);
- friend inline bool operator!=(const QColorVector &v1, const QColorVector &v2);
- bool isNull() const
+ constexpr bool isNull() const noexcept
{
- return !x && !y && !z;
+ return !x && !y && !z && !w;
+ }
+ bool isValid() const noexcept
+ {
+ return std::isfinite(x) && std::isfinite(y) && std::isfinite(z);
}
- static bool isValidChromaticity(const QPointF &chr)
+ static constexpr bool isValidChromaticity(const QPointF &chr)
{
if (chr.x() < qreal(0.0) || chr.x() > qreal(1.0))
return false;
@@ -56,26 +56,149 @@ public:
return true;
}
+ constexpr QColorVector operator*(float f) const { return QColorVector(x * f, y * f, z * f, w * f); }
+ constexpr QColorVector operator+(const QColorVector &v) const { return QColorVector(x + v.x, y + v.y, z + v.z, w + v.w); }
+ constexpr QColorVector operator-(const QColorVector &v) const { return QColorVector(x - v.x, y - v.y, z - v.z, w - v.w); }
+ void operator+=(const QColorVector &v) { x += v.x; y += v.y; z += v.z; w += v.w; }
+
+ QPointF toChromaticity() const
+ {
+ if (isNull())
+ return QPointF();
+ float mag = 1.0f / (x + y + z);
+ return QPointF(x * mag, y * mag);
+ }
+
// Common whitepoints:
static constexpr QPointF D50Chromaticity() { return QPointF(0.34567, 0.35850); }
static constexpr QPointF D65Chromaticity() { return QPointF(0.31271, 0.32902); }
- static constexpr QColorVector D50() { return QColorVector(D50Chromaticity()); }
- static constexpr QColorVector D65() { return QColorVector(D65Chromaticity()); }
+ static constexpr QColorVector D50() { return fromXYChromaticity(D50Chromaticity()); }
+ static constexpr QColorVector D65() { return fromXYChromaticity(D65Chromaticity()); }
+
+ QColorVector xyzToLab() const
+ {
+ constexpr QColorVector ref = D50();
+ constexpr float eps = 0.008856f;
+ constexpr float kap = 903.3f;
+#if defined(__SSE2__)
+ const __m128 iref = _mm_setr_ps(1.f / ref.x, 1.f / ref.y, 1.f / ref.z, 0.f);
+ __m128 v = _mm_loadu_ps(&x);
+ v = _mm_mul_ps(v, iref);
+
+ const __m128 f3 = _mm_set1_ps(3.f);
+ __m128 est = _mm_add_ps(_mm_set1_ps(0.25f), _mm_mul_ps(v, _mm_set1_ps(0.75f))); // float est = 0.25f + (x * 0.75f);
+ __m128 estsq = _mm_mul_ps(est, est);
+ est = _mm_sub_ps(est, _mm_mul_ps(_mm_sub_ps(_mm_mul_ps(estsq, est), v),
+ _mm_rcp_ps(_mm_mul_ps(estsq, f3)))); // est -= ((est * est * est) - x) / (3.f * (est * est));
+ estsq = _mm_mul_ps(est, est);
+ est = _mm_sub_ps(est, _mm_mul_ps(_mm_sub_ps(_mm_mul_ps(estsq, est), v),
+ _mm_rcp_ps(_mm_mul_ps(estsq, f3)))); // est -= ((est * est * est) - x) / (3.f * (est * est));
+ estsq = _mm_mul_ps(est, est);
+ est = _mm_sub_ps(est, _mm_mul_ps(_mm_sub_ps(_mm_mul_ps(estsq, est), v),
+ _mm_rcp_ps(_mm_mul_ps(estsq, f3)))); // est -= ((est * est * est) - x) / (3.f * (est * est));
+ estsq = _mm_mul_ps(est, est);
+ est = _mm_sub_ps(est, _mm_mul_ps(_mm_sub_ps(_mm_mul_ps(estsq, est), v),
+ _mm_rcp_ps(_mm_mul_ps(estsq, f3)))); // est -= ((est * est * est) - x) / (3.f * (est * est));
+
+ __m128 kapmul = _mm_mul_ps(_mm_add_ps(_mm_mul_ps(v, _mm_set1_ps(kap)), _mm_set1_ps(16.f)),
+ _mm_set1_ps(1.f / 116.f)); // f_ = (kap * f_ + 16.f) * (1.f / 116.f);
+ __m128 cmpgt = _mm_cmpgt_ps(v, _mm_set1_ps(eps)); // if (f_ > eps)
+#if defined(__SSE4_1__)
+ v = _mm_blendv_ps(kapmul, est, cmpgt); // if (..) f_ =.. else f_ =..
+#else
+ v = _mm_or_ps(_mm_and_ps(cmpgt, est), _mm_andnot_ps(cmpgt, kapmul));
+#endif
+ alignas(16) float out[4];
+ _mm_store_ps(out, v);
+ const float L = 116.f * out[1] - 16.f;
+ const float a = 500.f * (out[0] - out[1]);
+ const float b = 200.f * (out[1] - out[2]);
+#else
+ float xr = x * (1.f / ref.x);
+ float yr = y * (1.f / ref.y);
+ float zr = z * (1.f / ref.z);
+
+ float fx, fy, fz;
+ if (xr > eps)
+ fx = fastCbrt(xr);
+ else
+ fx = (kap * xr + 16.f) * (1.f / 116.f);
+ if (yr > eps)
+ fy = fastCbrt(yr);
+ else
+ fy = (kap * yr + 16.f) * (1.f / 116.f);
+ if (zr > eps)
+ fz = fastCbrt(zr);
+ else
+ fz = (kap * zr + 16.f) * (1.f / 116.f);
+
+ const float L = 116.f * fy - 16.f;
+ const float a = 500.f * (fx - fy);
+ const float b = 200.f * (fy - fz);
+#endif
+ // We output Lab values that has been scaled to 0.0->1.0 values, see also labToXyz.
+ return QColorVector(L * (1.f / 100.f), (a + 128.f) * (1.f / 255.f), (b + 128.f) * (1.f / 255.f));
+ }
+
+ QColorVector labToXyz() const
+ {
+ constexpr QColorVector ref = D50();
+ constexpr float eps = 0.008856f;
+ constexpr float kap = 903.3f;
+ // This transform has been guessed from the ICC spec, but it is not stated
+ // anywhere to be the one to use to map to and from 0.0->1.0 values:
+ const float L = x * 100.f;
+ const float a = (y * 255.f) - 128.f;
+ const float b = (z * 255.f) - 128.f;
+ // From here is official Lab->XYZ conversion:
+ float fy = (L + 16.f) * (1.f / 116.f);
+ float fx = fy + (a * (1.f / 500.f));
+ float fz = fy - (b * (1.f / 200.f));
+
+ float xr, yr, zr;
+ if (fx * fx * fx > eps)
+ xr = fx * fx * fx;
+ else
+ xr = (116.f * fx - 16) * (1.f / kap);
+ if (L > (kap * eps))
+ yr = fy * fy * fy;
+ else
+ yr = L * (1.f / kap);
+ if (fz * fz * fz > eps)
+ zr = fz * fz * fz;
+ else
+ zr = (116.f * fz - 16) * (1.f / kap);
+
+ xr = xr * ref.x;
+ yr = yr * ref.y;
+ zr = zr * ref.z;
+ return QColorVector(xr, yr, zr);
+ }
+ friend inline bool comparesEqual(const QColorVector &lhs, const QColorVector &rhs);
+ Q_DECLARE_EQUALITY_COMPARABLE(QColorVector);
+
+private:
+ static float fastCbrt(float x)
+ {
+ // This gives us cube root within the precision we need.
+ float est = 0.25f + (x * 0.75f); // guessing a cube-root of numbers between 0.01 and 1.
+ est -= ((est * est * est) - x) / (3.f * (est * est));
+ est -= ((est * est * est) - x) / (3.f * (est * est));
+ est -= ((est * est * est) - x) / (3.f * (est * est));
+ est -= ((est * est * est) - x) / (3.f * (est * est));
+ // Q_ASSERT(qAbs(est - std::cbrt(x)) < 0.0001f);
+ return est;
+ }
};
-inline bool operator==(const QColorVector &v1, const QColorVector &v2)
+inline bool comparesEqual(const QColorVector &v1, const QColorVector &v2)
{
return (std::abs(v1.x - v2.x) < (1.0f / 2048.0f))
&& (std::abs(v1.y - v2.y) < (1.0f / 2048.0f))
- && (std::abs(v1.z - v2.z) < (1.0f / 2048.0f));
+ && (std::abs(v1.z - v2.z) < (1.0f / 2048.0f))
+ && (std::abs(v1.w - v2.w) < (1.0f / 2048.0f));
}
-inline bool operator!=(const QColorVector &v1, const QColorVector &v2)
-{
- return !(v1 == v2);
-}
-
-
// A matrix mapping 3 value colors.
// Not using QTransform because only floats are needed and performance is critical.
class QColorMatrix
@@ -86,20 +209,20 @@ public:
QColorVector g;
QColorVector b;
- friend inline bool operator==(const QColorMatrix &m1, const QColorMatrix &m2);
- friend inline bool operator!=(const QColorMatrix &m1, const QColorMatrix &m2);
-
- bool isNull() const
+ constexpr bool isNull() const
{
return r.isNull() && g.isNull() && b.isNull();
}
+ constexpr float determinant() const
+ {
+ return r.x * (b.z * g.y - g.z * b.y) -
+ r.y * (b.z * g.x - g.z * b.x) +
+ r.z * (b.y * g.x - g.y * b.x);
+ }
bool isValid() const
{
// A color matrix must be invertible
- float det = r.x * (b.z * g.y - g.z * b.y) -
- r.y * (b.z * g.x - g.z * b.x) +
- r.z * (b.y * g.x - g.y * b.x);
- return !qFuzzyIsNull(det);
+ return std::isnormal(determinant());
}
bool isIdentity() const noexcept
{
@@ -108,9 +231,7 @@ public:
QColorMatrix inverted() const
{
- float det = r.x * (b.z * g.y - g.z * b.y) -
- r.y * (b.z * g.x - g.z * b.x) +
- r.z * (b.y * g.x - g.y * b.x);
+ float det = determinant();
det = 1.0f / det;
QColorMatrix inv;
inv.r.x = (g.y * b.z - b.y * g.z) * det;
@@ -124,20 +245,20 @@ public:
inv.b.z = (r.x * g.y - g.x * r.y) * det;
return inv;
}
- QColorMatrix operator*(const QColorMatrix &o) const
+ friend inline constexpr QColorMatrix operator*(const QColorMatrix &a, const QColorMatrix &o)
{
QColorMatrix comb;
- comb.r.x = r.x * o.r.x + g.x * o.r.y + b.x * o.r.z;
- comb.g.x = r.x * o.g.x + g.x * o.g.y + b.x * o.g.z;
- comb.b.x = r.x * o.b.x + g.x * o.b.y + b.x * o.b.z;
+ comb.r.x = a.r.x * o.r.x + a.g.x * o.r.y + a.b.x * o.r.z;
+ comb.g.x = a.r.x * o.g.x + a.g.x * o.g.y + a.b.x * o.g.z;
+ comb.b.x = a.r.x * o.b.x + a.g.x * o.b.y + a.b.x * o.b.z;
- comb.r.y = r.y * o.r.x + g.y * o.r.y + b.y * o.r.z;
- comb.g.y = r.y * o.g.x + g.y * o.g.y + b.y * o.g.z;
- comb.b.y = r.y * o.b.x + g.y * o.b.y + b.y * o.b.z;
+ comb.r.y = a.r.y * o.r.x + a.g.y * o.r.y + a.b.y * o.r.z;
+ comb.g.y = a.r.y * o.g.x + a.g.y * o.g.y + a.b.y * o.g.z;
+ comb.b.y = a.r.y * o.b.x + a.g.y * o.b.y + a.b.y * o.b.z;
- comb.r.z = r.z * o.r.x + g.z * o.r.y + b.z * o.r.z;
- comb.g.z = r.z * o.g.x + g.z * o.g.y + b.z * o.g.z;
- comb.b.z = r.z * o.b.x + g.z * o.b.y + b.z * o.b.z;
+ comb.r.z = a.r.z * o.r.x + a.g.z * o.r.y + a.b.z * o.r.z;
+ comb.g.z = a.r.z * o.g.x + a.g.z * o.g.y + a.b.z * o.g.z;
+ comb.b.z = a.r.z * o.b.x + a.g.z * o.b.y + a.b.z * o.b.z;
return comb;
}
@@ -164,6 +285,32 @@ public:
{ 0.0f, v.y, 0.0f },
{ 0.0f, 0.0f, v.z } };
}
+ static QColorMatrix chromaticAdaptation(const QColorVector &whitePoint)
+ {
+ constexpr QColorVector whitePointD50 = QColorVector::D50();
+ if (whitePoint != whitePointD50) {
+ // A chromatic adaptation to map a white point to XYZ D50.
+
+ // The Bradford method chromatic adaptation matrix:
+ const QColorMatrix abrad = { { 0.8951f, -0.7502f, 0.0389f },
+ { 0.2664f, 1.7135f, -0.0685f },
+ { -0.1614f, 0.0367f, 1.0296f } };
+ const QColorMatrix abradinv = { { 0.9869929f, 0.4323053f, -0.0085287f },
+ { -0.1470543f, 0.5183603f, 0.0400428f },
+ { 0.1599627f, 0.0492912f, 0.9684867f } };
+
+ const QColorVector srcCone = abrad.map(whitePoint);
+ if (srcCone.x && srcCone.y && srcCone.z) {
+ const QColorVector dstCone = abrad.map(whitePointD50);
+ const QColorMatrix wToD50 = { { dstCone.x / srcCone.x, 0, 0 },
+ { 0, dstCone.y / srcCone.y, 0 },
+ { 0, 0, dstCone.z / srcCone.z } };
+ return abradinv * (wToD50 * abrad);
+ }
+ }
+ return QColorMatrix::identity();
+ }
+
// These are used to recognize matrices from ICC profiles:
static QColorMatrix toXyzFromSRgb()
{
@@ -189,18 +336,15 @@ public:
{ 0.1351922452f, 0.7118769884f, 0.0000000000f },
{ 0.0313525312f, 0.0000856627f, 0.8251883388f } };
}
+ friend inline bool comparesEqual(const QColorMatrix &lhs, const QColorMatrix &rhs);
+ Q_DECLARE_EQUALITY_COMPARABLE(QColorMatrix);
};
-inline bool operator==(const QColorMatrix &m1, const QColorMatrix &m2)
+inline bool comparesEqual(const QColorMatrix &m1, const QColorMatrix &m2)
{
return (m1.r == m2.r) && (m1.g == m2.g) && (m1.b == m2.b);
}
-inline bool operator!=(const QColorMatrix &m1, const QColorMatrix &m2)
-{
- return !(m1 == m2);
-}
-
QT_END_NAMESPACE
#endif // QCOLORMATRIX_P_H
diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp
index 7fdfe34872..7a1d34a408 100644
--- a/src/gui/painting/qcolorspace.cpp
+++ b/src/gui/painting/qcolorspace.cpp
@@ -5,6 +5,7 @@
#include "qcolorspace_p.h"
#include "qcolortransform.h"
+#include "qcolorclut_p.h"
#include "qcolormatrix_p.h"
#include "qcolortransferfunction_p.h"
#include "qcolortransform_p.h"
@@ -20,7 +21,7 @@ QT_BEGIN_NAMESPACE
Q_CONSTINIT QBasicMutex QColorSpacePrivate::s_lutWriteLock;
-static QAtomicPointer<QColorSpacePrivate> s_predefinedColorspacePrivates[QColorSpace::ProPhotoRgb] = {};
+Q_CONSTINIT static QAtomicPointer<QColorSpacePrivate> s_predefinedColorspacePrivates[QColorSpace::ProPhotoRgb] = {};
static void cleanupPredefinedColorspaces()
{
for (QAtomicPointer<QColorSpacePrivate> &ptr : s_predefinedColorspacePrivates) {
@@ -80,49 +81,19 @@ bool QColorSpacePrimaries::areValid() const
QColorMatrix QColorSpacePrimaries::toXyzMatrix() const
{
// This converts to XYZ in some undefined scale.
- QColorMatrix toXyz = { QColorVector(redPoint),
- QColorVector(greenPoint),
- QColorVector(bluePoint) };
+ QColorMatrix toXyz = { QColorVector::fromXYChromaticity(redPoint),
+ QColorVector::fromXYChromaticity(greenPoint),
+ QColorVector::fromXYChromaticity(bluePoint) };
// Since the white point should be (1.0, 1.0, 1.0) in the
// input, we can figure out the scale by using the
// inverse conversion on the white point.
- QColorVector wXyz(whitePoint);
+ const auto wXyz = QColorVector::fromXYChromaticity(whitePoint);
QColorVector whiteScale = toXyz.inverted().map(wXyz);
// Now we have scaled conversion to XYZ relative to the given whitepoint
toXyz = toXyz * QColorMatrix::fromScale(whiteScale);
- // But we want a conversion to XYZ relative to D50
- QColorVector wXyzD50 = QColorVector::D50();
-
- if (wXyz != wXyzD50) {
- // Do chromatic adaptation to map our white point to XYZ D50.
-
- // The Bradford method chromatic adaptation matrix:
- QColorMatrix abrad = { { 0.8951f, -0.7502f, 0.0389f },
- { 0.2664f, 1.7135f, -0.0685f },
- { -0.1614f, 0.0367f, 1.0296f } };
- QColorMatrix abradinv = { { 0.9869929f, 0.4323053f, -0.0085287f },
- { -0.1470543f, 0.5183603f, 0.0400428f },
- { 0.1599627f, 0.0492912f, 0.9684867f } };
-
- QColorVector srcCone = abrad.map(wXyz);
- QColorVector dstCone = abrad.map(wXyzD50);
-
- if (srcCone.x && srcCone.y && srcCone.z) {
- QColorMatrix wToD50 = { { dstCone.x / srcCone.x, 0, 0 },
- { 0, dstCone.y / srcCone.y, 0 },
- { 0, 0, dstCone.z / srcCone.z } };
-
-
- QColorMatrix chromaticAdaptation = abradinv * (wToD50 * abrad);
- toXyz = chromaticAdaptation * toXyz;
- } else {
- toXyz.r = {0, 0, 0}; // set to invalid value
- }
- }
-
return toXyz;
}
@@ -132,6 +103,7 @@ QColorSpacePrivate::QColorSpacePrivate()
QColorSpacePrivate::QColorSpacePrivate(QColorSpace::NamedColorSpace namedColorSpace)
: namedColorSpace(namedColorSpace)
+ , colorModel(QColorSpace::ColorModel::Rgb)
{
switch (namedColorSpace) {
case QColorSpace::SRgb:
@@ -169,6 +141,7 @@ QColorSpacePrivate::QColorSpacePrivate(QColorSpace::NamedColorSpace namedColorSp
QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction transferFunction, float gamma)
: primaries(primaries)
, transferFunction(transferFunction)
+ , colorModel(QColorSpace::ColorModel::Rgb)
, gamma(gamma)
{
identifyColorSpace();
@@ -180,18 +153,50 @@ QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries,
float gamma)
: primaries(QColorSpace::Primaries::Custom)
, transferFunction(transferFunction)
+ , colorModel(QColorSpace::ColorModel::Rgb)
, gamma(gamma)
+ , whitePoint(QColorVector::fromXYChromaticity(primaries.whitePoint))
{
Q_ASSERT(primaries.areValid());
toXyz = primaries.toXyzMatrix();
- whitePoint = QColorVector(primaries.whitePoint);
+ chad = QColorMatrix::chromaticAdaptation(whitePoint);
+ toXyz = chad * toXyz;
+
identifyColorSpace();
setTransferFunction();
}
+QColorSpacePrivate::QColorSpacePrivate(const QPointF &whitePoint,
+ QColorSpace::TransferFunction transferFunction,
+ float gamma)
+ : primaries(QColorSpace::Primaries::Custom)
+ , transferFunction(transferFunction)
+ , colorModel(QColorSpace::ColorModel::Gray)
+ , gamma(gamma)
+ , whitePoint(QColorVector::fromXYChromaticity(whitePoint))
+{
+ chad = QColorMatrix::chromaticAdaptation(this->whitePoint);
+ toXyz = chad;
+ setTransferFunction();
+}
+
+QColorSpacePrivate::QColorSpacePrivate(const QPointF &whitePoint, const QList<uint16_t> &transferFunctionTable)
+ : primaries(QColorSpace::Primaries::Custom)
+ , transferFunction(QColorSpace::TransferFunction::Custom)
+ , colorModel(QColorSpace::ColorModel::Gray)
+ , gamma(0)
+ , whitePoint(QColorVector::fromXYChromaticity(whitePoint))
+{
+ chad = QColorMatrix::chromaticAdaptation(this->whitePoint);
+ toXyz = chad;
+ setTransferFunctionTable(transferFunctionTable);
+ setTransferFunction();
+}
+
QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, const QList<uint16_t> &transferFunctionTable)
: primaries(primaries)
, transferFunction(QColorSpace::TransferFunction::Custom)
+ , colorModel(QColorSpace::ColorModel::Rgb)
, gamma(0)
{
setTransferFunctionTable(transferFunctionTable);
@@ -202,11 +207,14 @@ QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, const Q
QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, const QList<uint16_t> &transferFunctionTable)
: primaries(QColorSpace::Primaries::Custom)
, transferFunction(QColorSpace::TransferFunction::Custom)
+ , colorModel(QColorSpace::ColorModel::Rgb)
, gamma(0)
+ , whitePoint(QColorVector::fromXYChromaticity(primaries.whitePoint))
{
Q_ASSERT(primaries.areValid());
toXyz = primaries.toXyzMatrix();
- whitePoint = QColorVector(primaries.whitePoint);
+ chad = QColorMatrix::chromaticAdaptation(whitePoint);
+ toXyz = chad * toXyz;
setTransferFunctionTable(transferFunctionTable);
identifyColorSpace();
initialize();
@@ -218,16 +226,18 @@ QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries,
const QList<uint16_t> &blueTransferFunctionTable)
: primaries(QColorSpace::Primaries::Custom)
, transferFunction(QColorSpace::TransferFunction::Custom)
+ , colorModel(QColorSpace::ColorModel::Rgb)
, gamma(0)
{
Q_ASSERT(primaries.areValid());
toXyz = primaries.toXyzMatrix();
- whitePoint = QColorVector(primaries.whitePoint);
+ whitePoint = QColorVector::fromXYChromaticity(primaries.whitePoint);
+ chad = QColorMatrix::chromaticAdaptation(whitePoint);
+ toXyz = chad * toXyz;
setTransferFunctionTables(redTransferFunctionTable,
greenTransferFunctionTable,
blueTransferFunctionTable);
identifyColorSpace();
- setToXyzMatrix();
}
void QColorSpacePrivate::identifyColorSpace()
@@ -304,7 +314,9 @@ void QColorSpacePrivate::setToXyzMatrix()
}
QColorSpacePrimaries colorSpacePrimaries(primaries);
toXyz = colorSpacePrimaries.toXyzMatrix();
- whitePoint = QColorVector(colorSpacePrimaries.whitePoint);
+ whitePoint = QColorVector::fromXYChromaticity(colorSpacePrimaries.whitePoint);
+ chad = QColorMatrix::chromaticAdaptation(whitePoint);
+ toXyz = chad * toXyz;
}
void QColorSpacePrivate::setTransferFunctionTable(const QList<uint16_t> &transferFunctionTable)
@@ -319,7 +331,7 @@ void QColorSpacePrivate::setTransferFunctionTable(const QList<uint16_t> &transfe
QColorTransferFunction curve;
if (table.asColorTransferFunction(&curve)) {
// Table recognized as a specific curve
- if (curve.isLinear()) {
+ if (curve.isIdentity()) {
transferFunction = QColorSpace::TransferFunction::Linear;
gamma = 1.0f;
} else if (curve.isSRgb()) {
@@ -418,7 +430,12 @@ QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpace
combined.d = ptr;
ptr->colorSpaceIn = this;
ptr->colorSpaceOut = out;
- ptr->colorMatrix = out->toXyz.inverted() * toXyz;
+ if (isThreeComponentMatrix())
+ ptr->colorMatrix = toXyz;
+ else
+ ptr->colorMatrix = QColorMatrix::identity();
+ if (out->isThreeComponentMatrix())
+ ptr->colorMatrix = out->toXyz.inverted() * ptr->colorMatrix;
if (ptr->isIdentity())
return QColorTransform();
return combined;
@@ -431,10 +448,32 @@ QColorTransform QColorSpacePrivate::transformationToXYZ() const
transform.d = ptr;
ptr->colorSpaceIn = this;
ptr->colorSpaceOut = this;
- ptr->colorMatrix = toXyz;
+ // Convert to XYZ relative to our white point, not the regular D50 white point.
+ if (isThreeComponentMatrix())
+ ptr->colorMatrix = QColorMatrix::chromaticAdaptation(whitePoint).inverted() * toXyz;
+ else
+ ptr->colorMatrix = QColorMatrix::identity();
return transform;
}
+bool QColorSpacePrivate::isThreeComponentMatrix() const
+{
+ return transformModel == QColorSpace::TransformModel::ThreeComponentMatrix;
+}
+
+void QColorSpacePrivate::clearElementListProcessingForEdit()
+{
+ Q_ASSERT(transformModel == QColorSpace::TransformModel::ElementListProcessing);
+ Q_ASSERT(primaries == QColorSpace::Primaries::Custom);
+ Q_ASSERT(transferFunction == QColorSpace::TransferFunction::Custom);
+
+ transformModel = QColorSpace::TransformModel::ThreeComponentMatrix;
+ colorModel = QColorSpace::ColorModel::Rgb;
+ isPcsLab = false;
+ mAB.clear();
+ mBA.clear();
+}
+
/*!
\class QColorSpace
\brief The QColorSpace class provides a color space abstraction.
@@ -457,9 +496,10 @@ QColorTransform QColorSpacePrivate::transformationToXYZ() const
A color space can generally speaking be conceived as a combination of set of primary
colors and a transfer function. The primaries defines the axes of the color space, and
the transfer function how values are mapped on the axes.
- The primaries are defined by three primary colors that represent exactly how red, green,
- and blue look in this particular color space, and a white color that represents where
- and how bright pure white is. The range of colors expressible by the primary colors is
+ The primaries are for ColorModel::Rgb color spaces defined by three primary colors that
+ represent exactly how red, green, and blue look in this particular color space, and a white
+ color that represents where and how bright pure white is. For grayscale color spaces, only
+ a single white primary is needed. The range of colors expressible by the primary colors is
called the gamut, and a color space that can represent a wider range of colors is also
known as a wide-gamut color space.
@@ -513,6 +553,35 @@ QColorTransform QColorSpacePrivate::transformationToXYZ() const
*/
/*!
+ \enum QColorSpace::TransformModel
+ \since 6.8
+
+ Defines the processing model used for color space transforms.
+
+ \value ThreeComponentMatrix The transform consist of a matrix calculated from primaries and set of transfer functions
+ for each color channel. This is very fast and used by all predefined color spaces. Any color space on this form is
+ reversible and always both valid sources and targets.
+ \value ElementListProcessing The transforms are one or two lists of processing elements that can do many things,
+ each list only process either to the connection color space or from it. This is very flexible, but rather
+ slow, and can only be set by reading ICC profiles (See \l fromIccProfile()). Since the two lists are
+ separate a color space on this form can be a valid source, but not necessarily also a valid target. When changing
+ either primaries or transfer function on a color space on this type it will reset to an empty ThreeComponentMatrix form.
+*/
+
+/*!
+ \enum QColorSpace::ColorModel
+ \since 6.8
+
+ Defines the color model used by the color space data.
+
+ \value Undefined No color model
+ \value Rgb An RGB color model with red, green, and blue colors. Can apply to RGB and grayscale data.
+ \value Gray A gray scale color model. Can only apply to grayscale data.
+ \value Cmyk Can only represent color data defined with cyan, magenta, yellow, and black colors.
+ In effect only QImage::Format_CMYK32. Note Cmyk color spaces will be TransformModel::ElementListProcessing.
+*/
+
+/*!
\fn QColorSpace::QColorSpace()
Creates a new colorspace object that represents an undefined and invalid colorspace.
@@ -575,6 +644,28 @@ QColorSpace::QColorSpace(QColorSpace::Primaries gamut, const QList<uint16_t> &tr
}
/*!
+ Creates a custom grayscale color space with the white point \a whitePoint, using the transfer function \a transferFunction and
+ optionally \a gamma.
+
+ \since 6.8
+*/
+QColorSpace::QColorSpace(const QPointF &whitePoint, TransferFunction transferFunction, float gamma)
+ : d_ptr(new QColorSpacePrivate(whitePoint, transferFunction, gamma))
+{
+}
+
+/*!
+ Creates a custom grayscale color space with white point \a whitePoint, and using the custom transfer function described by
+ \a transferFunctionTable.
+
+ \since 6.8
+*/
+QColorSpace::QColorSpace(const QPointF &whitePoint, const QList<uint16_t> &transferFunctionTable)
+ : d_ptr(new QColorSpacePrivate(whitePoint, transferFunctionTable))
+{
+}
+
+/*!
Creates a custom colorspace with a primaries based on the chromaticities of the primary colors \a whitePoint,
\a redPoint, \a greenPoint and \a bluePoint, and using the transfer function \a transferFunction and optionally \a gamma.
*/
@@ -689,7 +780,10 @@ void QColorSpace::setTransferFunction(QColorSpace::TransferFunction transferFunc
if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma)
return;
detach();
- d_ptr->description.clear();
+ if (d_ptr->transformModel == TransformModel::ElementListProcessing)
+ d_ptr->clearElementListProcessingForEdit();
+ d_ptr->iccProfile = {};
+ d_ptr->description = QString();
d_ptr->transferFunction = transferFunction;
d_ptr->gamma = gamma;
d_ptr->identifyColorSpace();
@@ -710,7 +804,10 @@ void QColorSpace::setTransferFunction(const QList<uint16_t> &transferFunctionTab
return;
}
detach();
- d_ptr->description.clear();
+ if (d_ptr->transformModel == TransformModel::ElementListProcessing)
+ d_ptr->clearElementListProcessingForEdit();
+ d_ptr->iccProfile = {};
+ d_ptr->description = QString();
d_ptr->setTransferFunctionTable(transferFunctionTable);
d_ptr->gamma = 0;
d_ptr->identifyColorSpace();
@@ -737,7 +834,10 @@ void QColorSpace::setTransferFunctions(const QList<uint16_t> &redTransferFunctio
return;
}
detach();
- d_ptr->description.clear();
+ if (d_ptr->transformModel == TransformModel::ElementListProcessing)
+ d_ptr->clearElementListProcessingForEdit();
+ d_ptr->iccProfile = {};
+ d_ptr->description = QString();
d_ptr->setTransferFunctionTables(redTransferFunctionTable,
greenTransferFunctionTable,
blueTransferFunctionTable);
@@ -753,7 +853,7 @@ void QColorSpace::setTransferFunctions(const QList<uint16_t> &redTransferFunctio
*/
QColorSpace QColorSpace::withTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma) const
{
- if (!isValid() || transferFunction == QColorSpace::TransferFunction::Custom)
+ if (!isValid() || transferFunction == TransferFunction::Custom)
return *this;
if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma)
return *this;
@@ -813,8 +913,12 @@ void QColorSpace::setPrimaries(QColorSpace::Primaries primariesId)
if (d_ptr->primaries == primariesId)
return;
detach();
- d_ptr->description.clear();
+ if (d_ptr->transformModel == TransformModel::ElementListProcessing)
+ d_ptr->clearElementListProcessingForEdit();
+ d_ptr->iccProfile = {};
+ d_ptr->description = QString();
d_ptr->primaries = primariesId;
+ d_ptr->colorModel = QColorSpace::ColorModel::Rgb;
d_ptr->identifyColorSpace();
d_ptr->setToXyzMatrix();
}
@@ -836,17 +940,100 @@ void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoin
return;
}
QColorMatrix toXyz = primaries.toXyzMatrix();
- if (QColorVector(primaries.whitePoint) == d_ptr->whitePoint && toXyz == d_ptr->toXyz)
+ QColorMatrix chad = QColorMatrix::chromaticAdaptation(QColorVector::fromXYChromaticity(whitePoint));
+ toXyz = chad * toXyz;
+ if (QColorVector::fromXYChromaticity(primaries.whitePoint) == d_ptr->whitePoint
+ && toXyz == d_ptr->toXyz && chad == d_ptr->chad)
return;
detach();
- d_ptr->description.clear();
+ if (d_ptr->transformModel == TransformModel::ElementListProcessing)
+ d_ptr->clearElementListProcessingForEdit();
+ d_ptr->iccProfile = {};
+ d_ptr->description = QString();
d_ptr->primaries = QColorSpace::Primaries::Custom;
+ d_ptr->colorModel = QColorSpace::ColorModel::Rgb;
d_ptr->toXyz = toXyz;
- d_ptr->whitePoint = QColorVector(primaries.whitePoint);
+ d_ptr->chad = chad;
+ d_ptr->whitePoint = QColorVector::fromXYChromaticity(primaries.whitePoint);
+ d_ptr->identifyColorSpace();
+}
+
+/*!
+ Returns the white point used for this color space. Returns a null QPointF if not defined.
+
+ \since 6.8
+*/
+QPointF QColorSpace::whitePoint() const
+{
+ if (Q_UNLIKELY(!d_ptr))
+ return QPointF();
+ return d_ptr->whitePoint.toChromaticity();
+}
+
+/*!
+ Sets the white point to used for this color space to \a whitePoint.
+
+ \since 6.8
+*/
+void QColorSpace::setWhitePoint(const QPointF &whitePoint)
+{
+ if (Q_UNLIKELY(!d_ptr)) {
+ d_ptr = new QColorSpacePrivate(whitePoint, TransferFunction::Custom, 0.0f);
+ return;
+ }
+ if (QColorVector::fromXYChromaticity(whitePoint) == d_ptr->whitePoint)
+ return;
+ detach();
+ if (d_ptr->transformModel == TransformModel::ElementListProcessing)
+ d_ptr->clearElementListProcessingForEdit();
+ d_ptr->iccProfile = {};
+ d_ptr->description = QString();
+ d_ptr->primaries = QColorSpace::Primaries::Custom;
+ // An RGB color model stays RGB, a gray stays gray, but an undefined one can now be considered gray
+ if (d_ptr->colorModel == QColorSpace::ColorModel::Undefined)
+ d_ptr->colorModel = QColorSpace::ColorModel::Gray;
+ QColorVector wXyz(QColorVector::fromXYChromaticity(whitePoint));
+ if (d_ptr->transformModel == QColorSpace::TransformModel::ThreeComponentMatrix) {
+ if (d_ptr->colorModel == QColorSpace::ColorModel::Rgb) {
+ // Rescale toXyz to new whitepoint
+ QColorMatrix rawToXyz = d_ptr->chad.inverted() * d_ptr->toXyz;
+ QColorVector whiteScale = rawToXyz.inverted().map(wXyz);
+ rawToXyz = rawToXyz * QColorMatrix::fromScale(whiteScale);
+ d_ptr->chad = QColorMatrix::chromaticAdaptation(wXyz);
+ d_ptr->toXyz = d_ptr->chad * rawToXyz;
+ } else if (d_ptr->colorModel == QColorSpace::ColorModel::Gray) {
+ d_ptr->chad = d_ptr->toXyz = QColorMatrix::chromaticAdaptation(wXyz);
+ }
+ }
+ d_ptr->whitePoint = wXyz;
d_ptr->identifyColorSpace();
}
/*!
+ Returns the transfrom processing model used for this color space.
+
+ \since 6.8
+*/
+QColorSpace::TransformModel QColorSpace::transformModel() const noexcept
+{
+ if (Q_UNLIKELY(!d_ptr))
+ return QColorSpace::TransformModel::ThreeComponentMatrix;
+ return d_ptr->transformModel;
+}
+
+/*!
+ Returns the color model this color space can represent
+
+ \since 6.8
+*/
+QColorSpace::ColorModel QColorSpace::colorModel() const noexcept
+{
+ if (Q_UNLIKELY(!d_ptr))
+ return QColorSpace::ColorModel::Undefined;
+ return d_ptr->colorModel;
+}
+
+/*!
\internal
*/
void QColorSpace::detach()
@@ -884,7 +1071,7 @@ QByteArray QColorSpace::iccProfile() const
Creates a QColorSpace from ICC profile \a iccProfile.
\note Not all ICC profiles are supported. QColorSpace only supports
- RGB-XYZ ICC profiles that are three-component matrix-based.
+ RGB or Gray ICC profiles.
If the ICC profile is not supported an invalid QColorSpace is returned
where you can still read the original ICC profile using iccProfile().
@@ -902,13 +1089,51 @@ QColorSpace QColorSpace::fromIccProfile(const QByteArray &iccProfile)
}
/*!
- Returns \c true if the color space is valid.
+ Returns \c true if the color space is valid. For a color space with \c TransformModel::ThreeComponentMatrix
+ that means both primaries and transfer functions set, and implies isValidTarget().
+ For a color space with \c TransformModel::ElementListProcessing it means it has a valid source transform, to
+ check if it also a valid target color space use isValidTarget().
+
+ \sa isValidTarget()
*/
bool QColorSpace::isValid() const noexcept
{
- return d_ptr
- && d_ptr->toXyz.isValid()
- && d_ptr->trc[0].isValid() && d_ptr->trc[1].isValid() && d_ptr->trc[2].isValid();
+ if (!d_ptr)
+ return false;
+ return d_ptr->isValid();
+}
+
+/*!
+ \since 6.8
+
+ Returns \c true if the color space is a valid target color space.
+*/
+bool QColorSpace::isValidTarget() const noexcept
+{
+ if (!d_ptr)
+ return false;
+ if (!d_ptr->isThreeComponentMatrix())
+ return !d_ptr->mBA.isEmpty();
+ return d_ptr->isValid();
+}
+
+/*!
+ \internal
+*/
+bool QColorSpacePrivate::isValid() const noexcept
+{
+ if (!isThreeComponentMatrix())
+ return !mAB.isEmpty();
+ if (!toXyz.isValid())
+ return false;
+ if (colorModel == QColorSpace::ColorModel::Gray) {
+ if (!trc[0].isValid())
+ return false;
+ } else {
+ if (!trc[0].isValid() || !trc[1].isValid() || !trc[2].isValid())
+ return false;
+ }
+ return true;
}
/*!
@@ -925,6 +1150,53 @@ bool QColorSpace::isValid() const noexcept
otherwise returns \c false
*/
+static bool compareElement(const QColorSpacePrivate::TransferElement &element,
+ const QColorSpacePrivate::TransferElement &other)
+{
+ return element.trc[0] == other.trc[0]
+ && element.trc[1] == other.trc[1]
+ && element.trc[2] == other.trc[2]
+ && element.trc[3] == other.trc[3];
+}
+
+static bool compareElement(const QColorMatrix &element,
+ const QColorMatrix &other)
+{
+ return element == other;
+}
+
+static bool compareElement(const QColorVector &element,
+ const QColorVector &other)
+{
+ return element == other;
+}
+
+static bool compareElement(const QColorCLUT &element,
+ const QColorCLUT &other)
+{
+ if (element.gridPointsX != other.gridPointsX)
+ return false;
+ if (element.gridPointsY != other.gridPointsY)
+ return false;
+ if (element.gridPointsZ != other.gridPointsZ)
+ return false;
+ if (element.gridPointsW != other.gridPointsW)
+ return false;
+ if (element.table.size() != other.table.size())
+ return false;
+ for (qsizetype i = 0; i < element.table.size(); ++i) {
+ if (element.table[i] != other.table[i])
+ return false;
+ }
+ return true;
+}
+
+template<typename T>
+static bool compareElements(const T &element, const QColorSpacePrivate::Element &other)
+{
+ return compareElement(element, std::get<T>(other));
+}
+
/*!
\internal
*/
@@ -932,43 +1204,89 @@ bool QColorSpace::equals(const QColorSpace &other) const
{
if (d_ptr == other.d_ptr)
return true;
- if (!d_ptr || !other.d_ptr)
+ if (!d_ptr)
return false;
+ return d_ptr->equals(other.d_ptr.constData());
+}
- if (d_ptr->namedColorSpace && other.d_ptr->namedColorSpace)
- return d_ptr->namedColorSpace == other.d_ptr->namedColorSpace;
+/*!
+ \internal
+*/
+bool QColorSpacePrivate::equals(const QColorSpacePrivate *other) const
+{
+ if (!other)
+ return false;
+
+ if (namedColorSpace && other->namedColorSpace)
+ return namedColorSpace == other->namedColorSpace;
const bool valid1 = isValid();
- const bool valid2 = other.isValid();
+ const bool valid2 = other->isValid();
if (valid1 != valid2)
return false;
if (!valid1 && !valid2) {
- if (!d_ptr->iccProfile.isEmpty() || !other.d_ptr->iccProfile.isEmpty())
- return d_ptr->iccProfile == other.d_ptr->iccProfile;
+ if (!iccProfile.isEmpty() || !other->iccProfile.isEmpty())
+ return iccProfile == other->iccProfile;
+ return false;
}
// At this point one or both color spaces are unknown, and must be compared in detail instead
- if (primaries() != QColorSpace::Primaries::Custom && other.primaries() != QColorSpace::Primaries::Custom) {
- if (primaries() != other.primaries())
+ if (transformModel != other->transformModel)
+ return false;
+
+ if (!isThreeComponentMatrix()) {
+ if (isPcsLab != other->isPcsLab)
+ return false;
+ if (colorModel != other->colorModel)
+ return false;
+ if (mAB.count() != other->mAB.count())
+ return false;
+ if (mBA.count() != other->mBA.count())
+ return false;
+
+ // Compare element types
+ for (qsizetype i = 0; i < mAB.count(); ++i) {
+ if (mAB[i].index() != other->mAB[i].index())
+ return false;
+ }
+ for (qsizetype i = 0; i < mBA.count(); ++i) {
+ if (mBA[i].index() != other->mBA[i].index())
+ return false;
+ }
+
+ // Compare element contents
+ for (qsizetype i = 0; i < mAB.count(); ++i) {
+ if (!std::visit([&](auto &&elm) { return compareElements(elm, other->mAB[i]); }, mAB[i]))
+ return false;
+ }
+ for (qsizetype i = 0; i < mBA.count(); ++i) {
+ if (!std::visit([&](auto &&elm) { return compareElements(elm, other->mBA[i]); }, mBA[i]))
+ return false;
+ }
+
+ return true;
+ }
+
+ if (primaries != QColorSpace::Primaries::Custom && other->primaries != QColorSpace::Primaries::Custom) {
+ if (primaries != other->primaries)
return false;
} else {
- if (d_ptr->toXyz != other.d_ptr->toXyz)
+ if (toXyz != other->toXyz)
return false;
}
- if (transferFunction() != QColorSpace::TransferFunction::Custom &&
- other.transferFunction() != QColorSpace::TransferFunction::Custom) {
- if (transferFunction() != other.transferFunction())
+ if (transferFunction != QColorSpace::TransferFunction::Custom && other->transferFunction != QColorSpace::TransferFunction::Custom) {
+ if (transferFunction != other->transferFunction)
return false;
- if (transferFunction() == QColorSpace::TransferFunction::Gamma)
- return (qAbs(gamma() - other.gamma()) <= (1.0f / 512.0f));
+ if (transferFunction == QColorSpace::TransferFunction::Gamma)
+ return (qAbs(gamma - other->gamma) <= (1.0f / 512.0f));
return true;
}
- if (d_ptr->trc[0] != other.d_ptr->trc[0] ||
- d_ptr->trc[1] != other.d_ptr->trc[1] ||
- d_ptr->trc[2] != other.d_ptr->trc[2])
+ if (trc[0] != other->trc[0] ||
+ trc[1] != other->trc[1] ||
+ trc[2] != other->trc[2])
return false;
return true;
@@ -980,11 +1298,15 @@ bool QColorSpace::equals(const QColorSpace &other) const
*/
QColorTransform QColorSpace::transformationToColorSpace(const QColorSpace &colorspace) const
{
- if (!isValid() || !colorspace.isValid())
+ if (!isValid())
return QColorTransform();
if (*this == colorspace)
return QColorTransform();
+ if (!colorspace.isValidTarget()) {
+ qWarning() << "QColorSpace::transformationToColorSpace: colorspace not a valid target";
+ return QColorTransform();
+ }
return d_ptr->transformationToColorSpace(colorspace.d_ptr.get());
}
@@ -1024,6 +1346,7 @@ QString QColorSpace::description() const noexcept
void QColorSpace::setDescription(const QString &description)
{
detach();
+ d_ptr->iccProfile = {};
d_ptr->userDescription = description;
}
@@ -1066,6 +1389,28 @@ QDataStream &operator>>(QDataStream &s, QColorSpace &colorSpace)
#endif // QT_NO_DATASTREAM
#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QColorSpacePrivate::TransferElement &)
+{
+ return dbg << ":Transfer";
+}
+QDebug operator<<(QDebug dbg, const QColorMatrix &)
+{
+ return dbg << ":Matrix";
+}
+QDebug operator<<(QDebug dbg, const QColorVector &)
+{
+ return dbg << ":Offset";
+}
+QDebug operator<<(QDebug dbg, const QColorCLUT &)
+{
+ return dbg << ":CLUT";
+}
+QDebug operator<<(QDebug dbg, const QList<QColorSpacePrivate::Element> &elements)
+{
+ for (auto &&element : elements)
+ std::visit([&](auto &&elm) { dbg << elm; }, element);
+ return dbg;
+}
QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace)
{
QDebugStateSaver saver(dbg);
@@ -1074,8 +1419,22 @@ QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace)
if (colorSpace.d_ptr) {
if (colorSpace.d_ptr->namedColorSpace)
dbg << colorSpace.d_ptr->namedColorSpace << ", ";
- dbg << colorSpace.primaries() << ", " << colorSpace.transferFunction();
- dbg << ", gamma=" << colorSpace.gamma();
+ if (!colorSpace.isValid()) {
+ dbg << "Invalid";
+ if (!colorSpace.d_ptr->iccProfile.isEmpty())
+ dbg << " with profile data";
+ } else if (colorSpace.d_ptr->isThreeComponentMatrix()) {
+ dbg << colorSpace.primaries() << ", " << colorSpace.transferFunction();
+ dbg << ", gamma=" << colorSpace.gamma();
+ } else {
+ if (colorSpace.d_ptr->isPcsLab)
+ dbg << "PCSLab, ";
+ else
+ dbg << "PCSXYZ, ";
+ dbg << "A2B" << colorSpace.d_ptr->mAB;
+ if (!colorSpace.d_ptr->mBA.isEmpty())
+ dbg << ", B2A" << colorSpace.d_ptr->mBA;
+ }
}
dbg << ')';
return dbg;
diff --git a/src/gui/painting/qcolorspace.h b/src/gui/painting/qcolorspace.h
index 4fb5c4273f..488cbd6a53 100644
--- a/src/gui/painting/qcolorspace.h
+++ b/src/gui/painting/qcolorspace.h
@@ -45,9 +45,23 @@ public:
ProPhotoRgb
};
Q_ENUM(TransferFunction)
+ enum class TransformModel : uint8_t {
+ ThreeComponentMatrix = 0,
+ ElementListProcessing,
+ };
+ Q_ENUM(TransformModel)
+ enum class ColorModel : uint8_t {
+ Undefined = 0,
+ Rgb = 1,
+ Gray = 2,
+ Cmyk = 3,
+ };
+ Q_ENUM(ColorModel)
QColorSpace() noexcept = default;
QColorSpace(NamedColorSpace namedColorSpace);
+ QColorSpace(const QPointF &whitePoint, TransferFunction transferFunction, float gamma = 0.0f);
+ QColorSpace(const QPointF &whitePoint, const QList<uint16_t> &transferFunctionTable);
QColorSpace(Primaries primaries, TransferFunction transferFunction, float gamma = 0.0f);
QColorSpace(Primaries primaries, float gamma);
QColorSpace(Primaries primaries, const QList<uint16_t> &transferFunctionTable);
@@ -99,9 +113,14 @@ public:
void setPrimaries(Primaries primariesId);
void setPrimaries(const QPointF &whitePoint, const QPointF &redPoint,
const QPointF &greenPoint, const QPointF &bluePoint);
+ void setWhitePoint(const QPointF &whitePoint);
+ QPointF whitePoint() const;
+ TransformModel transformModel() const noexcept;
+ ColorModel colorModel() const noexcept;
void detach();
bool isValid() const noexcept;
+ bool isValidTarget() const noexcept;
friend inline bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
{ return colorSpace1.equals(colorSpace2); }
diff --git a/src/gui/painting/qcolorspace_p.h b/src/gui/painting/qcolorspace_p.h
index dd668fcd41..4ec801b16b 100644
--- a/src/gui/painting/qcolorspace_p.h
+++ b/src/gui/painting/qcolorspace_p.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2018 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QCOLORSPACE_P_H
@@ -16,6 +16,7 @@
//
#include "qcolorspace.h"
+#include "qcolorclut_p.h"
#include "qcolormatrix_p.h"
#include "qcolortrc_p.h"
#include "qcolortrclut_p.h"
@@ -65,6 +66,8 @@ public:
const QList<uint16_t> &redTransferFunctionTable,
const QList<uint16_t> &greenTransferFunctionTable,
const QList<uint16_t> &blueRransferFunctionTable);
+ QColorSpacePrivate(const QPointF &whitePoint, QColorSpace::TransferFunction transferFunction, float gamma);
+ QColorSpacePrivate(const QPointF &whitePoint, const QList<uint16_t> &transferFunctionTable);
QColorSpacePrivate(const QColorSpacePrivate &other) = default;
static const QColorSpacePrivate *get(const QColorSpace &colorSpace)
@@ -77,6 +80,9 @@ public:
return colorSpace.d_ptr.get();
}
+ bool equals(const QColorSpacePrivate *other) const;
+ bool isValid() const noexcept;
+
void initialize();
void setToXyzMatrix();
void setTransferFunction();
@@ -88,22 +94,40 @@ public:
QColorTransform transformationToColorSpace(const QColorSpacePrivate *out) const;
QColorTransform transformationToXYZ() const;
+ bool isThreeComponentMatrix() const;
+ void clearElementListProcessingForEdit();
+
static constexpr QColorSpace::NamedColorSpace Unknown = QColorSpace::NamedColorSpace(0);
QColorSpace::NamedColorSpace namedColorSpace = Unknown;
QColorSpace::Primaries primaries = QColorSpace::Primaries::Custom;
QColorSpace::TransferFunction transferFunction = QColorSpace::TransferFunction::Custom;
+ QColorSpace::TransformModel transformModel = QColorSpace::TransformModel::ThreeComponentMatrix;
+ QColorSpace::ColorModel colorModel = QColorSpace::ColorModel::Undefined;
float gamma = 0.0f;
QColorVector whitePoint;
+ // Three component matrix data:
QColorTrc trc[3];
QColorMatrix toXyz;
-
+ QColorMatrix chad;
+
+ // Element list processing data:
+ struct TransferElement {
+ QColorTrc trc[4];
+ };
+ using Element = std::variant<TransferElement, QColorMatrix, QColorVector, QColorCLUT>;
+ bool isPcsLab = false;
+ // A = device, B = PCS
+ QList<Element> mAB, mBA;
+
+ // Metadata
QString description;
QString userDescription;
QByteArray iccProfile;
- static QBasicMutex s_lutWriteLock;
+ // Cached tables for three component matrix transform:
+ Q_CONSTINIT static QBasicMutex s_lutWriteLock;
struct LUT {
LUT() = default;
~LUT() = default;
diff --git a/src/gui/painting/qcolortransferfunction_p.h b/src/gui/painting/qcolortransferfunction_p.h
index a9bb26df59..484cc69114 100644
--- a/src/gui/painting/qcolortransferfunction_p.h
+++ b/src/gui/painting/qcolortransferfunction_p.h
@@ -16,44 +16,52 @@
//
#include <QtGui/private/qtguiglobal_p.h>
+#include <QtCore/QFlags>
#include <cmath>
QT_BEGIN_NAMESPACE
// Defines a ICC parametric curve type 4
-class Q_GUI_EXPORT QColorTransferFunction
+class QColorTransferFunction
{
public:
QColorTransferFunction() noexcept
- : m_a(1.0f), m_b(0.0f), m_c(1.0f), m_d(0.0f), m_e(0.0f), m_f(0.0f), m_g(1.0f), m_flags(0)
+ : m_a(1.0f), m_b(0.0f), m_c(1.0f), m_d(0.0f), m_e(0.0f), m_f(0.0f), m_g(1.0f)
+ , m_flags(Hints(Hint::Calculated) | Hint::IsGamma | Hint::IsIdentity)
{ }
+
QColorTransferFunction(float a, float b, float c, float d, float e, float f, float g) noexcept
- : m_a(a), m_b(b), m_c(c), m_d(d), m_e(e), m_f(f), m_g(g), m_flags(0)
+ : m_a(a), m_b(b), m_c(c), m_d(d), m_e(e), m_f(f), m_g(g), m_flags()
{ }
bool isGamma() const
{
updateHints();
- return m_flags & quint32(Hints::IsGamma);
+ return m_flags & Hint::IsGamma;
}
- bool isLinear() const
+ bool isIdentity() const
{
updateHints();
- return m_flags & quint32(Hints::IsLinear);
+ return m_flags & Hint::IsIdentity;
}
bool isSRgb() const
{
updateHints();
- return m_flags & quint32(Hints::IsSRgb);
+ return m_flags & Hint::IsSRgb;
}
float apply(float x) const
{
if (x < m_d)
return m_c * x + m_f;
+ float t = std::pow(m_a * x + m_b, m_g);
+ if (std::isfinite(t))
+ return t + m_e;
+ if (t > 0.f)
+ return 1.f;
else
- return std::pow(m_a * x + m_b, m_g) + m_e;
+ return 0.f;
}
QColorTransferFunction inverted() const
@@ -62,7 +70,7 @@ public:
d = m_c * m_d + m_f;
- if (!qFuzzyIsNull(m_c)) {
+ if (std::isnormal(m_c)) {
c = 1.0f / m_c;
f = -m_f / m_c;
} else {
@@ -70,8 +78,12 @@ public:
f = 0.0f;
}
- if (!qFuzzyIsNull(m_a) && !qFuzzyIsNull(m_g)) {
+ bool valid_abeg = std::isnormal(m_a) && std::isnormal(m_g);
+ if (valid_abeg)
a = std::pow(1.0f / m_a, m_g);
+ if (valid_abeg && !std::isfinite(a))
+ valid_abeg = false;
+ if (valid_abeg) {
b = -a * m_e;
e = -m_b / m_a;
g = 1.0f / m_g;
@@ -88,15 +100,19 @@ public:
// A few predefined curves:
static QColorTransferFunction fromGamma(float gamma)
{
- return QColorTransferFunction(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, gamma);
+ return QColorTransferFunction(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, gamma,
+ Hints(Hint::Calculated) | Hint::IsGamma |
+ (paramCompare(gamma, 1.0f) ? Hint::IsIdentity : Hint::NoHint));
}
static QColorTransferFunction fromSRgb()
{
- return QColorTransferFunction(1.0f / 1.055f, 0.055f / 1.055f, 1.0f / 12.92f, 0.04045f, 0.0f, 0.0f, 2.4f);
+ return QColorTransferFunction(1.0f / 1.055f, 0.055f / 1.055f, 1.0f / 12.92f, 0.04045f, 0.0f, 0.0f, 2.4f,
+ Hints(Hint::Calculated) | Hint::IsSRgb);
}
static QColorTransferFunction fromProPhotoRgb()
{
- return QColorTransferFunction(1.0f, 0.0f, 1.0f / 16.0f, 16.0f / 512.0f, 0.0f, 0.0f, 1.8f);
+ return QColorTransferFunction(1.0f, 0.0f, 1.0f / 16.0f, 16.0f / 512.0f, 0.0f, 0.0f, 1.8f,
+ Hints(Hint::Calculated));
}
bool matches(const QColorTransferFunction &o) const
{
@@ -116,7 +132,20 @@ public:
float m_f;
float m_g;
+ enum class Hint : quint32 {
+ NoHint = 0,
+ Calculated = 1,
+ IsGamma = 2,
+ IsIdentity = 4,
+ IsSRgb = 8
+ };
+
+ Q_DECLARE_FLAGS(Hints, Hint);
+
private:
+ QColorTransferFunction(float a, float b, float c, float d, float e, float f, float g, Hints flags) noexcept
+ : m_a(a), m_b(b), m_c(c), m_d(d), m_e(e), m_f(f), m_g(g), m_flags(flags)
+ { }
static inline bool paramCompare(float p1, float p2)
{
// Much fuzzier than fuzzy compare.
@@ -127,7 +156,7 @@ private:
void updateHints() const
{
- if (m_flags & quint32(Hints::Calculated))
+ if (m_flags & Hint::Calculated)
return;
// We do not consider the case with m_d = 1.0f linear or simple,
// since it wouldn't be linear for applyExtended().
@@ -135,24 +164,21 @@ private:
&& paramCompare(m_d, 0.0f)
&& paramCompare(m_e, 0.0f);
if (simple) {
- m_flags |= quint32(Hints::IsGamma);
+ m_flags |= Hint::IsGamma;
if (qFuzzyCompare(m_g, 1.0f))
- m_flags |= quint32(Hints::IsLinear);
+ m_flags |= Hint::IsIdentity;
} else {
if (*this == fromSRgb())
- m_flags |= quint32(Hints::IsSRgb);
+ m_flags |= Hint::IsSRgb;
}
- m_flags |= quint32(Hints::Calculated);
+ m_flags |= Hint::Calculated;
}
- enum class Hints : quint32 {
- Calculated = 1,
- IsGamma = 2,
- IsLinear = 4,
- IsSRgb = 8
- };
- mutable quint32 m_flags;
+
+ mutable Hints m_flags;
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QColorTransferFunction::Hints);
+
inline bool operator==(const QColorTransferFunction &f1, const QColorTransferFunction &f2)
{
return f1.matches(f2);
diff --git a/src/gui/painting/qcolortransfertable_p.h b/src/gui/painting/qcolortransfertable_p.h
index ac1f843912..ce6ad0c4b2 100644
--- a/src/gui/painting/qcolortransfertable_p.h
+++ b/src/gui/painting/qcolortransfertable_p.h
@@ -29,25 +29,38 @@ QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QColorTransferTable
{
public:
- QColorTransferTable() noexcept
- : m_tableSize(0)
- { }
- QColorTransferTable(uint32_t size, const QList<uint8_t> &table) noexcept
- : m_tableSize(size), m_table8(table)
+ enum Type : uint8_t {
+ TwoWay = 0,
+ OneWay,
+ };
+ QColorTransferTable() noexcept = default;
+ QColorTransferTable(uint32_t size, const QList<uint8_t> &table, Type type = TwoWay) noexcept
+ : m_type(type), m_tableSize(size), m_table8(table)
{
- Q_ASSERT(qsizetype(size) <= table.count());
+ Q_ASSERT(qsizetype(size) <= table.size());
}
- QColorTransferTable(uint32_t size, const QList<uint16_t> &table) noexcept
- : m_tableSize(size), m_table16(table)
+ QColorTransferTable(uint32_t size, const QList<uint16_t> &table, Type type = TwoWay) noexcept
+ : m_type(type), m_tableSize(size), m_table16(table)
{
- Q_ASSERT(qsizetype(size) <= table.count());
+ Q_ASSERT(qsizetype(size) <= table.size());
}
- bool isEmpty() const
+ bool isEmpty() const noexcept
{
return m_tableSize == 0;
}
+ bool isIdentity() const
+ {
+ if (isEmpty())
+ return true;
+ if (m_tableSize != 2)
+ return false;
+ if (!m_table8.isEmpty())
+ return m_table8[0] == 0 && m_table8[1] == 255;
+ return m_table16[0] == 0 && m_table16[1] == 65535;
+ }
+
bool checkValidity() const
{
if (isEmpty())
@@ -58,7 +71,11 @@ public:
// At least 2 elements
if (m_tableSize < 2)
return false;
- // The table must describe an injective curve:
+ return (m_type == OneWay) || checkInvertibility();
+ }
+ bool checkInvertibility() const
+ {
+ // The two-way tables must describe an injective curve:
if (!m_table8.isEmpty()) {
uint8_t val = 0;
for (uint i = 0; i < m_tableSize; ++i) {
@@ -80,15 +97,17 @@ public:
float apply(float x) const
{
+ if (isEmpty())
+ return x;
x = std::clamp(x, 0.0f, 1.0f);
x *= m_tableSize - 1;
- const uint32_t lo = static_cast<uint32_t>(std::floor(x));
+ const uint32_t lo = static_cast<uint32_t>(x);
const uint32_t hi = std::min(lo + 1, m_tableSize - 1);
const float frac = x - lo;
if (!m_table16.isEmpty())
- return (m_table16[lo] * (1.0f - frac) + m_table16[hi] * frac) * (1.0f/65535.0f);
+ return (m_table16[lo] + (m_table16[hi] - m_table16[lo]) * frac) * (1.0f/65535.0f);
if (!m_table8.isEmpty())
- return (m_table8[lo] * (1.0f - frac) + m_table8[hi] * frac) * (1.0f/255.0f);
+ return (m_table8[lo] + (m_table8[hi] - m_table8[lo]) * frac) * (1.0f/255.0f);
return x;
}
@@ -96,43 +115,25 @@ public:
float applyInverse(float x, float resultLargerThan = 0.0f) const
{
Q_ASSERT(resultLargerThan >= 0.0f && resultLargerThan <= 1.0f);
+ Q_ASSERT(m_type == TwoWay);
if (x <= 0.0f)
return 0.0f;
if (x >= 1.0f)
return 1.0f;
- if (!m_table16.isEmpty()) {
- const float v = x * 65535.0f;
- uint32_t i = static_cast<uint32_t>(std::floor(resultLargerThan * (m_tableSize - 1)));
- auto it = std::lower_bound(m_table16.cbegin() + i, m_table16.cend(), v);
- i = it - m_table16.cbegin();
- if (i >= m_tableSize - 1)
- return 1.0f;
- const float y1 = m_table16[i - 1];
- const float y2 = m_table16[i];
- Q_ASSERT(v >= y1 && v <= y2);
- const float fr = (v - y1) / (y2 - y1);
- return (i + fr) * (1.0f / (m_tableSize - 1));
-
- }
- if (!m_table8.isEmpty()) {
- const float v = x * 255.0f;
- uint32_t i = static_cast<uint32_t>(std::floor(resultLargerThan * (m_tableSize - 1)));
- auto it = std::lower_bound(m_table8.cbegin() + i, m_table8.cend(), v);
- i = it - m_table8.cbegin();
- if (i >= m_tableSize - 1)
- return 1.0f;
- const float y1 = m_table8[i - 1];
- const float y2 = m_table8[i];
- Q_ASSERT(v >= y1 && v <= y2);
- const float fr = (v - y1) / (y2 - y1);
- return (i + fr) * (1.0f / (m_tableSize - 1));
- }
+ if (!m_table16.isEmpty())
+ return inverseLookup(x * 65535.0f, resultLargerThan, m_table16, m_tableSize - 1);
+ if (!m_table8.isEmpty())
+ return inverseLookup(x * 255.0f, resultLargerThan, m_table8, m_tableSize - 1);
return x;
}
bool asColorTransferFunction(QColorTransferFunction *transferFn)
{
Q_ASSERT(transferFn);
+ if (isEmpty()) {
+ *transferFn = QColorTransferFunction();
+ return true;
+ }
if (m_tableSize < 2)
return false;
if (!m_table8.isEmpty() && (m_table8[0] != 0 || m_table8[m_tableSize - 1] != 255))
@@ -182,15 +183,36 @@ public:
friend inline bool operator!=(const QColorTransferTable &t1, const QColorTransferTable &t2);
friend inline bool operator==(const QColorTransferTable &t1, const QColorTransferTable &t2);
- uint32_t m_tableSize;
+ Type m_type = TwoWay;
+ uint32_t m_tableSize = 0;
QList<uint8_t> m_table8;
QList<uint16_t> m_table16;
+private:
+ template<typename T>
+ static float inverseLookup(float needle, float resultLargerThan, const QList<T> &table, quint32 tableMax)
+ {
+ uint32_t i = static_cast<uint32_t>(resultLargerThan * tableMax);
+ auto it = std::lower_bound(table.cbegin() + i, table.cend(), needle);
+ i = it - table.cbegin();
+ if (i == 0)
+ return 0.0f;
+ if (i >= tableMax)
+ return 1.0f;
+ const float y1 = table[i - 1];
+ const float y2 = table[i];
+ Q_ASSERT(needle >= y1 && needle <= y2);
+ const float fr = (needle - y1) / (y2 - y1);
+ return (i + fr) * (1.0f / tableMax);
+ }
+
};
inline bool operator!=(const QColorTransferTable &t1, const QColorTransferTable &t2)
{
if (t1.m_tableSize != t2.m_tableSize)
return true;
+ if (t1.m_type != t2.m_type)
+ return true;
if (t1.m_table8.isEmpty() != t2.m_table8.isEmpty())
return true;
if (t1.m_table16.isEmpty() != t2.m_table16.isEmpty())
diff --git a/src/gui/painting/qcolortransform.cpp b/src/gui/painting/qcolortransform.cpp
index 698696877b..aac07bdc09 100644
--- a/src/gui/painting/qcolortransform.cpp
+++ b/src/gui/painting/qcolortransform.cpp
@@ -1,9 +1,11 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qcolortransform.h"
#include "qcolortransform_p.h"
+#include "qcmyk_p.h"
+#include "qcolorclut_p.h"
#include "qcolormatrix_p.h"
#include "qcolorspace_p.h"
#include "qcolortrc_p.h"
@@ -139,11 +141,31 @@ bool QColorTransform::compare(const QColorTransform &other) const
return false;
if (bool(d->colorSpaceOut) != bool(other.d->colorSpaceOut))
return false;
- for (int i = 0; i < 3; ++i) {
- if (d->colorSpaceIn && d->colorSpaceIn->trc[i] != other.d->colorSpaceIn->trc[i])
+ if (d->colorSpaceIn) {
+ if (d->colorSpaceIn->transformModel != other.d->colorSpaceIn->transformModel)
return false;
- if (d->colorSpaceOut && d->colorSpaceOut->trc[i] != other.d->colorSpaceOut->trc[i])
+ if (d->colorSpaceIn->isThreeComponentMatrix()) {
+ for (int i = 0; i < 3; ++i) {
+ if (d->colorSpaceIn && d->colorSpaceIn->trc[i] != other.d->colorSpaceIn->trc[i])
+ return false;
+ }
+ } else {
+ if (!d->colorSpaceIn->equals(other.d->colorSpaceIn.constData()))
+ return false;
+ }
+ }
+ if (d->colorSpaceOut) {
+ if (d->colorSpaceOut->transformModel != other.d->colorSpaceOut->transformModel)
return false;
+ if (d->colorSpaceOut->isThreeComponentMatrix()) {
+ for (int i = 0; i < 3; ++i) {
+ if (d->colorSpaceOut && d->colorSpaceOut->trc[i] != other.d->colorSpaceOut->trc[i])
+ return false;
+ }
+ } else {
+ if (!d->colorSpaceOut->equals(other.d->colorSpaceOut.constData()))
+ return false;
+ }
}
return true;
}
@@ -159,29 +181,7 @@ QRgb QColorTransform::map(QRgb argb) const
return argb;
constexpr float f = 1.0f / 255.0f;
QColorVector c = { qRed(argb) * f, qGreen(argb) * f, qBlue(argb) * f };
- if (d->colorSpaceIn->lut.generated.loadAcquire()) {
- c.x = d->colorSpaceIn->lut[0]->toLinear(c.x);
- c.y = d->colorSpaceIn->lut[1]->toLinear(c.y);
- c.z = d->colorSpaceIn->lut[2]->toLinear(c.z);
- } else {
- c.x = d->colorSpaceIn->trc[0].apply(c.x);
- c.y = d->colorSpaceIn->trc[1].apply(c.y);
- c.z = d->colorSpaceIn->trc[2].apply(c.z);
- }
- c = d->colorMatrix.map(c);
- c.x = std::max(0.0f, std::min(1.0f, c.x));
- c.y = std::max(0.0f, std::min(1.0f, c.y));
- c.z = std::max(0.0f, std::min(1.0f, c.z));
- if (d->colorSpaceOut->lut.generated.loadAcquire()) {
- c.x = d->colorSpaceOut->lut[0]->fromLinear(c.x);
- c.y = d->colorSpaceOut->lut[1]->fromLinear(c.y);
- c.z = d->colorSpaceOut->lut[2]->fromLinear(c.z);
- } else {
- c.x = d->colorSpaceOut->trc[0].applyInverse(c.x);
- c.y = d->colorSpaceOut->trc[1].applyInverse(c.y);
- c.z = d->colorSpaceOut->trc[2].applyInverse(c.z);
- }
-
+ c = d->map(c);
return qRgba(c.x * 255 + 0.5f, c.y * 255 + 0.5f, c.z * 255 + 0.5f, qAlpha(argb));
}
@@ -196,29 +196,7 @@ QRgba64 QColorTransform::map(QRgba64 rgba64) const
return rgba64;
constexpr float f = 1.0f / 65535.0f;
QColorVector c = { rgba64.red() * f, rgba64.green() * f, rgba64.blue() * f };
- if (d->colorSpaceIn->lut.generated.loadAcquire()) {
- c.x = d->colorSpaceIn->lut[0]->toLinear(c.x);
- c.y = d->colorSpaceIn->lut[1]->toLinear(c.y);
- c.z = d->colorSpaceIn->lut[2]->toLinear(c.z);
- } else {
- c.x = d->colorSpaceIn->trc[0].apply(c.x);
- c.y = d->colorSpaceIn->trc[1].apply(c.y);
- c.z = d->colorSpaceIn->trc[2].apply(c.z);
- }
- c = d->colorMatrix.map(c);
- c.x = std::max(0.0f, std::min(1.0f, c.x));
- c.y = std::max(0.0f, std::min(1.0f, c.y));
- c.z = std::max(0.0f, std::min(1.0f, c.z));
- if (d->colorSpaceOut->lut.generated.loadAcquire()) {
- c.x = d->colorSpaceOut->lut[0]->fromLinear(c.x);
- c.y = d->colorSpaceOut->lut[1]->fromLinear(c.y);
- c.z = d->colorSpaceOut->lut[2]->fromLinear(c.z);
- } else {
- c.x = d->colorSpaceOut->trc[0].applyInverse(c.x);
- c.y = d->colorSpaceOut->trc[1].applyInverse(c.y);
- c.z = d->colorSpaceOut->trc[2].applyInverse(c.z);
- }
-
+ c = d->map(c);
return QRgba64::fromRgba64(c.x * 65535.f + 0.5f, c.y * 65535.f + 0.5f, c.z * 65535.f + 0.5f, rgba64.alpha());
}
@@ -232,14 +210,11 @@ QRgbaFloat16 QColorTransform::map(QRgbaFloat16 rgbafp16) const
{
if (!d)
return rgbafp16;
- QColorVector c;
- c.x = d->colorSpaceIn->trc[0].applyExtended(rgbafp16.r);
- c.y = d->colorSpaceIn->trc[1].applyExtended(rgbafp16.g);
- c.z = d->colorSpaceIn->trc[2].applyExtended(rgbafp16.b);
- c = d->colorMatrix.map(c);
- rgbafp16.r = d->colorSpaceOut->trc[0].applyInverseExtended(c.x);
- rgbafp16.g = d->colorSpaceOut->trc[1].applyInverseExtended(c.y);
- rgbafp16.b = d->colorSpaceOut->trc[2].applyInverseExtended(c.z);
+ QColorVector c(rgbafp16.r, rgbafp16.g, rgbafp16.b);
+ c = d->mapExtended(c);
+ rgbafp16.r = qfloat16(c.x);
+ rgbafp16.g = qfloat16(c.y);
+ rgbafp16.b = qfloat16(c.z);
return rgbafp16;
}
@@ -253,14 +228,11 @@ QRgbaFloat32 QColorTransform::map(QRgbaFloat32 rgbafp32) const
{
if (!d)
return rgbafp32;
- QColorVector c;
- c.x = d->colorSpaceIn->trc[0].applyExtended(rgbafp32.r);
- c.y = d->colorSpaceIn->trc[1].applyExtended(rgbafp32.g);
- c.z = d->colorSpaceIn->trc[2].applyExtended(rgbafp32.b);
- c = d->colorMatrix.map(c);
- rgbafp32.r = d->colorSpaceOut->trc[0].applyInverseExtended(c.x);
- rgbafp32.g = d->colorSpaceOut->trc[1].applyInverseExtended(c.y);
- rgbafp32.b = d->colorSpaceOut->trc[2].applyInverseExtended(c.z);
+ QColorVector c(rgbafp32.r, rgbafp32.g, rgbafp32.b);
+ c = d->mapExtended(c);
+ rgbafp32.r = c.x;
+ rgbafp32.g = c.y;
+ rgbafp32.b = c.z;
return rgbafp32;
}
@@ -273,44 +245,42 @@ QColor QColorTransform::map(const QColor &color) const
if (!d)
return color;
QColor clr = color;
- if (color.spec() != QColor::ExtendedRgb || color.spec() != QColor::Rgb)
- clr = clr.toRgb();
-
- QColorVector c = { (float)clr.redF(), (float)clr.greenF(), (float)clr.blueF() };
- if (clr.spec() == QColor::ExtendedRgb) {
- c.x = d->colorSpaceIn->trc[0].applyExtended(c.x);
- c.y = d->colorSpaceIn->trc[1].applyExtended(c.y);
- c.z = d->colorSpaceIn->trc[2].applyExtended(c.z);
- } else {
- c.x = d->colorSpaceIn->trc[0].apply(c.x);
- c.y = d->colorSpaceIn->trc[1].apply(c.y);
- c.z = d->colorSpaceIn->trc[2].apply(c.z);
- }
- c = d->colorMatrix.map(c);
- bool inGamut = c.x >= 0.0f && c.x <= 1.0f && c.y >= 0.0f && c.y <= 1.0f && c.z >= 0.0f && c.z <= 1.0f;
- if (inGamut) {
- if (d->colorSpaceOut->lut.generated.loadAcquire()) {
- c.x = d->colorSpaceOut->lut[0]->fromLinear(c.x);
- c.y = d->colorSpaceOut->lut[1]->fromLinear(c.y);
- c.z = d->colorSpaceOut->lut[2]->fromLinear(c.z);
- } else {
- c.x = d->colorSpaceOut->trc[0].applyInverse(c.x);
- c.y = d->colorSpaceOut->trc[1].applyInverse(c.y);
- c.z = d->colorSpaceOut->trc[2].applyInverse(c.z);
- }
- } else {
- c.x = d->colorSpaceOut->trc[0].applyInverseExtended(c.x);
- c.y = d->colorSpaceOut->trc[1].applyInverseExtended(c.y);
- c.z = d->colorSpaceOut->trc[2].applyInverseExtended(c.z);
+ if (d->colorSpaceIn->colorModel == QColorSpace::ColorModel::Rgb) {
+ if (color.spec() != QColor::ExtendedRgb && color.spec() != QColor::Rgb)
+ clr = clr.toRgb();
+ } else if (d->colorSpaceIn->colorModel == QColorSpace::ColorModel::Cmyk) {
+ if (color.spec() != QColor::Cmyk)
+ clr = clr.toCmyk();
}
+
+ QColorVector c =
+ (clr.spec() == QColor::Cmyk)
+ ? QColorVector(clr.cyanF(), clr.magentaF(), clr.yellowF(), clr.blackF())
+ : QColorVector(clr.redF(), clr.greenF(), clr.blueF());
+
+ c = d->mapExtended(c);
+
QColor out;
- out.setRgbF(c.x, c.y, c.z, color.alphaF());
+ if (d->colorSpaceOut->colorModel == QColorSpace::ColorModel::Cmyk) {
+ c.x = std::clamp(c.x, 0.f, 1.f);
+ c.y = std::clamp(c.y, 0.f, 1.f);
+ c.z = std::clamp(c.z, 0.f, 1.f);
+ c.w = std::clamp(c.w, 0.f, 1.f);
+ out.setCmykF(c.x, c.y, c.z, c.w, color.alphaF());
+ } else {
+ out.setRgbF(c.x, c.y, c.z, color.alphaF());
+ }
return out;
}
// Optimized sub-routines for fast block based conversion:
-template<bool DoClamp = true>
+enum ApplyMatrixForm {
+ DoNotClamp = 0,
+ DoClamp = 1
+};
+
+template<ApplyMatrixForm doClamp = DoClamp>
static void applyMatrix(QColorVector *buffer, const qsizetype len, const QColorMatrix &colorMatrix)
{
#if defined(__SSE2__)
@@ -330,7 +300,7 @@ static void applyMatrix(QColorVector *buffer, const qsizetype len, const QColorM
cx = _mm_add_ps(cx, cy);
cx = _mm_add_ps(cx, cz);
// Clamp:
- if (DoClamp) {
+ if (doClamp) {
cx = _mm_min_ps(cx, maxV);
cx = _mm_max_ps(cx, minV);
}
@@ -350,19 +320,19 @@ static void applyMatrix(QColorVector *buffer, const qsizetype len, const QColorM
cx = vaddq_f32(cx, cy);
cx = vaddq_f32(cx, cz);
// Clamp:
- if (DoClamp) {
+ if (doClamp) {
cx = vminq_f32(cx, maxV);
cx = vmaxq_f32(cx, minV);
}
vst1q_f32(&buffer[j].x, cx);
}
#else
- for (int j = 0; j < len; ++j) {
+ for (qsizetype j = 0; j < len; ++j) {
const QColorVector cv = colorMatrix.map(buffer[j]);
- if (DoClamp) {
- buffer[j].x = std::max(0.0f, std::min(1.0f, cv.x));
- buffer[j].y = std::max(0.0f, std::min(1.0f, cv.y));
- buffer[j].z = std::max(0.0f, std::min(1.0f, cv.z));
+ if (doClamp) {
+ buffer[j].x = std::clamp(cv.x, 0.f, 1.f);
+ buffer[j].y = std::clamp(cv.y, 0.f, 1.f);
+ buffer[j].z = std::clamp(cv.z, 0.f, 1.f);
} else {
buffer[j] = cv;
}
@@ -370,6 +340,39 @@ static void applyMatrix(QColorVector *buffer, const qsizetype len, const QColorM
#endif
}
+template<ApplyMatrixForm doClamp = DoClamp>
+static void clampIfNeeded(QColorVector *buffer, const qsizetype len)
+{
+ if constexpr (doClamp != DoClamp)
+ return;
+#if defined(__SSE2__)
+ const __m128 minV = _mm_set1_ps(0.0f);
+ const __m128 maxV = _mm_set1_ps(1.0f);
+ for (qsizetype j = 0; j < len; ++j) {
+ __m128 c = _mm_loadu_ps(&buffer[j].x);
+ c = _mm_min_ps(c, maxV);
+ c = _mm_max_ps(c, minV);
+ _mm_storeu_ps(&buffer[j].x, c);
+ }
+#elif defined(__ARM_NEON__)
+ const float32x4_t minV = vdupq_n_f32(0.0f);
+ const float32x4_t maxV = vdupq_n_f32(1.0f);
+ for (qsizetype j = 0; j < len; ++j) {
+ float32x4_t c = vld1q_f32(&buffer[j].x);
+ c = vminq_f32(c, maxV);
+ c = vmaxq_f32(c, minV);
+ vst1q_f32(&buffer[j].x, c);
+ }
+#else
+ for (qsizetype j = 0; j < len; ++j) {
+ const QColorVector cv = buffer[j];
+ buffer[j].x = std::clamp(cv.x, 0.f, 1.f);
+ buffer[j].y = std::clamp(cv.y, 0.f, 1.f);
+ buffer[j].z = std::clamp(cv.z, 0.f, 1.f);
+ }
+#endif
+}
+
#if defined(__SSE2__) || defined(__ARM_NEON__)
template<typename T>
static constexpr inline bool isArgb();
@@ -386,9 +389,29 @@ inline int getAlpha<QRgb>(const QRgb &p)
template<>
inline int getAlpha<QRgba64>(const QRgba64 &p)
{ return p.alpha(); }
+
#endif
template<typename T>
+static float getAlphaF(const T &);
+template<> float getAlphaF(const QRgb &r)
+{
+ return qAlpha(r) * (1.f / 255.f);
+}
+template<> float getAlphaF(const QCmyk32 &)
+{
+ return 1.f;
+}
+template<> float getAlphaF(const QRgba64 &r)
+{
+ return r.alpha() * (1.f / 65535.f);
+}
+template<> float getAlphaF(const QRgbaFloat32 &r)
+{
+ return r.a;
+}
+
+template<typename T>
static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr);
template<typename T>
static void loadUnpremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr);
@@ -424,7 +447,7 @@ inline void loadP<QRgba64>(const QRgba64 &p, __m128i &v)
template<typename T>
static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
{
- const __m128 v4080 = _mm_set1_ps(4080.f);
+ const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256));
constexpr bool isARGB = isArgb<T>();
for (qsizetype i = 0; i < len; ++i) {
@@ -443,7 +466,7 @@ static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetyp
vf = _mm_andnot_ps(vAlphaMask, vf);
// LUT
- v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
+ v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
const int ridx = isARGB ? _mm_extract_epi16(v, 4) : _mm_extract_epi16(v, 0);
const int gidx = _mm_extract_epi16(v, 2);
const int bidx = isARGB ? _mm_extract_epi16(v, 0) : _mm_extract_epi16(v, 4);
@@ -459,7 +482,7 @@ static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetyp
template<>
void loadPremultiplied<QRgbaFloat32>(QColorVector *buffer, const QRgbaFloat32 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
{
- const __m128 v4080 = _mm_set1_ps(4080.f);
+ const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
const __m128 viFF00 = _mm_set1_ps(1.0f / (255 * 256));
const __m128 vZero = _mm_set1_ps(0.0f);
const __m128 vOne = _mm_set1_ps(1.0f);
@@ -481,7 +504,7 @@ void loadPremultiplied<QRgbaFloat32>(QColorVector *buffer, const QRgbaFloat32 *s
const __m128 over = _mm_cmpgt_ps(vf, vOne);
if (_mm_movemask_ps(_mm_or_ps(under, over)) == 0) {
// Within gamut
- __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
+ __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
const int ridx = _mm_extract_epi16(v, 0);
const int gidx = _mm_extract_epi16(v, 2);
const int bidx = _mm_extract_epi16(v, 4);
@@ -500,7 +523,7 @@ void loadPremultiplied<QRgbaFloat32>(QColorVector *buffer, const QRgbaFloat32 *s
}
}
-// Load to [0-4080] in 4x32 SIMD
+// Load to [0->TrcResolution] in 4x32 SIMD
template<typename T>
static inline void loadPU(const T &p, __m128i &v);
@@ -514,7 +537,7 @@ inline void loadPU<QRgb>(const QRgb &p, __m128i &v)
v = _mm_unpacklo_epi8(v, _mm_setzero_si128());
v = _mm_unpacklo_epi16(v, _mm_setzero_si128());
#endif
- v = _mm_slli_epi32(v, 4);
+ v = _mm_slli_epi32(v, QColorTrcLut::ShiftUp);
}
template<>
@@ -527,7 +550,7 @@ inline void loadPU<QRgba64>(const QRgba64 &p, __m128i &v)
#else
v = _mm_unpacklo_epi16(v, _mm_setzero_si128());
#endif
- v = _mm_srli_epi32(v, 4);
+ v = _mm_srli_epi32(v, QColorTrcLut::ShiftDown);
}
template<typename T>
@@ -552,7 +575,7 @@ void loadUnpremultiplied(QColorVector *buffer, const T *src, const qsizetype len
template<>
void loadUnpremultiplied<QRgbaFloat32>(QColorVector *buffer, const QRgbaFloat32 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
{
- const __m128 v4080 = _mm_set1_ps(4080.f);
+ const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256));
const __m128 vZero = _mm_set1_ps(0.0f);
const __m128 vOne = _mm_set1_ps(1.0f);
@@ -562,7 +585,7 @@ void loadUnpremultiplied<QRgbaFloat32>(QColorVector *buffer, const QRgbaFloat32
const __m128 over = _mm_cmpgt_ps(vf, vOne);
if (_mm_movemask_ps(_mm_or_ps(under, over)) == 0) {
// Within gamut
- __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
+ __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
const int ridx = _mm_extract_epi16(v, 0);
const int gidx = _mm_extract_epi16(v, 2);
const int bidx = _mm_extract_epi16(v, 4);
@@ -623,7 +646,7 @@ static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetyp
vf = vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(vf), vAlphaMask));
// LUT
- v = vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, 4080.f), vdupq_n_f32(0.5f)));
+ v = vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, float(QColorTrcLut::Resolution)), vdupq_n_f32(0.5f)));
const int ridx = isARGB ? vgetq_lane_u32(v, 2) : vgetq_lane_u32(v, 0);
const int gidx = vgetq_lane_u32(v, 1);
const int bidx = isARGB ? vgetq_lane_u32(v, 0) : vgetq_lane_u32(v, 2);
@@ -636,7 +659,7 @@ static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetyp
}
}
-// Load to [0-4080] in 4x32 SIMD
+// Load to [0->TrcResultion] in 4x32 SIMD
template<typename T>
static inline void loadPU(const T &p, uint32x4_t &v);
@@ -644,7 +667,7 @@ template<>
inline void loadPU<QRgb>(const QRgb &p, uint32x4_t &v)
{
v = vmovl_u16(vget_low_u16(vmovl_u8(vreinterpret_u8_u32(vmov_n_u32(p)))));
- v = vshlq_n_u32(v, 4);
+ v = vshlq_n_u32(v, QColorTrcLut::ShiftUp);
}
template<>
@@ -653,7 +676,7 @@ inline void loadPU<QRgba64>(const QRgba64 &p, uint32x4_t &v)
uint16x4_t v16 = vreinterpret_u16_u64(vld1_u64(reinterpret_cast<const uint64_t *>(&p)));
v16 = vsub_u16(v16, vshr_n_u16(v16, 8));
v = vmovl_u16(v16);
- v = vshrq_n_u32(v, 4);
+ v = vshrq_n_u32(v, QColorTrcLut::ShiftDown);
}
template<typename T>
@@ -682,7 +705,7 @@ void loadPremultiplied<QRgb>(QColorVector *buffer, const QRgb *src, const qsizet
const uint p = src[i];
const int a = qAlpha(p);
if (a) {
- const float ia = 4080.0f / a;
+ const float ia = float(QColorTrcLut::Resolution) / a;
const int ridx = int(qRed(p) * ia + 0.5f);
const int gidx = int(qGreen(p) * ia + 0.5f);
const int bidx = int(qBlue(p) * ia + 0.5f);
@@ -702,7 +725,7 @@ void loadPremultiplied<QRgba64>(QColorVector *buffer, const QRgba64 *src, const
const QRgba64 &p = src[i];
const int a = p.alpha();
if (a) {
- const float ia = 4080.0f / a;
+ const float ia = float(QColorTrcLut::Resolution) / a;
const int ridx = int(p.red() * ia + 0.5f);
const int gidx = int(p.green() * ia + 0.5f);
const int bidx = int(p.blue() * ia + 0.5f);
@@ -792,17 +815,18 @@ inline void storeP<QRgba64>(QRgba64 &p, __m128i &v, int a)
#endif
}
-template<typename T>
-static void storePremultiplied(T *dst, const T *src, const QColorVector *buffer, const qsizetype len,
+template<typename D, typename S,
+ typename = std::enable_if_t<!std::is_same_v<D, QRgbaFloat32>, void>>
+static void storePremultiplied(D *dst, const S *src, const QColorVector *buffer, const qsizetype len,
const QColorTransformPrivate *d_ptr)
{
- const __m128 v4080 = _mm_set1_ps(4080.f);
+ const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256));
- constexpr bool isARGB = isArgb<T>();
+ constexpr bool isARGB = isArgb<D>();
for (qsizetype i = 0; i < len; ++i) {
- const int a = getAlpha<T>(src[i]);
+ const int a = getAlpha<S>(src[i]);
__m128 vf = _mm_loadu_ps(&buffer[i].x);
- __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
+ __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
__m128 va = _mm_mul_ps(_mm_set1_ps(a), iFF00);
const int ridx = _mm_extract_epi16(v, 0);
const int gidx = _mm_extract_epi16(v, 2);
@@ -813,21 +837,21 @@ static void storePremultiplied(T *dst, const T *src, const QColorVector *buffer,
vf = _mm_cvtepi32_ps(v);
vf = _mm_mul_ps(vf, va);
v = _mm_cvtps_epi32(vf);
- storeP<T>(dst[i], v, a);
+ storeP<D>(dst[i], v, a);
}
}
-template<>
-void storePremultiplied<QRgbaFloat32>(QRgbaFloat32 *dst, const QRgbaFloat32 *src,
- const QColorVector *buffer, const qsizetype len,
- const QColorTransformPrivate *d_ptr)
+template<typename S>
+static void storePremultiplied(QRgbaFloat32 *dst, const S *src,
+ const QColorVector *buffer, const qsizetype len,
+ const QColorTransformPrivate *d_ptr)
{
- const __m128 v4080 = _mm_set1_ps(4080.f);
+ const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
const __m128 vZero = _mm_set1_ps(0.0f);
const __m128 vOne = _mm_set1_ps(1.0f);
const __m128 viFF00 = _mm_set1_ps(1.0f / (255 * 256));
for (qsizetype i = 0; i < len; ++i) {
- const float a = src[i].a;
+ const float a = getAlphaF<S>(src[i]);
__m128 va = _mm_set1_ps(a);
__m128 vf = _mm_loadu_ps(&buffer[i].x);
const __m128 under = _mm_cmplt_ps(vf, vZero);
@@ -835,7 +859,7 @@ void storePremultiplied<QRgbaFloat32>(QRgbaFloat32 *dst, const QRgbaFloat32 *src
if (_mm_movemask_ps(_mm_or_ps(under, over)) == 0) {
// Within gamut
va = _mm_mul_ps(va, viFF00);
- __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
+ __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
const int ridx = _mm_extract_epi16(v, 0);
const int gidx = _mm_extract_epi16(v, 2);
const int bidx = _mm_extract_epi16(v, 4);
@@ -874,16 +898,17 @@ inline void storePU<QRgba64>(QRgba64 &p, __m128i &v, int a)
_mm_storel_epi64((__m128i *)&p, v);
}
-template<typename T>
-static void storeUnpremultiplied(T *dst, const T *src, const QColorVector *buffer, const qsizetype len,
+template<typename D, typename S,
+ typename = std::enable_if_t<!std::is_same_v<D, QRgbaFloat32>, void>>
+static void storeUnpremultiplied(D *dst, const S *src, const QColorVector *buffer, const qsizetype len,
const QColorTransformPrivate *d_ptr)
{
- const __m128 v4080 = _mm_set1_ps(4080.f);
- constexpr bool isARGB = isArgb<T>();
+ const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
+ constexpr bool isARGB = isArgb<D>();
for (qsizetype i = 0; i < len; ++i) {
- const int a = getAlpha<T>(src[i]);
+ const int a = getAlpha<S>(src[i]);
__m128 vf = _mm_loadu_ps(&buffer[i].x);
- __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
+ __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
const int ridx = _mm_extract_epi16(v, 0);
const int gidx = _mm_extract_epi16(v, 2);
const int bidx = _mm_extract_epi16(v, 4);
@@ -891,27 +916,27 @@ static void storeUnpremultiplied(T *dst, const T *src, const QColorVector *buffe
v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], isARGB ? 2 : 0);
v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], 1);
v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], isARGB ? 0 : 2);
- storePU<T>(dst[i], v, a);
+ storePU<D>(dst[i], v, a);
}
}
-template<>
-void storeUnpremultiplied<QRgbaFloat32>(QRgbaFloat32 *dst, const QRgbaFloat32 *src,
- const QColorVector *buffer, const qsizetype len,
- const QColorTransformPrivate *d_ptr)
+template<typename S>
+void storeUnpremultiplied(QRgbaFloat32 *dst, const S *src,
+ const QColorVector *buffer, const qsizetype len,
+ const QColorTransformPrivate *d_ptr)
{
- const __m128 v4080 = _mm_set1_ps(4080.f);
+ const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
const __m128 vZero = _mm_set1_ps(0.0f);
const __m128 vOne = _mm_set1_ps(1.0f);
const __m128 viFF00 = _mm_set1_ps(1.0f / (255 * 256));
for (qsizetype i = 0; i < len; ++i) {
- const float a = src[i].a;
+ const float a = getAlphaF<S>(src[i]);
__m128 vf = _mm_loadu_ps(&buffer[i].x);
const __m128 under = _mm_cmplt_ps(vf, vZero);
const __m128 over = _mm_cmpgt_ps(vf, vOne);
if (_mm_movemask_ps(_mm_or_ps(under, over)) == 0) {
// Within gamut
- __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
+ __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
const int ridx = _mm_extract_epi16(v, 0);
const int gidx = _mm_extract_epi16(v, 2);
const int bidx = _mm_extract_epi16(v, 4);
@@ -931,15 +956,14 @@ void storeUnpremultiplied<QRgbaFloat32>(QRgbaFloat32 *dst, const QRgbaFloat32 *s
}
template<typename T>
-static void storeOpaque(T *dst, const T *src, const QColorVector *buffer, const qsizetype len,
+static void storeOpaque(T *dst, const QColorVector *buffer, const qsizetype len,
const QColorTransformPrivate *d_ptr)
{
- Q_UNUSED(src);
- const __m128 v4080 = _mm_set1_ps(4080.f);
+ const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
constexpr bool isARGB = isArgb<T>();
for (qsizetype i = 0; i < len; ++i) {
__m128 vf = _mm_loadu_ps(&buffer[i].x);
- __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
+ __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
const int ridx = _mm_extract_epi16(v, 0);
const int gidx = _mm_extract_epi16(v, 2);
const int bidx = _mm_extract_epi16(v, 4);
@@ -952,12 +976,10 @@ static void storeOpaque(T *dst, const T *src, const QColorVector *buffer, const
}
template<>
-void storeOpaque<QRgbaFloat32>(QRgbaFloat32 *dst, const QRgbaFloat32 *src,
- const QColorVector *buffer, const qsizetype len,
- const QColorTransformPrivate *d_ptr)
+void storeOpaque(QRgbaFloat32 *dst, const QColorVector *buffer, const qsizetype len,
+ const QColorTransformPrivate *d_ptr)
{
- Q_UNUSED(src);
- const __m128 v4080 = _mm_set1_ps(4080.f);
+ const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
const __m128 vZero = _mm_set1_ps(0.0f);
const __m128 vOne = _mm_set1_ps(1.0f);
const __m128 viFF00 = _mm_set1_ps(1.0f / (255 * 256));
@@ -967,7 +989,7 @@ void storeOpaque<QRgbaFloat32>(QRgbaFloat32 *dst, const QRgbaFloat32 *src,
const __m128 over = _mm_cmpgt_ps(vf, vOne);
if (_mm_movemask_ps(_mm_or_ps(under, over)) == 0) {
// Within gamut
- __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
+ __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
const int ridx = _mm_extract_epi16(v, 0);
const int gidx = _mm_extract_epi16(v, 2);
const int bidx = _mm_extract_epi16(v, 4);
@@ -1000,16 +1022,17 @@ inline void storeP<QRgba64>(QRgba64 &p, const uint16x4_t &v)
vst1_u16((uint16_t *)&p, v);
}
-template<typename T>
-static void storePremultiplied(T *dst, const T *src, const QColorVector *buffer, const qsizetype len,
+template<typename D, typename S,
+ typename = std::enable_if_t<!std::is_same_v<D, QRgbaFloat32>, void>>
+static void storePremultiplied(D *dst, const S *src, const QColorVector *buffer, const qsizetype len,
const QColorTransformPrivate *d_ptr)
{
const float iFF00 = 1.0f / (255 * 256);
- constexpr bool isARGB = isArgb<T>();
+ constexpr bool isARGB = isArgb<D>();
for (qsizetype i = 0; i < len; ++i) {
- const int a = getAlpha<T>(src[i]);
+ const int a = getAlpha<S>(src[i]);
float32x4_t vf = vld1q_f32(&buffer[i].x);
- uint32x4_t v = vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, 4080.f), vdupq_n_f32(0.5f)));
+ uint32x4_t v = vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, float(QColorTrcLut::Resolution)), vdupq_n_f32(0.5f)));
const int ridx = vgetq_lane_u32(v, 0);
const int gidx = vgetq_lane_u32(v, 1);
const int bidx = vgetq_lane_u32(v, 2);
@@ -1022,7 +1045,7 @@ static void storePremultiplied(T *dst, const T *src, const QColorVector *buffer,
v = vcvtq_u32_f32(vf);
uint16x4_t v16 = vmovn_u32(v);
v16 = vset_lane_u16(a, v16, 3);
- storeP<T>(dst[i], v16);
+ storeP<D>(dst[i], v16);
}
}
@@ -1044,34 +1067,34 @@ inline void storePU<QRgba64>(QRgba64 &p, uint16x4_t &v, int a)
vst1_u16((uint16_t *)&p, v);
}
-template<typename T>
-static void storeUnpremultiplied(T *dst, const T *src, const QColorVector *buffer, const qsizetype len,
+template<typename D, typename S,
+ typename = std::enable_if_t<!std::is_same_v<D, QRgbaFloat32>, void>>
+static void storeUnpremultiplied(D *dst, const S *src, const QColorVector *buffer, const qsizetype len,
const QColorTransformPrivate *d_ptr)
{
- constexpr bool isARGB = isArgb<T>();
+ constexpr bool isARGB = isArgb<D>();
for (qsizetype i = 0; i < len; ++i) {
- const int a = getAlpha<T>(src[i]);
+ const int a = getAlpha<S>(src[i]);
float32x4_t vf = vld1q_f32(&buffer[i].x);
- uint16x4_t v = vmovn_u32(vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, 4080.f), vdupq_n_f32(0.5f))));
+ uint16x4_t v = vmovn_u32(vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, float(QColorTrcLut::Resolution)), vdupq_n_f32(0.5f))));
const int ridx = vget_lane_u16(v, 0);
const int gidx = vget_lane_u16(v, 1);
const int bidx = vget_lane_u16(v, 2);
v = vset_lane_u16(d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], v, isARGB ? 2 : 0);
v = vset_lane_u16(d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], v, 1);
v = vset_lane_u16(d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], v, isARGB ? 0 : 2);
- storePU<T>(dst[i], v, a);
+ storePU<D>(dst[i], v, a);
}
}
template<typename T>
-static void storeOpaque(T *dst, const T *src, const QColorVector *buffer, const qsizetype len,
+static void storeOpaque(T *dst, const QColorVector *buffer, const qsizetype len,
const QColorTransformPrivate *d_ptr)
{
- Q_UNUSED(src);
constexpr bool isARGB = isArgb<T>();
for (qsizetype i = 0; i < len; ++i) {
float32x4_t vf = vld1q_f32(&buffer[i].x);
- uint16x4_t v = vmovn_u32(vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, 4080.f), vdupq_n_f32(0.5f))));
+ uint16x4_t v = vmovn_u32(vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, float(QColorTrcLut::Resolution)), vdupq_n_f32(0.5f))));
const int ridx = vget_lane_u16(v, 0);
const int gidx = vget_lane_u16(v, 1);
const int bidx = vget_lane_u16(v, 2);
@@ -1088,9 +1111,9 @@ static void storePremultiplied(QRgb *dst, const QRgb *src, const QColorVector *b
for (qsizetype i = 0; i < len; ++i) {
const int a = qAlpha(src[i]);
const float fa = a / (255.0f * 256.0f);
- const float r = d_ptr->colorSpaceOut->lut[0]->m_fromLinear[int(buffer[i].x * 4080.0f + 0.5f)];
- const float g = d_ptr->colorSpaceOut->lut[1]->m_fromLinear[int(buffer[i].y * 4080.0f + 0.5f)];
- const float b = d_ptr->colorSpaceOut->lut[2]->m_fromLinear[int(buffer[i].z * 4080.0f + 0.5f)];
+ const float r = d_ptr->colorSpaceOut->lut[0]->m_fromLinear[int(buffer[i].x * float(QColorTrcLut::Resolution) + 0.5f)];
+ const float g = d_ptr->colorSpaceOut->lut[1]->m_fromLinear[int(buffer[i].y * float(QColorTrcLut::Resolution) + 0.5f)];
+ const float b = d_ptr->colorSpaceOut->lut[2]->m_fromLinear[int(buffer[i].z * float(QColorTrcLut::Resolution) + 0.5f)];
dst[i] = qRgba(r * fa + 0.5f, g * fa + 0.5f, b * fa + 0.5f, a);
}
}
@@ -1106,10 +1129,9 @@ static void storeUnpremultiplied(QRgb *dst, const QRgb *src, const QColorVector
}
}
-static void storeOpaque(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len,
+static void storeOpaque(QRgb *dst, const QColorVector *buffer, const qsizetype len,
const QColorTransformPrivate *d_ptr)
{
- Q_UNUSED(src);
for (qsizetype i = 0; i < len; ++i) {
const int r = d_ptr->colorSpaceOut->lut[0]->u8FromLinearF32(buffer[i].x);
const int g = d_ptr->colorSpaceOut->lut[1]->u8FromLinearF32(buffer[i].y);
@@ -1118,34 +1140,36 @@ static void storeOpaque(QRgb *dst, const QRgb *src, const QColorVector *buffer,
}
}
-static void storePremultiplied(QRgba64 *dst, const QRgba64 *src, const QColorVector *buffer, const qsizetype len,
+template<typename S>
+static void storePremultiplied(QRgba64 *dst, const S *src, const QColorVector *buffer, const qsizetype len,
const QColorTransformPrivate *d_ptr)
{
for (qsizetype i = 0; i < len; ++i) {
- const int a = src[i].alpha();
+ const int a = getAlphaF(src[i]) * 65535.f;
const float fa = a / (255.0f * 256.0f);
- const float r = d_ptr->colorSpaceOut->lut[0]->m_fromLinear[int(buffer[i].x * 4080.0f + 0.5f)];
- const float g = d_ptr->colorSpaceOut->lut[1]->m_fromLinear[int(buffer[i].y * 4080.0f + 0.5f)];
- const float b = d_ptr->colorSpaceOut->lut[2]->m_fromLinear[int(buffer[i].z * 4080.0f + 0.5f)];
+ const float r = d_ptr->colorSpaceOut->lut[0]->m_fromLinear[int(buffer[i].x * float(QColorTrcLut::Resolution) + 0.5f)];
+ const float g = d_ptr->colorSpaceOut->lut[1]->m_fromLinear[int(buffer[i].y * float(QColorTrcLut::Resolution) + 0.5f)];
+ const float b = d_ptr->colorSpaceOut->lut[2]->m_fromLinear[int(buffer[i].z * float(QColorTrcLut::Resolution) + 0.5f)];
dst[i] = qRgba64(r * fa + 0.5f, g * fa + 0.5f, b * fa + 0.5f, a);
}
}
-static void storeUnpremultiplied(QRgba64 *dst, const QRgba64 *src, const QColorVector *buffer, const qsizetype len,
+template<typename S>
+static void storeUnpremultiplied(QRgba64 *dst, const S *src, const QColorVector *buffer, const qsizetype len,
const QColorTransformPrivate *d_ptr)
{
for (qsizetype i = 0; i < len; ++i) {
+ const int a = getAlphaF(src[i]) * 65535.f;
const int r = d_ptr->colorSpaceOut->lut[0]->u16FromLinearF32(buffer[i].x);
const int g = d_ptr->colorSpaceOut->lut[1]->u16FromLinearF32(buffer[i].y);
const int b = d_ptr->colorSpaceOut->lut[2]->u16FromLinearF32(buffer[i].z);
- dst[i] = qRgba64(r, g, b, src[i].alpha());
+ dst[i] = qRgba64(r, g, b, a);
}
}
-static void storeOpaque(QRgba64 *dst, const QRgba64 *src, const QColorVector *buffer, const qsizetype len,
+static void storeOpaque(QRgba64 *dst, const QColorVector *buffer, const qsizetype len,
const QColorTransformPrivate *d_ptr)
{
- Q_UNUSED(src);
for (qsizetype i = 0; i < len; ++i) {
const int r = d_ptr->colorSpaceOut->lut[0]->u16FromLinearF32(buffer[i].x);
const int g = d_ptr->colorSpaceOut->lut[1]->u16FromLinearF32(buffer[i].y);
@@ -1155,11 +1179,12 @@ static void storeOpaque(QRgba64 *dst, const QRgba64 *src, const QColorVector *bu
}
#endif
#if !defined(__SSE2__)
-static void storePremultiplied(QRgbaFloat32 *dst, const QRgbaFloat32 *src, const QColorVector *buffer,
+template<typename S>
+static void storePremultiplied(QRgbaFloat32 *dst, const S *src, const QColorVector *buffer,
const qsizetype len, const QColorTransformPrivate *d_ptr)
{
for (qsizetype i = 0; i < len; ++i) {
- const float a = src[i].a;
+ const float a = getAlphaF(src[i]);
dst[i].r = d_ptr->colorSpaceOut->trc[0].applyInverseExtended(buffer[i].x) * a;
dst[i].g = d_ptr->colorSpaceOut->trc[1].applyInverseExtended(buffer[i].y) * a;
dst[i].b = d_ptr->colorSpaceOut->trc[2].applyInverseExtended(buffer[i].z) * a;
@@ -1167,11 +1192,12 @@ static void storePremultiplied(QRgbaFloat32 *dst, const QRgbaFloat32 *src, const
}
}
-static void storeUnpremultiplied(QRgbaFloat32 *dst, const QRgbaFloat32 *src, const QColorVector *buffer,
+template<typename S>
+static void storeUnpremultiplied(QRgbaFloat32 *dst, const S *src, const QColorVector *buffer,
const qsizetype len, const QColorTransformPrivate *d_ptr)
{
for (qsizetype i = 0; i < len; ++i) {
- const float a = src[i].a;
+ const float a = getAlphaF(src[i]);
dst[i].r = d_ptr->colorSpaceOut->trc[0].applyInverseExtended(buffer[i].x);
dst[i].g = d_ptr->colorSpaceOut->trc[1].applyInverseExtended(buffer[i].y);
dst[i].b = d_ptr->colorSpaceOut->trc[2].applyInverseExtended(buffer[i].z);
@@ -1179,10 +1205,9 @@ static void storeUnpremultiplied(QRgbaFloat32 *dst, const QRgbaFloat32 *src, con
}
}
-static void storeOpaque(QRgbaFloat32 *dst, const QRgbaFloat32 *src, const QColorVector *buffer, const qsizetype len,
+static void storeOpaque(QRgbaFloat32 *dst, const QColorVector *buffer, const qsizetype len,
const QColorTransformPrivate *d_ptr)
{
- Q_UNUSED(src);
for (qsizetype i = 0; i < len; ++i) {
dst[i].r = d_ptr->colorSpaceOut->trc[0].applyInverseExtended(buffer[i].x);
dst[i].g = d_ptr->colorSpaceOut->trc[1].applyInverseExtended(buffer[i].y);
@@ -1191,20 +1216,35 @@ static void storeOpaque(QRgbaFloat32 *dst, const QRgbaFloat32 *src, const QColor
}
}
#endif
-static void storeGray(quint8 *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len,
+
+static void loadGray(QColorVector *buffer, const quint8 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const float y = d_ptr->colorSpaceIn->lut[0]->u8ToLinearF32(src[i]);
+ buffer[i] = d_ptr->colorSpaceIn->whitePoint * y;
+ }
+}
+
+static void loadGray(QColorVector *buffer, const quint16 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const float y = d_ptr->colorSpaceIn->lut[0]->u16ToLinearF32(src[i]);
+ buffer[i] = d_ptr->colorSpaceIn->whitePoint * y;
+ }
+}
+
+static void storeOpaque(quint8 *dst, const QColorVector *buffer, const qsizetype len,
const QColorTransformPrivate *d_ptr)
{
- Q_UNUSED(src);
for (qsizetype i = 0; i < len; ++i)
- dst[i] = d_ptr->colorSpaceOut->lut[1]->u8FromLinearF32(buffer[i].y);
+ dst[i] = d_ptr->colorSpaceOut->lut[0]->u8FromLinearF32(buffer[i].y);
}
-static void storeGray(quint16 *dst, const QRgba64 *src, const QColorVector *buffer, const qsizetype len,
+static void storeOpaque(quint16 *dst, const QColorVector *buffer, const qsizetype len,
const QColorTransformPrivate *d_ptr)
{
- Q_UNUSED(src);
for (qsizetype i = 0; i < len; ++i)
- dst[i] = d_ptr->colorSpaceOut->lut[1]->u16FromLinearF32(buffer[i].y);
+ dst[i] = d_ptr->colorSpaceOut->lut[0]->u16FromLinearF32(buffer[i].y);
}
static constexpr qsizetype WorkBlockSize = 256;
@@ -1218,53 +1258,493 @@ private:
alignas(T) char data[sizeof(T) * Count];
};
+void loadUnpremultipliedLUT(QColorVector *buffer, const QRgb *src, const qsizetype len)
+{
+ const float f = 1.0f / 255.f;
+ for (qsizetype i = 0; i < len; ++i) {
+ const uint p = src[i];
+ buffer[i].x = qRed(p) * f;
+ buffer[i].y = qGreen(p) * f;
+ buffer[i].z = qBlue(p) * f;
+ }
+}
+
+void loadUnpremultipliedLUT(QColorVector *buffer, const QCmyk32 *src, const qsizetype len)
+{
+ const float f = 1.0f / 255.f;
+ for (qsizetype i = 0; i < len; ++i) {
+ const QCmyk32 p = src[i];
+ buffer[i].x = (p.cyan() * f);
+ buffer[i].y = (p.magenta() * f);
+ buffer[i].z = (p.yellow() * f);
+ buffer[i].w = (p.black() * f);
+ }
+}
+
+void loadUnpremultipliedLUT(QColorVector *buffer, const QRgba64 *src, const qsizetype len)
+{
+ const float f = 1.0f / 65535.f;
+ for (qsizetype i = 0; i < len; ++i) {
+ buffer[i].x = src[i].red() * f;
+ buffer[i].y = src[i].green() * f;
+ buffer[i].z = src[i].blue() * f;
+ }
+}
+
+void loadUnpremultipliedLUT(QColorVector *buffer, const QRgbaFloat32 *src, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ buffer[i].x = src[i].r;
+ buffer[i].y = src[i].g;
+ buffer[i].z = src[i].b;
+ }
+}
+
+void loadPremultipliedLUT(QColorVector *buffer, const QRgb *src, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const uint p = src[i];
+ const float f = 1.0f / qAlpha(p);
+ buffer[i].x = (qRed(p) * f);
+ buffer[i].y = (qGreen(p) * f);
+ buffer[i].z = (qBlue(p) * f);
+ }
+}
+
+void loadPremultipliedLUT(QColorVector *, const QCmyk32 *, const qsizetype)
+{
+ Q_UNREACHABLE();
+}
+
+void loadPremultipliedLUT(QColorVector *buffer, const QRgba64 *src, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const float f = 1.0f / src[i].alpha();
+ buffer[i].x = (src[i].red() * f);
+ buffer[i].y = (src[i].green() * f);
+ buffer[i].z = (src[i].blue() * f);
+ }
+}
+
+void loadPremultipliedLUT(QColorVector *buffer, const QRgbaFloat32 *src, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const float f = 1.0f / src[i].a;
+ buffer[i].x = src[i].r * f;
+ buffer[i].y = src[i].g * f;
+ buffer[i].z = src[i].b * f;
+ }
+}
template<typename T>
-void QColorTransformPrivate::apply(T *dst, const T *src, qsizetype count, TransformFlags flags) const
+static void storeUnpremultipliedLUT(QRgb *dst, const T *, const QColorVector *buffer, const qsizetype len)
{
- if (!colorMatrix.isValid())
+ for (qsizetype i = 0; i < len; ++i) {
+ const int r = buffer[i].x * 255.f;
+ const int g = buffer[i].y * 255.f;
+ const int b = buffer[i].z * 255.f;
+ dst[i] = 0xff000000 | (r << 16) | (g << 8) | (b << 0);
+ }
+}
+
+template<>
+void storeUnpremultipliedLUT(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const int r = buffer[i].x * 255.f;
+ const int g = buffer[i].y * 255.f;
+ const int b = buffer[i].z * 255.f;
+ dst[i] = (src[i] & 0xff000000) | (r << 16) | (g << 8) | (b << 0);
+ }
+}
+
+
+template<typename T>
+void storeUnpremultipliedLUT(QCmyk32 *dst, const T *, const QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const int c = buffer[i].x * 255.f;
+ const int m = buffer[i].y * 255.f;
+ const int y = buffer[i].z * 255.f;
+ const int k = buffer[i].w * 255.f;
+ dst[i] = QCmyk32(c, m, y, k);
+ }
+}
+
+template<typename T>
+static void storeUnpremultipliedLUT(QRgba64 *dst, const T *,
+ const QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const int r = buffer[i].x * 65535.f;
+ const int g = buffer[i].y * 65535.f;
+ const int b = buffer[i].z * 65535.f;
+ dst[i] = qRgba64(r, g, b, 65535);
+ }
+}
+
+template<>
+void storeUnpremultipliedLUT(QRgba64 *dst, const QRgb *src,
+ const QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const int a = qAlpha(src[i]) * 257;
+ const int r = buffer[i].x * 65535.f;
+ const int g = buffer[i].y * 65535.f;
+ const int b = buffer[i].z * 65535.f;
+ dst[i] = qRgba64(r, g, b, a);
+ }
+}
+
+template<>
+void storeUnpremultipliedLUT(QRgba64 *dst, const QRgba64 *src,
+ const QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const int r = buffer[i].x * 65535.f;
+ const int g = buffer[i].y * 65535.f;
+ const int b = buffer[i].z * 65535.f;
+ dst[i] = qRgba64(r, g, b, src[i].alpha());
+ }
+}
+
+template<typename T>
+static void storeUnpremultipliedLUT(QRgbaFloat32 *dst, const T *src,
+ const QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const float r = buffer[i].x;
+ const float g = buffer[i].y;
+ const float b = buffer[i].z;
+ dst[i] = QRgbaFloat32{r, g, b, getAlphaF(src[i])};
+ }
+}
+
+template<typename T>
+static void storePremultipliedLUT(QRgb *, const T *, const QColorVector *, const qsizetype);
+
+template<>
+void storePremultipliedLUT(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const int a = qAlpha(src[i]);
+ const int r = buffer[i].x * a;
+ const int g = buffer[i].y * a;
+ const int b = buffer[i].z * a;
+ dst[i] = (src[i] & 0xff000000) | (r << 16) | (g << 8) | (b << 0);
+ }
+}
+
+template<>
+void storePremultipliedLUT(QRgb *dst, const QCmyk32 *, const QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const int r = buffer[i].x * 255.f;
+ const int g = buffer[i].y * 255.f;
+ const int b = buffer[i].z * 255.f;
+ dst[i] = 0xff000000 | (r << 16) | (g << 8) | (b << 0);
+ }
+}
+
+
+template<typename T>
+static void storePremultipliedLUT(QCmyk32 *dst, const T *src, const QColorVector *buffer, const qsizetype len)
+{
+ storeUnpremultipliedLUT(dst, src, buffer, len);
+}
+
+template<typename T>
+static void storePremultipliedLUT(QRgba64 *, const T *, const QColorVector *, const qsizetype);
+
+template<>
+void storePremultipliedLUT(QRgba64 *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const int a = qAlpha(src[i]) * 257;
+ const int r = buffer[i].x * a;
+ const int g = buffer[i].y * a;
+ const int b = buffer[i].z * a;
+ dst[i] = qRgba64(r, g, b, a);
+ }
+}
+
+template<>
+void storePremultipliedLUT(QRgba64 *dst, const QCmyk32 *, const QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const int r = buffer[i].x * 65535.f;
+ const int g = buffer[i].y * 65535.f;
+ const int b = buffer[i].z * 65535.f;
+ dst[i] = qRgba64(r, g, b, 65535);
+ }
+}
+
+template<>
+void storePremultipliedLUT(QRgba64 *dst, const QRgba64 *src, const QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const int a = src[i].alpha();
+ const int r = buffer[i].x * a;
+ const int g = buffer[i].y * a;
+ const int b = buffer[i].z * a;
+ dst[i] = qRgba64(r, g, b, a);
+ }
+}
+
+template<typename T>
+static void storePremultipliedLUT(QRgbaFloat32 *dst, const T *src, const QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const float a = getAlphaF(src[i]);
+ const float r = buffer[i].x * a;
+ const float g = buffer[i].y * a;
+ const float b = buffer[i].z * a;
+ dst[i] = QRgbaFloat32{r, g, b, a};
+ }
+}
+
+static void visitElement(const QColorSpacePrivate::TransferElement &element, QColorVector *buffer, const qsizetype len)
+{
+ const bool doW = element.trc[3].isValid();
+ for (qsizetype i = 0; i < len; ++i) {
+ buffer[i].x = element.trc[0].apply(buffer[i].x);
+ buffer[i].y = element.trc[1].apply(buffer[i].y);
+ buffer[i].z = element.trc[2].apply(buffer[i].z);
+ if (doW)
+ buffer[i].w = element.trc[3].apply(buffer[i].w);
+ }
+}
+
+static void visitElement(const QColorMatrix &element, QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i)
+ buffer[i] = element.map(buffer[i]);
+}
+
+static void visitElement(const QColorVector &offset, QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i)
+ buffer[i] += offset;
+}
+
+static void visitElement(const QColorCLUT &element, QColorVector *buffer, const qsizetype len)
+{
+ if (element.isEmpty())
return;
+ for (qsizetype i = 0; i < len; ++i)
+ buffer[i] = element.apply(buffer[i]);
+}
- updateLutsIn();
- updateLutsOut();
+/*!
+ \internal
+*/
+QColorVector QColorTransformPrivate::map(QColorVector c) const
+{
+ if (colorSpaceIn->isThreeComponentMatrix()) {
+ if (colorSpaceIn->lut.generated.loadAcquire()) {
+ c.x = colorSpaceIn->lut[0]->toLinear(c.x);
+ c.y = colorSpaceIn->lut[1]->toLinear(c.y);
+ c.z = colorSpaceIn->lut[2]->toLinear(c.z);
+ } else {
+ c.x = colorSpaceIn->trc[0].apply(c.x);
+ c.y = colorSpaceIn->trc[1].apply(c.y);
+ c.z = colorSpaceIn->trc[2].apply(c.z);
+ }
+ c = colorMatrix.map(c);
+ } else {
+ // Do element based conversion
+ for (auto &&element : colorSpaceIn->mAB)
+ std::visit([&c](auto &&elm) { visitElement(elm, &c, 1); }, element);
+ }
+ c.x = std::clamp(c.x, 0.0f, 1.0f);
+ c.y = std::clamp(c.y, 0.0f, 1.0f);
+ c.z = std::clamp(c.z, 0.0f, 1.0f);
+
+ // Match Profile Connection Spaces (PCS):
+ if (colorSpaceOut->isPcsLab && !colorSpaceIn->isPcsLab)
+ c = c.xyzToLab();
+ else if (colorSpaceIn->isPcsLab && !colorSpaceOut->isPcsLab)
+ c = c.labToXyz();
+
+ if (colorSpaceOut->isThreeComponentMatrix()) {
+ if (!colorSpaceIn->isThreeComponentMatrix()) {
+ c = colorMatrix.map(c);
+ c.x = std::clamp(c.x, 0.0f, 1.0f);
+ c.y = std::clamp(c.y, 0.0f, 1.0f);
+ c.z = std::clamp(c.z, 0.0f, 1.0f);
+ }
+ if (colorSpaceOut->lut.generated.loadAcquire()) {
+ c.x = colorSpaceOut->lut[0]->fromLinear(c.x);
+ c.y = colorSpaceOut->lut[1]->fromLinear(c.y);
+ c.z = colorSpaceOut->lut[2]->fromLinear(c.z);
+ } else {
+ c.x = colorSpaceOut->trc[0].applyInverse(c.x);
+ c.y = colorSpaceOut->trc[1].applyInverse(c.y);
+ c.z = colorSpaceOut->trc[2].applyInverse(c.z);
+ }
+ } else {
+ // Do element based conversion
+ for (auto &&element : colorSpaceOut->mBA)
+ std::visit([&c](auto &&elm) { visitElement(elm, &c, 1); }, element);
+ c.x = std::clamp(c.x, 0.0f, 1.0f);
+ c.y = std::clamp(c.y, 0.0f, 1.0f);
+ c.z = std::clamp(c.z, 0.0f, 1.0f);
+ }
+ return c;
+}
- bool doApplyMatrix = !colorMatrix.isIdentity();
- constexpr bool DoClip = !std::is_same_v<T, QRgbaFloat16> && !std::is_same_v<T, QRgbaFloat32>;
+/*!
+ \internal
+*/
+QColorVector QColorTransformPrivate::mapExtended(QColorVector c) const
+{
+ if (colorSpaceIn->isThreeComponentMatrix()) {
+ c.x = colorSpaceIn->trc[0].applyExtended(c.x);
+ c.y = colorSpaceIn->trc[1].applyExtended(c.y);
+ c.z = colorSpaceIn->trc[2].applyExtended(c.z);
+ c = colorMatrix.map(c);
+ } else {
+ // Do element based conversion
+ for (auto &&element : colorSpaceIn->mAB)
+ std::visit([&c](auto &&elm) { visitElement(elm, &c, 1); }, element);
+ }
- QUninitialized<QColorVector, WorkBlockSize> buffer;
+ // Match Profile Connection Spaces (PCS):
+ if (colorSpaceOut->isPcsLab && !colorSpaceIn->isPcsLab)
+ c = c.xyzToLab();
+ else if (colorSpaceIn->isPcsLab && !colorSpaceOut->isPcsLab)
+ c = c.labToXyz();
+
+ if (colorSpaceOut->isThreeComponentMatrix()) {
+ if (!colorSpaceIn->isThreeComponentMatrix())
+ c = colorMatrix.map(c);
+ c.x = colorSpaceOut->trc[0].applyInverseExtended(c.x);
+ c.y = colorSpaceOut->trc[1].applyInverseExtended(c.y);
+ c.z = colorSpaceOut->trc[2].applyInverseExtended(c.z);
+ } else {
+ // Do element based conversion
+ for (auto &&element : colorSpaceOut->mBA)
+ std::visit([&c](auto &&elm) { visitElement(elm, &c, 1); }, element);
+ }
+ return c;
+}
+
+template<typename S>
+void QColorTransformPrivate::applyConvertIn(const S *src, QColorVector *buffer, qsizetype len, TransformFlags flags) const
+{
+ // Avoid compiling this part for S=QCmyk32:
+ if constexpr (!std::is_same_v<S, QCmyk32>) {
+ if (colorSpaceIn->isThreeComponentMatrix()) {
+ if (flags & InputPremultiplied)
+ loadPremultiplied(buffer, src, len, this);
+ else
+ loadUnpremultiplied(buffer, src, len, this);
+
+ if (!colorSpaceOut->isThreeComponentMatrix())
+ applyMatrix<DoClamp>(buffer, len, colorMatrix); // colorMatrix should have the first half only.
+ return;
+ }
+ }
+ Q_ASSERT(!colorSpaceIn->isThreeComponentMatrix());
+
+ if (flags & InputPremultiplied)
+ loadPremultipliedLUT(buffer, src, len);
+ else
+ loadUnpremultipliedLUT(buffer, src, len);
+
+ if constexpr (std::is_same_v<S, QRgbaFloat16> || std::is_same_v<S, QRgbaFloat32>)
+ clampIfNeeded<DoClamp>(buffer, len);
+
+ // Do element based conversion
+ for (auto &&element : colorSpaceIn->mAB)
+ std::visit([&buffer, len](auto &&elm) { visitElement(elm, buffer, len); }, element);
+}
+
+template<typename D, typename S>
+void QColorTransformPrivate::applyConvertOut(D *dst, const S *src, QColorVector *buffer, qsizetype len, TransformFlags flags) const
+{
+ constexpr ApplyMatrixForm doClamp = (std::is_same_v<D, QRgbaFloat16> || std::is_same_v<D, QRgbaFloat32>) ? DoNotClamp : DoClamp;
+ // Avoid compiling this part for D=QCmyk32:
+ if constexpr (!std::is_same_v<D, QCmyk32>) {
+ if (colorSpaceOut->isThreeComponentMatrix()) {
+ applyMatrix<doClamp>(buffer, len, colorMatrix); // colorMatrix should have the latter half only.
+
+ if constexpr (std::is_same_v<S, QCmyk32>) {
+ storeOpaque(dst, buffer, len, this);
+ } else {
+ if (flags & InputOpaque)
+ storeOpaque(dst, buffer, len, this);
+ else if (flags & OutputPremultiplied)
+ storePremultiplied(dst, src, buffer, len, this);
+ else
+ storeUnpremultiplied(dst, src, buffer, len, this);
+ }
+ return;
+ }
+ }
+ Q_ASSERT(!colorSpaceOut->isThreeComponentMatrix());
+
+ // Do element based conversion
+ for (auto &&element : colorSpaceOut->mBA)
+ std::visit([&buffer, len](auto &&elm) { visitElement(elm, buffer, len); }, element);
+
+ clampIfNeeded<doClamp>(buffer, len);
+
+ if (flags & OutputPremultiplied)
+ storePremultipliedLUT(dst, src, buffer, len);
+ else
+ storeUnpremultipliedLUT(dst, src, buffer, len);
+}
+
+template<typename D, typename S>
+void QColorTransformPrivate::applyElementListTransform(D *dst, const S *src, qsizetype count, TransformFlags flags) const
+{
+ Q_ASSERT(!colorSpaceIn->isThreeComponentMatrix() || !colorSpaceOut->isThreeComponentMatrix());
+ if (!colorMatrix.isValid())
+ return;
+
+ if (colorSpaceIn->isThreeComponentMatrix())
+ updateLutsIn();
+ if (colorSpaceOut->isThreeComponentMatrix())
+ updateLutsOut();
+
+ QUninitialized<QColorVector, WorkBlockSize> buffer;
qsizetype i = 0;
while (i < count) {
const qsizetype len = qMin(count - i, WorkBlockSize);
- if (flags & InputPremultiplied)
- loadPremultiplied(buffer, src + i, len, this);
- else
- loadUnpremultiplied(buffer, src + i, len, this);
- if (doApplyMatrix)
- applyMatrix<DoClip>(buffer, len, colorMatrix);
+ applyConvertIn(src + i, buffer, len, flags);
- if (flags & InputOpaque)
- storeOpaque(dst + i, src + i, buffer, len, this);
- else if (flags & OutputPremultiplied)
- storePremultiplied(dst + i, src + i, buffer, len, this);
- else
- storeUnpremultiplied(dst + i, src + i, buffer, len, this);
+ // Match Profile Connection Spaces (PCS):
+ if (colorSpaceOut->isPcsLab && !colorSpaceIn->isPcsLab) {
+ for (qsizetype j = 0; j < len; ++j)
+ buffer[j] = buffer[j].xyzToLab();
+ } else if (colorSpaceIn->isPcsLab && !colorSpaceOut->isPcsLab) {
+ for (qsizetype j = 0; j < len; ++j)
+ buffer[j] = buffer[j].labToXyz();
+ }
+
+ applyConvertOut(dst + i, src + i, buffer, len, flags);
i += len;
}
}
template<typename D, typename S>
-void QColorTransformPrivate::applyReturnGray(D *dst, const S *src, qsizetype count, TransformFlags flags) const
+void QColorTransformPrivate::applyThreeComponentMatrix(D *dst, const S *src, qsizetype count, TransformFlags flags) const
{
+ Q_ASSERT(colorSpaceIn->isThreeComponentMatrix() && colorSpaceOut->isThreeComponentMatrix());
+
if (!colorMatrix.isValid())
return;
updateLutsIn();
updateLutsOut();
- QUninitialized<QColorVector, WorkBlockSize> buffer;
+ bool doApplyMatrix = !colorMatrix.isIdentity();
+ constexpr ApplyMatrixForm doClamp = (std::is_same_v<D, QRgbaFloat16> || std::is_same_v<D, QRgbaFloat32>) ? DoNotClamp : DoClamp;
+ QUninitialized<QColorVector, WorkBlockSize> buffer;
qsizetype i = 0;
while (i < count) {
const qsizetype len = qMin(count - i, WorkBlockSize);
@@ -1273,9 +1753,17 @@ void QColorTransformPrivate::applyReturnGray(D *dst, const S *src, qsizetype cou
else
loadUnpremultiplied(buffer, src + i, len, this);
- applyMatrix(buffer, len, colorMatrix);
+ if (doApplyMatrix)
+ applyMatrix<doClamp>(buffer, len, colorMatrix);
+ else
+ clampIfNeeded<doClamp>(buffer, len);
- storeGray(dst + i, src + i, buffer, len, this);
+ if (flags & InputOpaque)
+ storeOpaque(dst + i, buffer, len, this);
+ else if (flags & OutputPremultiplied)
+ storePremultiplied(dst + i, src + i, buffer, len, this);
+ else
+ storeUnpremultiplied(dst + i, src + i, buffer, len, this);
i += len;
}
@@ -1283,110 +1771,228 @@ void QColorTransformPrivate::applyReturnGray(D *dst, const S *src, qsizetype cou
/*!
\internal
- \enum QColorTransformPrivate::TransformFlag
+ Applies the color transformation on \a count S pixels starting from
+ \a src and stores the result in \a dst as D pixels .
- Defines how the transform is to be applied.
+ Assumes unpremultiplied data by default. Set \a flags to change defaults.
- \value Unpremultiplied The input and output should both be unpremultiplied.
- \value InputOpaque The input is guaranteed to be opaque.
- \value InputPremultiplied The input is premultiplied.
- \value OutputPremultiplied The output should be premultiplied.
- \value Premultiplied Both input and output should both be premultiplied.
+ \sa prepare()
*/
+template<typename D, typename S>
+void QColorTransformPrivate::apply(D *dst, const S *src, qsizetype count, TransformFlags flags) const
+{
+ if constexpr (!std::is_same_v<D, QCmyk32> && !std::is_same_v<S, QCmyk32>) {
+ if (isThreeComponentMatrix())
+ return applyThreeComponentMatrix<D, S>(dst, src, count, flags);
+ }
+ applyElementListTransform<D, S>(dst, src, count, flags);
+}
/*!
\internal
- Prepares a color transformation for fast application. You do not need to
- call this explicitly as it will be called implicitly on the first transforms, but
- if you want predictable performance on the first transforms, you can perform it
- in advance.
+ Is to be called on a color-transform to XYZ, returns only luminance values.
- \sa QColorTransform::map(), apply()
-*/
-void QColorTransformPrivate::prepare()
+ */
+template<typename D, typename S>
+void QColorTransformPrivate::applyReturnGray(D *dst, const S *src, qsizetype count, TransformFlags flags) const
{
- updateLutsIn();
+ Q_ASSERT(colorSpaceOut->isThreeComponentMatrix());
updateLutsOut();
-}
+ if (!colorSpaceIn->isThreeComponentMatrix()) {
+ QUninitialized<QColorVector, WorkBlockSize> buffer;
-/*!
- \internal
- Applies the color transformation on \a count QRgb pixels starting from
- \a src and stores the result in \a dst.
+ qsizetype i = 0;
+ while (i < count) {
+ const qsizetype len = qMin(count - i, WorkBlockSize);
- Thread-safe if prepare() has been called first.
+ applyConvertIn(src, buffer, len, flags);
- Assumes unpremultiplied data by default. Set \a flags to change defaults.
+ // Match Profile Connection Spaces (PCS):
+ if (colorSpaceOut->isPcsLab && !colorSpaceIn->isPcsLab) {
+ for (qsizetype j = 0; j < len; ++j)
+ buffer[j] = buffer[j].xyzToLab();
+ } else if (colorSpaceIn->isPcsLab && !colorSpaceOut->isPcsLab) {
+ for (qsizetype j = 0; j < len; ++j)
+ buffer[j] = buffer[j].labToXyz();
+ }
- \sa prepare()
-*/
-void QColorTransformPrivate::apply(QRgb *dst, const QRgb *src, qsizetype count, TransformFlags flags) const
-{
- apply<QRgb>(dst, src, count, flags);
-}
+ applyMatrix<DoClamp>(buffer, len, colorMatrix);
+ storeOpaque(dst + i, buffer, len, this);
-/*!
- \internal
- Applies the color transformation on \a count QRgba64 pixels starting from
- \a src and stores the result in \a dst.
+ i += len;
+ }
+ return;
+ }
+ if constexpr (!std::is_same_v<S, QCmyk32>) {
+ if (!colorMatrix.isValid())
+ return;
- Thread-safe if prepare() has been called first.
+ updateLutsIn();
- Assumes unpremultiplied data by default. Set \a flags to change defaults.
+ QUninitialized<QColorVector, WorkBlockSize> buffer;
- \sa prepare()
-*/
-void QColorTransformPrivate::apply(QRgba64 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const
-{
- apply<QRgba64>(dst, src, count, flags);
+ qsizetype i = 0;
+ while (i < count) {
+ const qsizetype len = qMin(count - i, WorkBlockSize);
+ if (flags & InputPremultiplied)
+ loadPremultiplied(buffer, src + i, len, this);
+ else
+ loadUnpremultiplied(buffer, src + i, len, this);
+
+ applyMatrix<DoClamp>(buffer, len, colorMatrix);
+
+ storeOpaque(dst + i, buffer, len, this);
+
+ i += len;
+ }
+ } else {
+ Q_UNREACHABLE();
+ }
}
/*!
\internal
- Applies the color transformation on \a count QRgbaFloat32 pixels starting from
- \a src and stores the result in \a dst.
+*/
+template<typename D, typename S>
+void QColorTransformPrivate::applyGray(D *dst, const S *src, qsizetype count, TransformFlags) const
+{
+ Q_ASSERT(colorSpaceIn->isThreeComponentMatrix());
+ updateLutsIn();
+ if constexpr (std::is_same_v<D, QRgb> || std::is_same_v<D, QRgba64> || std::is_same_v<D, QRgbaFloat32> || std::is_same_v<D, QCmyk32>) {
+ if (!colorSpaceOut->isThreeComponentMatrix()) {
+ QUninitialized<QColorVector, WorkBlockSize> buffer;
- Thread-safe if prepare() has been called first.
+ qsizetype i = 0;
+ while (i < count) {
+ const qsizetype len = qMin(count - i, WorkBlockSize);
+ loadGray(buffer, src + i, len, this);
- Assumes unpremultiplied data by default. Set \a flags to change defaults.
+ applyMatrix<DoClamp>(buffer, len, colorMatrix);
- \sa prepare()
-*/
-void QColorTransformPrivate::apply(QRgbaFloat32 *dst, const QRgbaFloat32 *src, qsizetype count,
- TransformFlags flags) const
-{
- apply<QRgbaFloat32>(dst, src, count, flags);
+ // Match Profile Connection Spaces (PCS):
+ if (colorSpaceOut->isPcsLab && !colorSpaceIn->isPcsLab) {
+ for (qsizetype j = 0; j < len; ++j)
+ buffer[j] = buffer[j].xyzToLab();
+ } else if (colorSpaceIn->isPcsLab && !colorSpaceOut->isPcsLab) {
+ for (qsizetype j = 0; j < len; ++j)
+ buffer[j] = buffer[j].labToXyz();
+ }
+
+ // Do element based conversion
+ for (auto &&element : colorSpaceOut->mBA)
+ std::visit([&buffer, len](auto &&elm) { visitElement(elm, buffer, len); }, element);
+
+ clampIfNeeded<DoClamp>(buffer, len);
+
+ storeUnpremultipliedLUT(dst, src, buffer, len); // input is always opaque
+
+ i += len;
+ }
+ return;
+ }
+ }
+ Q_ASSERT(colorSpaceOut->isThreeComponentMatrix());
+ if constexpr (!std::is_same_v<D, QCmyk32>) {
+ if (!colorMatrix.isValid())
+ return;
+
+ updateLutsOut();
+
+ QUninitialized<QColorVector, WorkBlockSize> buffer;
+
+ qsizetype i = 0;
+ while (i < count) {
+ const qsizetype len = qMin(count - i, WorkBlockSize);
+ loadGray(buffer, src + i, len, this);
+
+ applyMatrix<DoClamp>(buffer, len, colorMatrix);
+
+ storeOpaque(dst + i, buffer, len, this);
+ i += len;
+ }
+ } else {
+ Q_UNREACHABLE();
+ }
}
/*!
\internal
- Is to be called on a color-transform to XYZ, returns only luminance values.
+ \enum QColorTransformPrivate::TransformFlag
+
+ Defines how the transform should handle alpha values.
+ \value Unpremultiplied The input and output should both be unpremultiplied.
+ \value InputOpaque The input is guaranteed to be opaque.
+ \value InputPremultiplied The input is premultiplied.
+ \value OutputPremultiplied The output should be premultiplied.
+ \value Premultiplied Both input and output should both be premultiplied.
*/
-void QColorTransformPrivate::apply(quint8 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const
-{
- applyReturnGray<quint8, QRgb>(dst, src, count, flags);
-}
/*!
\internal
- Is to be called on a color-transform to XYZ, returns only luminance values.
+ Prepares a color transformation for fast application. You do not need to
+ call this explicitly as it will be called implicitly on the first transforms, but
+ if you want predictable performance on the first transforms, you can perform it
+ in advance.
+ \sa QColorTransform::map(), apply()
*/
-void QColorTransformPrivate::apply(quint16 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const
+void QColorTransformPrivate::prepare()
{
- applyReturnGray<quint16, QRgba64>(dst, src, count, flags);
+ updateLutsIn();
+ updateLutsOut();
}
+// Only allow versions increasing precision
+template void QColorTransformPrivate::applyReturnGray<quint8, QRgb>(quint8 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::applyReturnGray<quint8, QCmyk32>(quint8 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::applyReturnGray<quint16, QCmyk32>(quint16 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::applyReturnGray<quint16, QRgba64>(quint16 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::applyGray<quint8, quint8>(quint8 *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::applyGray<quint16, quint8>(quint16 *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::applyGray<quint16, quint16>(quint16 *dst, const quint16 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::applyGray<QRgb, quint8>(QRgb *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::applyGray<QCmyk32, quint8>(QCmyk32 *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::applyGray<QCmyk32, quint16>(QCmyk32 *dst, const quint16 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::applyGray<QRgba64, quint16>(QRgba64 *dst, const quint16 *src, qsizetype count, TransformFlags flags) const;
+
+template void QColorTransformPrivate::apply<QRgb, QRgb>(QRgb *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QRgb, QCmyk32>(QRgb *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QCmyk32, QRgb>(QCmyk32 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QCmyk32, QCmyk32>(QCmyk32 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QCmyk32, QRgba64>(QCmyk32 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QCmyk32, QRgbaFloat32>(QCmyk32 *dst, const QRgbaFloat32 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QRgba64, QRgb>(QRgba64 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QRgba64, QCmyk32>(QRgba64 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QRgba64, QRgba64>(QRgba64 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QRgbaFloat32, QRgb>(QRgbaFloat32 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QRgbaFloat32, QCmyk32>(QRgbaFloat32 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QRgbaFloat32, QRgba64>(QRgbaFloat32 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QRgbaFloat32, QRgbaFloat32>(QRgbaFloat32 *dst, const QRgbaFloat32 *src, qsizetype count, TransformFlags flags) const;
+
+bool QColorTransformPrivate::isThreeComponentMatrix() const
+{
+ if (colorSpaceIn && !colorSpaceIn->isThreeComponentMatrix())
+ return false;
+ if (colorSpaceOut && !colorSpaceOut->isThreeComponentMatrix())
+ return false;
+ return true;
+}
/*!
\internal
*/
bool QColorTransformPrivate::isIdentity() const
{
+ if (colorSpaceIn == colorSpaceOut)
+ return true;
if (!colorMatrix.isIdentity())
return false;
if (colorSpaceIn && colorSpaceOut) {
+ if (colorSpaceIn->equals(colorSpaceOut.constData()))
+ return true;
+ if (!isThreeComponentMatrix())
+ return false;
if (colorSpaceIn->transferFunction != colorSpaceOut->transferFunction)
return false;
if (colorSpaceIn->transferFunction == QColorSpace::TransferFunction::Custom) {
@@ -1395,6 +2001,8 @@ bool QColorTransformPrivate::isIdentity() const
&& colorSpaceIn->trc[2] == colorSpaceOut->trc[2];
}
} else {
+ if (!isThreeComponentMatrix())
+ return false;
if (colorSpaceIn && colorSpaceIn->transferFunction != QColorSpace::TransferFunction::Linear)
return false;
if (colorSpaceOut && colorSpaceOut->transferFunction != QColorSpace::TransferFunction::Linear)
diff --git a/src/gui/painting/qcolortransform_p.h b/src/gui/painting/qcolortransform_p.h
index 99c09188cb..59ea6a2405 100644
--- a/src/gui/painting/qcolortransform_p.h
+++ b/src/gui/painting/qcolortransform_p.h
@@ -22,6 +22,7 @@
#include <QtGui/qrgbafloat.h>
QT_BEGIN_NAMESPACE
+class QCmyk32;
class QColorTransformPrivate : public QSharedData
{
@@ -35,8 +36,8 @@ public:
void updateLutsIn() const;
void updateLutsOut() const;
- bool simpleGammaCorrection() const;
bool isIdentity() const;
+ bool isThreeComponentMatrix() const;
Q_GUI_EXPORT void prepare();
enum TransformFlag {
@@ -48,19 +49,25 @@ public:
};
Q_DECLARE_FLAGS(TransformFlags, TransformFlag)
- void apply(QRgb *dst, const QRgb *src, qsizetype count, TransformFlags flags = Unpremultiplied) const;
- void apply(QRgba64 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags = Unpremultiplied) const;
- void apply(QRgbaFloat32 *dst, const QRgbaFloat32 *src, qsizetype count,
- TransformFlags flags = Unpremultiplied) const;
- void apply(quint8 *dst, const QRgb *src, qsizetype count, TransformFlags flags = Unpremultiplied) const;
- void apply(quint16 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags = Unpremultiplied) const;
-
- template<typename T>
- void apply(T *dst, const T *src, qsizetype count, TransformFlags flags) const;
+ QColorVector map(QColorVector color) const;
+ QColorVector mapExtended(QColorVector color) const;
template<typename D, typename S>
+ void apply(D *dst, const S *src, qsizetype count, TransformFlags flags) const;
+ template<typename D, typename S>
+ void applyGray(D *dst, const S *src, qsizetype count, TransformFlags flags) const;
+ template<typename D, typename S>
void applyReturnGray(D *dst, const S *src, qsizetype count, TransformFlags flags) const;
+private:
+ template<typename S>
+ void applyConvertIn(const S *src, QColorVector *buffer, qsizetype len, TransformFlags flags) const;
+ template<typename D, typename S>
+ void applyConvertOut(D *dst, const S *src, QColorVector *buffer, qsizetype len, TransformFlags flags) const;
+ template<typename D, typename S>
+ void applyElementListTransform(D *dst, const S *src, qsizetype count, TransformFlags flags) const;
+ template<typename D, typename S>
+ void applyThreeComponentMatrix(D *dst, const S *src, qsizetype count, TransformFlags flags) const;
};
QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolortrc_p.h b/src/gui/painting/qcolortrc_p.h
index 9e879bb3e2..d1ad1987df 100644
--- a/src/gui/painting/qcolortrc_p.h
+++ b/src/gui/painting/qcolortrc_p.h
@@ -21,17 +21,15 @@
QT_BEGIN_NAMESPACE
-
-// Defines an ICC TRC (Tone Reproduction Curve)
+// Defines a TRC (Tone Reproduction Curve)
class Q_GUI_EXPORT QColorTrc
{
public:
- QColorTrc() noexcept : m_type(Type::Uninitialized)
- { }
- QColorTrc(const QColorTransferFunction &fun) : m_type(Type::Function), m_fun(fun)
- { }
- QColorTrc(const QColorTransferTable &table) : m_type(Type::Table), m_table(table)
- { }
+ QColorTrc() noexcept : m_type(Type::Uninitialized) { }
+ QColorTrc(const QColorTransferFunction &fun) : m_type(Type::Function), m_fun(fun) { }
+ QColorTrc(const QColorTransferTable &table) : m_type(Type::Table), m_table(table) { }
+ QColorTrc(QColorTransferFunction &&fun) noexcept : m_type(Type::Function), m_fun(std::move(fun)) { }
+ QColorTrc(QColorTransferTable &&table) noexcept : m_type(Type::Table), m_table(std::move(table)) { }
enum class Type {
Uninitialized,
@@ -39,9 +37,10 @@ public:
Table
};
- bool isLinear() const
+ bool isIdentity() const
{
- return m_type == Type::Uninitialized || (m_type == Type::Function && m_fun.isLinear());
+ return (m_type == Type::Function && m_fun.isIdentity())
+ || (m_type == Type::Table && m_table.isIdentity());
}
bool isValid() const
{
diff --git a/src/gui/painting/qcolortrclut.cpp b/src/gui/painting/qcolortrclut.cpp
index 6f1cacea75..8a7673bc00 100644
--- a/src/gui/painting/qcolortrclut.cpp
+++ b/src/gui/painting/qcolortrclut.cpp
@@ -13,43 +13,80 @@ std::shared_ptr<QColorTrcLut> QColorTrcLut::create()
return std::make_shared<Access>();
}
-std::shared_ptr<QColorTrcLut> QColorTrcLut::fromGamma(qreal gamma)
+std::shared_ptr<QColorTrcLut> QColorTrcLut::fromGamma(qreal gamma, Direction dir)
{
auto cp = create();
+ cp->setFromGamma(gamma, dir);
+ return cp;
+}
- for (int i = 0; i <= (255 * 16); ++i) {
- cp->m_toLinear[i] = ushort(qRound(qPow(i / qreal(255 * 16), gamma) * (255 * 256)));
- cp->m_fromLinear[i] = ushort(qRound(qPow(i / qreal(255 * 16), qreal(1) / gamma) * (255 * 256)));
- }
-
+std::shared_ptr<QColorTrcLut> QColorTrcLut::fromTransferFunction(const QColorTransferFunction &fun, Direction dir)
+{
+ auto cp = create();
+ cp->setFromTransferFunction(fun, dir);
return cp;
}
-std::shared_ptr<QColorTrcLut> QColorTrcLut::fromTransferFunction(const QColorTransferFunction &fun)
+std::shared_ptr<QColorTrcLut> QColorTrcLut::fromTransferTable(const QColorTransferTable &table, Direction dir)
{
auto cp = create();
- QColorTransferFunction inv = fun.inverted();
+ cp->setFromTransferTable(table, dir);
+ return cp;
+}
- for (int i = 0; i <= (255 * 16); ++i) {
- cp->m_toLinear[i] = ushort(qRound(fun.apply(i / qreal(255 * 16)) * (255 * 256)));
- cp->m_fromLinear[i] = ushort(qRound(inv.apply(i / qreal(255 * 16)) * (255 * 256)));
+void QColorTrcLut::setFromGamma(qreal gamma, Direction dir)
+{
+ if (dir & ToLinear) {
+ if (!m_toLinear)
+ m_toLinear.reset(new ushort[Resolution + 1]);
+ for (int i = 0; i <= Resolution; ++i)
+ m_toLinear[i] = ushort(qRound(qPow(i / qreal(Resolution), gamma) * (255 * 256)));
}
- return cp;
+ if (dir & FromLinear) {
+ if (!m_fromLinear)
+ m_fromLinear.reset(new ushort[Resolution + 1]);
+ for (int i = 0; i <= Resolution; ++i)
+ m_fromLinear[i] = ushort(qRound(qPow(i / qreal(Resolution), qreal(1) / gamma) * (255 * 256)));
+ }
}
-std::shared_ptr<QColorTrcLut> QColorTrcLut::fromTransferTable(const QColorTransferTable &table)
+void QColorTrcLut::setFromTransferFunction(const QColorTransferFunction &fun, Direction dir)
{
- auto cp = create();
+ if (dir & ToLinear) {
+ if (!m_toLinear)
+ m_toLinear.reset(new ushort[Resolution + 1]);
+ for (int i = 0; i <= Resolution; ++i)
+ m_toLinear[i] = ushort(qRound(fun.apply(i / qreal(Resolution)) * (255 * 256)));
+ }
- float minInverse = 0.0f;
- for (int i = 0; i <= (255 * 16); ++i) {
- cp->m_toLinear[i] = ushort(qBound(0, qRound(table.apply(i / qreal(255 * 16)) * (255 * 256)), 65280));
- minInverse = table.applyInverse(i / qreal(255 * 16), minInverse);
- cp->m_fromLinear[i] = ushort(qBound(0, qRound(minInverse * (255 * 256)), 65280));
+ if (dir & FromLinear) {
+ if (!m_fromLinear)
+ m_fromLinear.reset(new ushort[Resolution + 1]);
+ QColorTransferFunction inv = fun.inverted();
+ for (int i = 0; i <= Resolution; ++i)
+ m_fromLinear[i] = ushort(qRound(inv.apply(i / qreal(Resolution)) * (255 * 256)));
}
+}
- return cp;
+void QColorTrcLut::setFromTransferTable(const QColorTransferTable &table, Direction dir)
+{
+ if (dir & ToLinear) {
+ if (!m_toLinear)
+ m_toLinear.reset(new ushort[Resolution + 1]);
+ for (int i = 0; i <= Resolution; ++i)
+ m_toLinear[i] = ushort(qBound(0, qRound(table.apply(i / qreal(Resolution)) * (255 * 256)), 65280));
+ }
+
+ if (dir & FromLinear) {
+ if (!m_fromLinear)
+ m_fromLinear.reset(new ushort[Resolution + 1]);
+ float minInverse = 0.0f;
+ for (int i = 0; i <= Resolution; ++i) {
+ minInverse = table.applyInverse(i / qreal(Resolution), minInverse);
+ m_fromLinear[i] = ushort(qBound(0, qRound(minInverse * (255 * 256)), 65280));
+ }
+ }
}
QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolortrclut_p.h b/src/gui/painting/qcolortrclut_p.h
index 1b312d4a78..3ebab42809 100644
--- a/src/gui/painting/qcolortrclut_p.h
+++ b/src/gui/painting/qcolortrclut_p.h
@@ -36,9 +36,22 @@ class QColorTransferTable;
class Q_GUI_EXPORT QColorTrcLut
{
public:
- static std::shared_ptr<QColorTrcLut> fromGamma(qreal gamma);
- static std::shared_ptr<QColorTrcLut> fromTransferFunction(const QColorTransferFunction &transfn);
- static std::shared_ptr<QColorTrcLut> fromTransferTable(const QColorTransferTable &transTable);
+ static constexpr uint32_t ShiftUp = 4; // Amount to shift up from 1->255
+ static constexpr uint32_t ShiftDown = (8 - ShiftUp); // Amount to shift down from 1->65280
+ static constexpr qsizetype Resolution = (1 << ShiftUp) * 255; // Number of entries in table
+
+ enum Direction {
+ ToLinear = 1,
+ FromLinear = 2,
+ BiLinear = ToLinear | FromLinear
+ };
+
+ static std::shared_ptr<QColorTrcLut> fromGamma(qreal gamma, Direction dir = BiLinear);
+ static std::shared_ptr<QColorTrcLut> fromTransferFunction(const QColorTransferFunction &transFn, Direction dir = BiLinear);
+ static std::shared_ptr<QColorTrcLut> fromTransferTable(const QColorTransferTable &transTable, Direction dir = BiLinear);
+ void setFromGamma(qreal gamma, Direction dir = BiLinear);
+ void setFromTransferFunction(const QColorTransferFunction &transFn, Direction dir = BiLinear);
+ void setFromTransferTable(const QColorTransferTable &transTable, Direction dir = BiLinear);
// The following methods all convert opaque or unpremultiplied colors:
@@ -47,7 +60,7 @@ public:
#if defined(__SSE2__)
__m128i v = _mm_cvtsi32_si128(rgb32);
v = _mm_unpacklo_epi8(v, _mm_setzero_si128());
- const __m128i vidx = _mm_slli_epi16(v, 4);
+ const __m128i vidx = _mm_slli_epi16(v, ShiftUp);
const int ridx = _mm_extract_epi16(vidx, 2);
const int gidx = _mm_extract_epi16(vidx, 1);
const int bidx = _mm_extract_epi16(vidx, 0);
@@ -62,7 +75,7 @@ public:
#elif (defined(__ARM_NEON__) || defined(__ARM_NEON)) && Q_BYTE_ORDER == Q_LITTLE_ENDIAN
uint8x8_t v8 = vreinterpret_u8_u32(vmov_n_u32(rgb32));
uint16x4_t v16 = vget_low_u16(vmovl_u8(v8));
- const uint16x4_t vidx = vshl_n_u16(v16, 4);
+ const uint16x4_t vidx = vshl_n_u16(v16, ShiftUp);
const int ridx = vget_lane_u16(vidx, 2);
const int gidx = vget_lane_u16(vidx, 1);
const int bidx = vget_lane_u16(vidx, 0);
@@ -73,42 +86,43 @@ public:
v16 = vadd_u16(v16, vshr_n_u16(v16, 8));
return QRgba64::fromRgba64(vget_lane_u64(vreinterpret_u64_u16(v16), 0));
#else
- uint r = m_toLinear[qRed(rgb32) << 4];
- uint g = m_toLinear[qGreen(rgb32) << 4];
- uint b = m_toLinear[qBlue(rgb32) << 4];
+ uint r = m_toLinear[qRed(rgb32) << ShiftUp];
+ uint g = m_toLinear[qGreen(rgb32) << ShiftUp];
+ uint b = m_toLinear[qBlue(rgb32) << ShiftUp];
r = r + (r >> 8);
g = g + (g >> 8);
b = b + (b >> 8);
return QRgba64::fromRgba64(r, g, b, qAlpha(rgb32) * 257);
#endif
}
+ QRgba64 toLinear64(QRgba64) const = delete;
QRgb toLinear(QRgb rgb32) const
{
- return convertWithTable(rgb32, m_toLinear);
+ return convertWithTable(rgb32, m_toLinear.get());
}
QRgba64 toLinear(QRgba64 rgb64) const
{
- return convertWithTable(rgb64, m_toLinear);
+ return convertWithTable(rgb64, m_toLinear.get());
}
float u8ToLinearF32(int c) const
{
- ushort v = m_toLinear[c << 4];
+ ushort v = m_toLinear[c << ShiftUp];
return v * (1.0f / (255*256));
}
float u16ToLinearF32(int c) const
{
c -= (c >> 8);
- ushort v = m_toLinear[c >> 4];
+ ushort v = m_toLinear[c >> ShiftDown];
return v * (1.0f / (255*256));
}
float toLinear(float f) const
{
- ushort v = m_toLinear[(int)(f * (255 * 16) + 0.5f)];
+ ushort v = m_toLinear[(int)(f * Resolution + 0.5f)];
return v * (1.0f / (255*256));
}
@@ -117,7 +131,7 @@ public:
#if defined(__SSE2__)
__m128i v = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(&rgb64));
v = _mm_sub_epi16(v, _mm_srli_epi16(v, 8));
- const __m128i vidx = _mm_srli_epi16(v, 4);
+ const __m128i vidx = _mm_srli_epi16(v, ShiftDown);
const int ridx = _mm_extract_epi16(vidx, 0);
const int gidx = _mm_extract_epi16(vidx, 1);
const int bidx = _mm_extract_epi16(vidx, 2);
@@ -131,7 +145,7 @@ public:
#elif (defined(__ARM_NEON__) || defined(__ARM_NEON)) && Q_BYTE_ORDER == Q_LITTLE_ENDIAN
uint16x4_t v = vreinterpret_u16_u64(vmov_n_u64(rgb64));
v = vsub_u16(v, vshr_n_u16(v, 8));
- const uint16x4_t vidx = vshr_n_u16(v, 4);
+ const uint16x4_t vidx = vshr_n_u16(v, ShiftDown);
const int ridx = vget_lane_u16(vidx, 0);
const int gidx = vget_lane_u16(vidx, 1);
const int bidx = vget_lane_u16(vidx, 2);
@@ -150,56 +164,56 @@ public:
g = g - (g >> 8);
b = b - (b >> 8);
a = (a + 0x80) >> 8;
- r = (m_fromLinear[r >> 4] + 0x80) >> 8;
- g = (m_fromLinear[g >> 4] + 0x80) >> 8;
- b = (m_fromLinear[b >> 4] + 0x80) >> 8;
+ r = (m_fromLinear[r >> ShiftDown] + 0x80) >> 8;
+ g = (m_fromLinear[g >> ShiftDown] + 0x80) >> 8;
+ b = (m_fromLinear[b >> ShiftDown] + 0x80) >> 8;
return (a << 24) | (r << 16) | (g << 8) | b;
#endif
}
QRgb fromLinear(QRgb rgb32) const
{
- return convertWithTable(rgb32, m_fromLinear);
+ return convertWithTable(rgb32, m_fromLinear.get());
}
QRgba64 fromLinear(QRgba64 rgb64) const
{
- return convertWithTable(rgb64, m_fromLinear);
+ return convertWithTable(rgb64, m_fromLinear.get());
}
int u8FromLinearF32(float f) const
{
- ushort v = m_fromLinear[(int)(f * (255 * 16) + 0.5f)];
+ ushort v = m_fromLinear[(int)(f * Resolution + 0.5f)];
return (v + 0x80) >> 8;
}
int u16FromLinearF32(float f) const
{
- ushort v = m_fromLinear[(int)(f * (255 * 16) + 0.5f)];
+ ushort v = m_fromLinear[(int)(f * Resolution + 0.5f)];
return v + (v >> 8);
}
float fromLinear(float f) const
{
- ushort v = m_fromLinear[(int)(f * (255 * 16) + 0.5f)];
+ ushort v = m_fromLinear[(int)(f * Resolution + 0.5f)];
return v * (1.0f / (255*256));
}
// We translate to 0-65280 (255*256) instead to 0-65535 to make simple
// shifting an accurate conversion.
- // We translate from 0-4080 (255*16) for the same speed up, and to keep
- // the tables small enough to fit in most inner caches.
- ushort m_toLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280]
- ushort m_fromLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280]
+ // We translate from 0->Resolution (4080 = 255*16) for the same speed up,
+ // and to keep the tables small enough to fit in most inner caches.
+ std::unique_ptr<ushort[]> m_toLinear; // [0->Resolution] -> [0-65280]
+ std::unique_ptr<ushort[]> m_fromLinear; // [0->Resolution] -> [0-65280]
private:
- QColorTrcLut() { } // force uninitialized members
+ QColorTrcLut() = default;
static std::shared_ptr<QColorTrcLut> create();
Q_ALWAYS_INLINE static QRgb convertWithTable(QRgb rgb32, const ushort *table)
{
- const int r = (table[qRed(rgb32) << 4] + 0x80) >> 8;
- const int g = (table[qGreen(rgb32) << 4] + 0x80) >> 8;
- const int b = (table[qBlue(rgb32) << 4] + 0x80) >> 8;
+ const int r = (table[qRed(rgb32) << ShiftUp] + 0x80) >> 8;
+ const int g = (table[qGreen(rgb32) << ShiftUp] + 0x80) >> 8;
+ const int b = (table[qBlue(rgb32) << ShiftUp] + 0x80) >> 8;
return (rgb32 & 0xff000000) | (r << 16) | (g << 8) | b;
}
Q_ALWAYS_INLINE static QRgba64 convertWithTable(QRgba64 rgb64, const ushort *table)
@@ -207,7 +221,7 @@ private:
#if defined(__SSE2__)
__m128i v = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(&rgb64));
v = _mm_sub_epi16(v, _mm_srli_epi16(v, 8));
- const __m128i vidx = _mm_srli_epi16(v, 4);
+ const __m128i vidx = _mm_srli_epi16(v, ShiftDown);
const int ridx = _mm_extract_epi16(vidx, 2);
const int gidx = _mm_extract_epi16(vidx, 1);
const int bidx = _mm_extract_epi16(vidx, 0);
@@ -221,7 +235,7 @@ private:
#elif (defined(__ARM_NEON__) || defined(__ARM_NEON)) && Q_BYTE_ORDER == Q_LITTLE_ENDIAN
uint16x4_t v = vreinterpret_u16_u64(vmov_n_u64(rgb64));
v = vsub_u16(v, vshr_n_u16(v, 8));
- const uint16x4_t vidx = vshr_n_u16(v, 4);
+ const uint16x4_t vidx = vshr_n_u16(v, ShiftDown);
const int ridx = vget_lane_u16(vidx, 2);
const int gidx = vget_lane_u16(vidx, 1);
const int bidx = vget_lane_u16(vidx, 0);
@@ -237,9 +251,9 @@ private:
r = r - (r >> 8);
g = g - (g >> 8);
b = b - (b >> 8);
- r = table[r >> 4];
- g = table[g >> 4];
- b = table[b >> 4];
+ r = table[r >> ShiftDown];
+ g = table[g >> ShiftDown];
+ b = table[b >> ShiftDown];
r = r + (r >> 8);
g = g + (g >> 8);
b = b + (b >> 8);
diff --git a/src/gui/painting/qcompositionfunctions.cpp b/src/gui/painting/qcompositionfunctions.cpp
index 46e1a812fc..00fd749fe6 100644
--- a/src/gui/painting/qcompositionfunctions.cpp
+++ b/src/gui/painting/qcompositionfunctions.cpp
@@ -397,25 +397,25 @@ struct RgbaFPOperationsSSE2 : public RgbaFPOperationsBase
typedef __m128 OptimalType;
typedef __m128 OptimalScalar;
- static OptimalType load(const Type *ptr)
+ static OptimalType Q_DECL_VECTORCALL load(const Type *ptr)
{
- return _mm_load_ps(reinterpret_cast<const float *>(ptr));
+ return _mm_loadu_ps(reinterpret_cast<const float *>(ptr));
}
- static OptimalType convert(const Type &value)
+ static OptimalType Q_DECL_VECTORCALL convert(const Type &value)
{
return load(&value);
}
- static void store(Type *ptr, OptimalType value)
+ static void Q_DECL_VECTORCALL store(Type *ptr, OptimalType value)
{
- _mm_store_ps(reinterpret_cast<float *>(ptr), value);
+ _mm_storeu_ps(reinterpret_cast<float *>(ptr), value);
}
- static OptimalType add(OptimalType a, OptimalType b)
+ static OptimalType Q_DECL_VECTORCALL add(OptimalType a, OptimalType b)
{
return _mm_add_ps(a, b);
}
// same as above:
// static OptimalScalar add(OptimalScalar a, OptimalScalar b)
- static OptimalType plus(OptimalType a, OptimalType b)
+ static OptimalType Q_DECL_VECTORCALL plus(OptimalType a, OptimalType b)
{
a = _mm_add_ps(a, b);
__m128 aa = _mm_min_ps(a, _mm_set1_ps(1.0f));
@@ -425,37 +425,37 @@ struct RgbaFPOperationsSSE2 : public RgbaFPOperationsBase
a = _mm_shuffle_ps(a, aa, _MM_SHUFFLE(0, 2, 1, 0));
return a;
}
- static OptimalScalar alpha(OptimalType c)
+ static OptimalScalar Q_DECL_VECTORCALL alpha(OptimalType c)
{
return _mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3));
}
- static OptimalScalar invAlpha(Scalar c)
+ static OptimalScalar Q_DECL_VECTORCALL invAlpha(Scalar c)
{
return _mm_set1_ps(1.0f - float(c));
}
- static OptimalScalar invAlpha(OptimalType c)
+ static OptimalScalar Q_DECL_VECTORCALL invAlpha(OptimalType c)
{
return _mm_sub_ps(_mm_set1_ps(1.0f), alpha(c));
}
- static OptimalScalar scalar(Scalar n)
+ static OptimalScalar Q_DECL_VECTORCALL scalar(Scalar n)
{
return _mm_set1_ps(float(n));
}
- static OptimalType multiplyAlpha(OptimalType val, OptimalScalar a)
+ static OptimalType Q_DECL_VECTORCALL multiplyAlpha(OptimalType val, OptimalScalar a)
{
return _mm_mul_ps(val, a);
}
- static OptimalType interpolate(OptimalType x, OptimalScalar a1, OptimalType y, OptimalScalar a2)
+ static OptimalType Q_DECL_VECTORCALL interpolate(OptimalType x, OptimalScalar a1, OptimalType y, OptimalScalar a2)
{
return add(multiplyAlpha(x, a1), multiplyAlpha(y, a2));
}
- static OptimalType multiplyAlpha8bit(OptimalType val, uint8_t a)
+ static OptimalType Q_DECL_VECTORCALL multiplyAlpha8bit(OptimalType val, uint8_t a)
{
return multiplyAlpha(val, _mm_set1_ps(a * (1.0f / 255.0f)));
}
// same as above:
// static OptimalScalar multiplyAlpha8bit(OptimalScalar a, uint8_t a)
- static OptimalType interpolate8bit(OptimalType x, uint8_t a1, OptimalType y, uint8_t a2)
+ static OptimalType Q_DECL_VECTORCALL interpolate8bit(OptimalType x, uint8_t a1, OptimalType y, uint8_t a2)
{
return add(multiplyAlpha8bit(x, a1), multiplyAlpha8bit(y, a2));
}
diff --git a/src/gui/painting/qcoregraphics.mm b/src/gui/painting/qcoregraphics.mm
index ed44efc5ec..27b46202f5 100644
--- a/src/gui/painting/qcoregraphics.mm
+++ b/src/gui/painting/qcoregraphics.mm
@@ -132,7 +132,7 @@ QT_END_NAMESPACE
auto nsImage = [[[NSImage alloc] initWithSize:NSZeroSize] autorelease];
- for (QSize size : qAsConst(availableSizes)) {
+ for (QSize size : std::as_const(availableSizes)) {
QImage image = icon.pixmap(size).toImage();
if (image.isNull())
continue;
@@ -162,6 +162,9 @@ QT_BEGIN_NAMESPACE
QPixmap qt_mac_toQPixmap(const NSImage *image, const QSizeF &size)
{
+ // ### TODO: add parameter so that we can decide whether to maintain the aspect
+ // ratio of the image (positioning the image inside the pixmap of size \a size),
+ // or whether we want to fill the resulting pixmap by stretching the image.
const NSSize pixmapSize = NSMakeSize(size.width(), size.height());
QPixmap pixmap(pixmapSize.width, pixmapSize.height);
pixmap.fill(Qt::transparent);
@@ -182,6 +185,25 @@ QPixmap qt_mac_toQPixmap(const NSImage *image, const QSizeF &size)
#endif // Q_OS_MACOS
+#ifdef QT_PLATFORM_UIKIT
+
+QImage qt_mac_toQImage(const UIImage *image, QSizeF size)
+{
+ // ### TODO: same as above
+ QImage ret(size.width(), size.height(), QImage::Format_ARGB32_Premultiplied);
+ ret.fill(Qt::transparent);
+ QMacCGContext ctx(&ret);
+ if (!ctx)
+ return QImage();
+ UIGraphicsPushContext(ctx);
+ const CGRect rect = CGRectMake(0, 0, size.width(), size.height());
+ [image drawInRect:rect];
+ UIGraphicsPopContext();
+ return ret;
+}
+
+#endif // QT_PLATFORM_UIKIT
+
// ---------------------- Colors and Brushes ----------------------
QColor qt_mac_toQColor(CGColorRef color)
@@ -386,11 +408,14 @@ void QMacCGContext::initialize(QPaintDevice *paintDevice)
// Find the underlying QImage of the paint device
switch (int deviceType = paintDevice->devType()) {
case QInternal::Pixmap: {
- auto *platformPixmap = static_cast<QPixmap*>(paintDevice)->handle();
- if (platformPixmap && platformPixmap->classId() == QPlatformPixmap::RasterClass)
- initialize(platformPixmap->buffer());
- else
- qWarning() << "QMacCGContext: Unsupported pixmap class" << platformPixmap->classId();
+ if (auto *platformPixmap = static_cast<QPixmap*>(paintDevice)->handle()) {
+ if (platformPixmap->classId() == QPlatformPixmap::RasterClass)
+ initialize(platformPixmap->buffer());
+ else
+ qWarning() << "QMacCGContext: Unsupported pixmap class" << platformPixmap->classId();
+ } else {
+ qWarning() << "QMacCGContext: Empty platformPixmap";
+ }
break;
}
case QInternal::Image:
diff --git a/src/gui/painting/qcoregraphics_p.h b/src/gui/painting/qcoregraphics_p.h
index f0640ba500..a35f27a730 100644
--- a/src/gui/painting/qcoregraphics_p.h
+++ b/src/gui/painting/qcoregraphics_p.h
@@ -23,16 +23,23 @@
#include <CoreGraphics/CoreGraphics.h>
-#if defined(__OBJC__) && defined(Q_OS_MACOS)
-#include <AppKit/AppKit.h>
-#define HAVE_APPKIT
+#if defined(__OBJC__)
+# if defined(Q_OS_MACOS)
+# include <AppKit/AppKit.h>
+# elif defined(QT_PLATFORM_UIKIT)
+# include <UIKit/UIKit.h>
+# endif
#endif
QT_BEGIN_NAMESPACE
Q_GUI_EXPORT CGBitmapInfo qt_mac_bitmapInfoForImage(const QImage &image);
-#ifdef HAVE_APPKIT
+#ifdef QT_PLATFORM_UIKIT
+Q_GUI_EXPORT QImage qt_mac_toQImage(const UIImage *image, QSizeF size);
+#endif
+
+#ifdef Q_OS_MACOS
Q_GUI_EXPORT QPixmap qt_mac_toQPixmap(const NSImage *image, const QSizeF &size);
QT_END_NAMESPACE
@@ -57,7 +64,7 @@ Q_GUI_EXPORT void qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBou
Q_GUI_EXPORT void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig_xform);
-#ifdef HAVE_APPKIT
+#ifdef Q_OS_MACOS
Q_GUI_EXPORT QColor qt_mac_toQColor(const NSColor *color);
Q_GUI_EXPORT QBrush qt_mac_toQBrush(const NSColor *color, QPalette::ColorGroup colorGroup = QPalette::Normal);
#endif
@@ -81,6 +88,4 @@ private:
QT_END_NAMESPACE
-#undef HAVE_APPKIT
-
#endif // QCOREGRAPHICS_P_H
diff --git a/src/gui/painting/qcosmeticstroker.cpp b/src/gui/painting/qcosmeticstroker.cpp
index f555b56adb..a0eddf65d9 100644
--- a/src/gui/painting/qcosmeticstroker.cpp
+++ b/src/gui/painting/qcosmeticstroker.cpp
@@ -364,7 +364,7 @@ void QCosmeticStroker::drawPoints(const QPoint *points, int num)
const QPoint *end = points + num;
while (points < end) {
QPointF p = QPointF(*points) * state->matrix;
- drawPixel(this, qRound(p.x()), qRound(p.y()), 255);
+ drawPixel(this, std::floor(p.x()), std::floor(p.y()), 255);
++points;
}
@@ -377,7 +377,7 @@ void QCosmeticStroker::drawPoints(const QPointF *points, int num)
const QPointF *end = points + num;
while (points < end) {
QPointF p = (*points) * state->matrix;
- drawPixel(this, qRound(p.x()), qRound(p.y()), 255);
+ drawPixel(this, std::floor(p.x()), std::floor(p.y()), 255);
++points;
}
diff --git a/src/gui/painting/qcssutil.cpp b/src/gui/painting/qcssutil.cpp
index db9b7c24fe..caface7d1a 100644
--- a/src/gui/painting/qcssutil.cpp
+++ b/src/gui/painting/qcssutil.cpp
@@ -168,28 +168,28 @@ void qDrawEdge(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2, qreal dw1, q
if (width == 1 || (dw1 == 0 && dw2 == 0)) {
p->drawRect(QRectF(x1, y1, x2-x1, y2-y1));
} else { // draw trapezoid
- QPolygonF quad;
+ std::array<QPointF, 4> quad;
switch (edge) {
case TopEdge:
- quad << QPointF(x1, y1) << QPointF(x1 + dw1, y2)
- << QPointF(x2 - dw2, y2) << QPointF(x2, y1);
+ quad = {QPointF(x1, y1), QPointF(x1 + dw1, y2),
+ QPointF(x2 - dw2, y2), QPointF(x2, y1)};
break;
case BottomEdge:
- quad << QPointF(x1 + dw1, y1) << QPointF(x1, y2)
- << QPointF(x2, y2) << QPointF(x2 - dw2, y1);
+ quad = {QPointF(x1 + dw1, y1), QPointF(x1, y2),
+ QPointF(x2, y2), QPointF(x2 - dw2, y1)};
break;
case LeftEdge:
- quad << QPointF(x1, y1) << QPointF(x1, y2)
- << QPointF(x2, y2 - dw2) << QPointF(x2, y1 + dw1);
+ quad = {QPointF(x1, y1), QPointF(x1, y2),
+ QPointF(x2, y2 - dw2), QPointF(x2, y1 + dw1)};
break;
case RightEdge:
- quad << QPointF(x1, y1 + dw1) << QPointF(x1, y2 - dw2)
- << QPointF(x2, y2) << QPointF(x2, y1);
+ quad = {QPointF(x1, y1 + dw1), QPointF(x1, y2 - dw2),
+ QPointF(x2, y2), QPointF(x2, y1)};
break;
default:
break;
}
- p->drawConvexPolygon(quad);
+ p->drawConvexPolygon(quad.data(), static_cast<int>(quad.size()));
}
break;
}
@@ -268,6 +268,7 @@ void qDrawEdge(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2, qreal dw1, q
default:
break;
}
+ break;
}
default:
break;
diff --git a/src/gui/painting/qdatabuffer_p.h b/src/gui/painting/qdatabuffer_p.h
index b8bb44de6c..8f467afe4e 100644
--- a/src/gui/painting/qdatabuffer_p.h
+++ b/src/gui/painting/qdatabuffer_p.h
@@ -26,7 +26,7 @@ template <typename Type> class QDataBuffer
{
Q_DISABLE_COPY_MOVE(QDataBuffer)
public:
- QDataBuffer(int res)
+ explicit QDataBuffer(qsizetype res)
{
capacity = res;
if (res) {
@@ -51,11 +51,11 @@ public:
inline bool isEmpty() const { return siz==0; }
- inline int size() const { return siz; }
+ qsizetype size() const { return siz; }
inline Type *data() const { return buffer; }
- inline Type &at(int i) { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; }
- inline const Type &at(int i) const { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; }
+ Type &at(qsizetype i) { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; }
+ const Type &at(qsizetype i) const { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; }
inline Type &last() { Q_ASSERT(!isEmpty()); return buffer[siz-1]; }
inline const Type &last() const { Q_ASSERT(!isEmpty()); return buffer[siz-1]; }
inline Type &first() { Q_ASSERT(!isEmpty()); return buffer[0]; }
@@ -72,12 +72,12 @@ public:
--siz;
}
- inline void resize(int size) {
+ void resize(qsizetype size) {
reserve(size);
siz = size;
}
- inline void reserve(int size) {
+ void reserve(qsizetype size) {
if (size > capacity) {
if (capacity == 0)
capacity = 1;
@@ -88,14 +88,17 @@ public:
}
}
- inline void shrink(int size) {
+ void shrink(qsizetype size) {
+ Q_ASSERT(capacity >= size);
capacity = size;
if (size) {
buffer = (Type*) realloc(static_cast<void*>(buffer), capacity * sizeof(Type));
Q_CHECK_PTR(buffer);
+ siz = std::min(siz, size);
} else {
free(buffer);
buffer = nullptr;
+ siz = 0;
}
}
@@ -108,8 +111,8 @@ public:
inline QDataBuffer &operator<<(const Type &t) { add(t); return *this; }
private:
- int capacity;
- int siz;
+ qsizetype capacity;
+ qsizetype siz;
Type *buffer;
};
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
index 35c44b8286..b7a943be38 100644
--- a/src/gui/painting/qdrawhelper.cpp
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -32,6 +32,7 @@
#if defined(QT_USE_THREAD_PARALLEL_FILLS)
#include <qsemaphore.h>
#include <qthreadpool.h>
+#include <private/qthreadpool_p.h>
#endif
QT_BEGIN_NAMESPACE
@@ -50,8 +51,7 @@ constexpr int half_point = 1 << 15;
template <QPixelLayout::BPP bpp> static
inline uint QT_FASTCALL fetch1Pixel(const uchar *, int)
{
- Q_UNREACHABLE();
- return 0;
+ Q_UNREACHABLE_RETURN(0);
}
template <>
@@ -176,7 +176,7 @@ static void QT_FASTCALL convertRGBA32FPMToRGBA64PM(QRgba64 *buffer, int count)
}
}
-static Convert64Func convert64ToRGBA64PM[QImage::NImageFormats] = {
+static Convert64Func convert64ToRGBA64PM[] = {
nullptr,
nullptr,
nullptr,
@@ -213,7 +213,10 @@ static Convert64Func convert64ToRGBA64PM[QImage::NImageFormats] = {
convertRGBA32FPMToRGBA64PM,
convertRGBA32FToRGBA64PM,
convertRGBA32FPMToRGBA64PM,
+ nullptr,
};
+
+static_assert(std::size(convert64ToRGBA64PM) == QImage::NImageFormats);
#endif
#if QT_CONFIG(raster_fp)
@@ -247,7 +250,7 @@ static void QT_FASTCALL convertRGBA16FToRGBA32F(QRgbaFloat32 *buffer, const quin
qFloatFromFloat16((float *)buffer, (const qfloat16 *)src, count * 4);
}
-static Convert64ToFPFunc convert64ToRGBA32F[QImage::NImageFormats] = {
+static Convert64ToFPFunc convert64ToRGBA32F[] = {
nullptr,
nullptr,
nullptr,
@@ -284,8 +287,11 @@ static Convert64ToFPFunc convert64ToRGBA32F[QImage::NImageFormats] = {
nullptr,
nullptr,
nullptr,
+ nullptr,
};
+static_assert(std::size(convert64ToRGBA32F) == QImage::NImageFormats);
+
static void convertRGBA32FToRGBA32FPM(QRgbaFloat32 *buffer, int count)
{
for (int i = 0; i < count; ++i)
@@ -353,7 +359,7 @@ static uint *QT_FASTCALL destFetchUndefined(uint *buffer, QRasterBuffer *, int,
return buffer;
}
-static DestFetchProc destFetchProc[QImage::NImageFormats] =
+static DestFetchProc destFetchProc[] =
{
nullptr, // Format_Invalid
destFetchMono, // Format_Mono,
@@ -391,8 +397,11 @@ static DestFetchProc destFetchProc[QImage::NImageFormats] =
destFetch, // Format_RGBX32FPx4
destFetch, // Format_RGBA32FPx4
destFetch, // Format_RGBA32FPx4_Premultiplied
+ destFetch, // Format_CMYK8888
};
+static_assert(std::size(destFetchProc) == QImage::NImageFormats);
+
#if QT_CONFIG(raster_64bit)
static QRgba64 *QT_FASTCALL destFetch64(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
{
@@ -410,7 +419,7 @@ static QRgba64 * QT_FASTCALL destFetch64Undefined(QRgba64 *buffer, QRasterBuffer
return buffer;
}
-static DestFetchProc64 destFetchProc64[QImage::NImageFormats] =
+static DestFetchProc64 destFetchProc64[] =
{
nullptr, // Format_Invalid
nullptr, // Format_Mono,
@@ -448,7 +457,10 @@ static DestFetchProc64 destFetchProc64[QImage::NImageFormats] =
destFetch64, // Format_RGBX32FPx4
destFetch64, // Format_RGBA32FPx4
destFetch64, // Format_RGBA32FPx4_Premultiplied
+ destFetch64, // Format_CMYK8888
};
+
+static_assert(std::size(destFetchProc64) == QImage::NImageFormats);
#endif
#if QT_CONFIG(raster_fp)
@@ -466,7 +478,7 @@ static QRgbaFloat32 *QT_FASTCALL destFetchFPUndefined(QRgbaFloat32 *buffer, QRas
{
return buffer;
}
-static DestFetchProcFP destFetchProcFP[QImage::NImageFormats] =
+static DestFetchProcFP destFetchProcFP[] =
{
nullptr, // Format_Invalid
nullptr, // Format_Mono,
@@ -504,7 +516,10 @@ static DestFetchProcFP destFetchProcFP[QImage::NImageFormats] =
destFetchRGBFP, // Format_RGBX32FPx4
destFetchFP, // Format_RGBA32FPx4
destFetchRGBFP, // Format_RGBA32FPx4_Premultiplied
+ destFetchFP, // Format_CMYK8888
};
+
+static_assert(std::size(destFetchProcFP) == QImage::NImageFormats);
#endif
/*
@@ -513,9 +528,8 @@ static DestFetchProcFP destFetchProcFP[QImage::NImageFormats] =
*/
static inline QRgb findNearestColor(QRgb color, QRasterBuffer *rbuf)
{
- QRgb color_0 = qPremultiply(rbuf->destColor0);
- QRgb color_1 = qPremultiply(rbuf->destColor1);
- color = qPremultiply(color);
+ const QRgb color_0 = rbuf->destColor0;
+ const QRgb color_1 = rbuf->destColor1;
int r = qRed(color);
int g = qGreen(color);
@@ -630,7 +644,7 @@ static void QT_FASTCALL destStoreGray8(QRasterBuffer *rasterBuffer, int x, int y
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
- tfd->apply(data, buffer, length, QColorTransformPrivate::InputPremultiplied);
+ tfd->applyReturnGray(data, buffer, length, QColorTransformPrivate::InputPremultiplied);
}
}
@@ -654,11 +668,11 @@ static void QT_FASTCALL destStoreGray16(QRasterBuffer *rasterBuffer, int x, int
QRgba64 tmp_line[BufferSize];
for (int k = 0; k < length; ++k)
tmp_line[k] = QRgba64::fromArgb32(buffer[k]);
- tfd->apply(data, tmp_line, length, QColorTransformPrivate::InputPremultiplied);
+ tfd->applyReturnGray(data, tmp_line, length, QColorTransformPrivate::InputPremultiplied);
}
}
-static DestStoreProc destStoreProc[QImage::NImageFormats] =
+static DestStoreProc destStoreProc[] =
{
nullptr, // Format_Invalid
destStoreMono, // Format_Mono,
@@ -696,8 +710,11 @@ static DestStoreProc destStoreProc[QImage::NImageFormats] =
destStore, // Format_RGBX32FPx4
destStore, // Format_RGBA32FPx4
destStore, // Format_RGBA32FPx4_Premultiplied
+ destStore, // Format_CMYK8888
};
+static_assert(std::size(destStoreProc) == QImage::NImageFormats);
+
#if QT_CONFIG(raster_64bit)
static void QT_FASTCALL destStore64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
{
@@ -732,7 +749,7 @@ static void QT_FASTCALL destStore64Gray8(QRasterBuffer *rasterBuffer, int x, int
QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
quint16 gray_line[BufferSize];
- tfd->apply(gray_line, buffer, length, QColorTransformPrivate::InputPremultiplied);
+ tfd->applyReturnGray(gray_line, buffer, length, QColorTransformPrivate::InputPremultiplied);
for (int k = 0; k < length; ++k)
data[k] = qt_div_257(gray_line[k]);
}
@@ -754,11 +771,11 @@ static void QT_FASTCALL destStore64Gray16(QRasterBuffer *rasterBuffer, int x, in
QColorSpace fromCS = rasterBuffer->colorSpace.isValid() ? rasterBuffer->colorSpace : QColorSpace::SRgb;
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
- tfd->apply(data, buffer, length, QColorTransformPrivate::InputPremultiplied);
+ tfd->applyReturnGray(data, buffer, length, QColorTransformPrivate::InputPremultiplied);
}
}
-static DestStoreProc64 destStoreProc64[QImage::NImageFormats] =
+static DestStoreProc64 destStoreProc64[] =
{
nullptr, // Format_Invalid
nullptr, // Format_Mono,
@@ -796,7 +813,10 @@ static DestStoreProc64 destStoreProc64[QImage::NImageFormats] =
destStore64, // Format_RGBX32FPx4
destStore64, // Format_RGBA32FPx4
destStore64, // Format_RGBA32FPx4_Premultiplied
+ destStore64, // Format_CMYK8888
};
+
+static_assert(std::size(destStoreProc64) == QImage::NImageFormats);
#endif
#if QT_CONFIG(raster_fp)
@@ -3071,7 +3091,7 @@ static const QRgbaFloat32 *QT_FASTCALL fetchTransformedBilinearFP(QRgbaFloat32 *
#endif // QT_CONFIG(raster_fp)
// FetchUntransformed can have more specialized methods added depending on SIMD features.
-static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = {
+static SourceFetchProc sourceFetchUntransformed[] = {
nullptr, // Invalid
fetchUntransformed, // Mono
fetchUntransformed, // MonoLsb
@@ -3108,9 +3128,12 @@ static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = {
fetchUntransformed, // RGBX32Px4
fetchUntransformed, // RGBA32FPx4
fetchUntransformed, // RGBA32FPx4_Premultiplied
+ fetchUntransformed, // CMYK8888
};
-static const SourceFetchProc sourceFetchGeneric[NBlendTypes] = {
+static_assert(std::size(sourceFetchUntransformed) == QImage::NImageFormats);
+
+static const SourceFetchProc sourceFetchGeneric[] = {
fetchUntransformed, // Untransformed
fetchUntransformed, // Tiled
fetchTransformed<BlendTransformed, QPixelLayout::BPPNone>, // Transformed
@@ -3119,7 +3142,9 @@ static const SourceFetchProc sourceFetchGeneric[NBlendTypes] = {
fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPPNone> // TransformedBilinearTiled
};
-static SourceFetchProc sourceFetchARGB32PM[NBlendTypes] = {
+static_assert(std::size(sourceFetchGeneric) == NBlendTypes);
+
+static SourceFetchProc sourceFetchARGB32PM[] = {
fetchUntransformedARGB32PM, // Untransformed
fetchUntransformedARGB32PM, // Tiled
fetchTransformed<BlendTransformed, QPixelLayout::BPP32>, // Transformed
@@ -3128,7 +3153,9 @@ static SourceFetchProc sourceFetchARGB32PM[NBlendTypes] = {
fetchTransformedBilinearARGB32PM<BlendTransformedBilinearTiled> // BilinearTiled
};
-static SourceFetchProc sourceFetchAny16[NBlendTypes] = {
+static_assert(std::size(sourceFetchARGB32PM) == NBlendTypes);
+
+static SourceFetchProc sourceFetchAny16[] = {
fetchUntransformed, // Untransformed
fetchUntransformed, // Tiled
fetchTransformed<BlendTransformed, QPixelLayout::BPP16>, // Transformed
@@ -3137,7 +3164,9 @@ static SourceFetchProc sourceFetchAny16[NBlendTypes] = {
fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP16> // TransformedBilinearTiled
};
-static SourceFetchProc sourceFetchAny32[NBlendTypes] = {
+static_assert(std::size(sourceFetchAny16) == NBlendTypes);
+
+static SourceFetchProc sourceFetchAny32[] = {
fetchUntransformed, // Untransformed
fetchUntransformed, // Tiled
fetchTransformed<BlendTransformed, QPixelLayout::BPP32>, // Transformed
@@ -3146,6 +3175,8 @@ static SourceFetchProc sourceFetchAny32[NBlendTypes] = {
fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP32> // TransformedBilinearTiled
};
+static_assert(std::size(sourceFetchAny32) == NBlendTypes);
+
static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage::Format format)
{
if (format == QImage::Format_RGB32 || format == QImage::Format_ARGB32_Premultiplied)
@@ -3160,7 +3191,7 @@ static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage:
}
#if QT_CONFIG(raster_64bit)
-static const SourceFetchProc64 sourceFetchGeneric64[NBlendTypes] = {
+static const SourceFetchProc64 sourceFetchGeneric64[] = {
fetchUntransformed64, // Untransformed
fetchUntransformed64, // Tiled
fetchTransformed64<BlendTransformed>, // Transformed
@@ -3169,7 +3200,9 @@ static const SourceFetchProc64 sourceFetchGeneric64[NBlendTypes] = {
fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled
};
-static const SourceFetchProc64 sourceFetchRGBA64PM[NBlendTypes] = {
+static_assert(std::size(sourceFetchGeneric64) == NBlendTypes);
+
+static const SourceFetchProc64 sourceFetchRGBA64PM[] = {
fetchUntransformedRGBA64PM, // Untransformed
fetchUntransformedRGBA64PM, // Tiled
fetchTransformed64<BlendTransformed>, // Transformed
@@ -3178,6 +3211,8 @@ static const SourceFetchProc64 sourceFetchRGBA64PM[NBlendTypes] = {
fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled
};
+static_assert(std::size(sourceFetchRGBA64PM) == NBlendTypes);
+
static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QImage::Format format)
{
if (format == QImage::Format_RGBX64 || format == QImage::Format_RGBA64_Premultiplied)
@@ -3187,7 +3222,7 @@ static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QIm
#endif
#if QT_CONFIG(raster_fp)
-static const SourceFetchProcFP sourceFetchGenericFP[NBlendTypes] = {
+static const SourceFetchProcFP sourceFetchGenericFP[] = {
fetchUntransformedFP, // Untransformed
fetchUntransformedFP, // Tiled
fetchTransformedFP<BlendTransformed>, // Transformed
@@ -3196,6 +3231,8 @@ static const SourceFetchProcFP sourceFetchGenericFP[NBlendTypes] = {
fetchTransformedBilinearFP<BlendTransformedBilinearTiled> // BilinearTiled
};
+static_assert(std::size(sourceFetchGenericFP) == NBlendTypes);
+
static inline SourceFetchProcFP getSourceFetchFP(TextureBlendType blendType, QImage::Format /*format*/)
{
return sourceFetchGenericFP[blendType];
@@ -3256,6 +3293,7 @@ public:
static Type null() { return 0; }
static Type fetchSingle(const QGradientData& gradient, qreal v)
{
+ Q_ASSERT(std::isfinite(v));
return qt_gradient_pixel(&gradient, v);
}
static Type fetchSingle(const QGradientData& gradient, int v)
@@ -3276,6 +3314,7 @@ public:
static Type null() { return QRgba64::fromRgba64(0); }
static Type fetchSingle(const QGradientData& gradient, qreal v)
{
+ Q_ASSERT(std::isfinite(v));
return qt_gradient_pixel64(&gradient, v);
}
static Type fetchSingle(const QGradientData& gradient, int v)
@@ -3297,6 +3336,7 @@ public:
static Type null() { return QRgbaFloat32::fromRgba64(0,0,0,0); }
static Type fetchSingle(const QGradientData& gradient, qreal v)
{
+ Q_ASSERT(std::isfinite(v));
return qt_gradient_pixelFP(&gradient, v);
}
static Type fetchSingle(const QGradientData& gradient, int v)
@@ -3414,7 +3454,6 @@ static void QT_FASTCALL getRadialGradientValues(RadialGradientValues *v, const Q
v->sqrfr = data->gradient.radial.focal.radius * data->gradient.radial.focal.radius;
v->a = v->dr * v->dr - v->dx*v->dx - v->dy*v->dy;
- v->inv2a = 1 / (2 * v->a);
v->extended = !qFuzzyIsNull(data->gradient.radial.focal.radius) || v->a <= 0;
}
@@ -3447,7 +3486,13 @@ public:
}
} else {
while (buffer < end) {
- *buffer++ = GradientBase::fetchSingle(data->gradient, qSqrt(det) - b);
+ BlendType result = GradientBase::null();
+ if (det >= 0) {
+ qreal w = qSqrt(det) - b;
+ result = GradientBase::fetchSingle(data->gradient, w);
+ }
+
+ *buffer++ = result;
det += delta_det;
delta_det += delta_delta_det;
@@ -3601,11 +3646,10 @@ static TextureBlendType getBlendType(const QSpanData *data)
return ft;
}
-static inline Operator getOperator(const QSpanData *data, const QSpan *spans, int spanCount)
+static inline Operator getOperator(const QSpanData *data, const QT_FT_Span *spans, int spanCount)
{
Operator op;
bool solidSource = false;
-
switch(data->type) {
case QSpanData::Solid:
solidSource = data->solidColor.alphaF() >= 1.0f;
@@ -3649,7 +3693,7 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in
solidSource = !data->texture.hasAlpha;
op.srcFetch = getSourceFetch(getBlendType(data), data->texture.format);
#if QT_CONFIG(raster_64bit)
- op.srcFetch64 = getSourceFetch64(getBlendType(data), data->texture.format);;
+ op.srcFetch64 = getSourceFetch64(getBlendType(data), data->texture.format);
#endif
#if QT_CONFIG(raster_fp)
op.srcFetchFP = getSourceFetchFP(getBlendType(data), data->texture.format);
@@ -3683,7 +3727,7 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in
#endif
if (op.mode == QPainter::CompositionMode_Source &&
(data->type != QSpanData::Texture || data->texture.const_alpha == 256)) {
- const QSpan *lastSpan = spans + spanCount;
+ const QT_FT_Span *lastSpan = spans + spanCount;
bool alphaSpans = false;
while (spans < lastSpan) {
if (spans->coverage != 255) {
@@ -3778,8 +3822,9 @@ static void spanfill_from_first(QRasterBuffer *rasterBuffer, QPixelLayout::BPP b
#if defined(QT_USE_THREAD_PARALLEL_FILLS)
#define QT_THREAD_PARALLEL_FILLS(function) \
const int segments = (count + 32) / 64; \
- QThreadPool *threadPool = QThreadPool::globalInstance(); \
- if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) { \
+ QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance(); \
+ if (segments > 1 && qPixelLayouts[data->rasterBuffer->format].bpp >= QPixelLayout::BPP8 \
+ && threadPool && !threadPool->contains(QThread::currentThread())) { \
QSemaphore semaphore; \
int c = 0; \
for (int i = 0; i < segments; ++i) { \
@@ -3787,7 +3832,7 @@ static void spanfill_from_first(QRasterBuffer *rasterBuffer, QPixelLayout::BPP b
threadPool->start([&, c, cn]() { \
function(c, c + cn); \
semaphore.release(1); \
- }); \
+ }, 1); \
c += cn; \
} \
semaphore.acquire(segments); \
@@ -3797,7 +3842,7 @@ static void spanfill_from_first(QRasterBuffer *rasterBuffer, QPixelLayout::BPP b
#define QT_THREAD_PARALLEL_FILLS(function) function(0, count)
#endif
-static void blend_color_generic(int count, const QSpan *spans, void *userData)
+static void blend_color_generic(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
const Operator op = getOperator(data, nullptr, 0);
@@ -3831,7 +3876,7 @@ static void blend_color_generic(int count, const QSpan *spans, void *userData)
QT_THREAD_PARALLEL_FILLS(function);
}
-static void blend_color_argb(int count, const QSpan *spans, void *userData)
+static void blend_color_argb(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
@@ -3868,7 +3913,7 @@ static void blend_color_argb(int count, const QSpan *spans, void *userData)
QT_THREAD_PARALLEL_FILLS(function);
}
-static void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData)
+static void blend_color_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
{
#if QT_CONFIG(raster_64bit)
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
@@ -3912,7 +3957,7 @@ static void blend_color_generic_rgb64(int count, const QSpan *spans, void *userD
#endif
}
-static void blend_color_generic_fp(int count, const QSpan *spans, void *userData)
+static void blend_color_generic_fp(int count, const QT_FT_Span *spans, void *userData)
{
#if QT_CONFIG(raster_fp)
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
@@ -3959,7 +4004,7 @@ static void blend_color_generic_fp(int count, const QSpan *spans, void *userData
}
template <typename T>
-void handleSpans(int count, const QSpan *spans, const QSpanData *data, const Operator &op)
+void handleSpans(int count, const QT_FT_Span *spans, const QSpanData *data, const Operator &op)
{
const int const_alpha = (data->type == QSpanData::Texture) ? data->texture.const_alpha : 256;
const bool solidSource = op.mode == QPainter::CompositionMode_Source && const_alpha == 256;
@@ -4131,7 +4176,7 @@ public:
};
#endif
-static void blend_src_generic(int count, const QSpan *spans, void *userData)
+static void blend_src_generic(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
const Operator op = getOperator(data, nullptr, 0);
@@ -4139,7 +4184,7 @@ static void blend_src_generic(int count, const QSpan *spans, void *userData)
}
#if QT_CONFIG(raster_64bit)
-static void blend_src_generic_rgb64(int count, const QSpan *spans, void *userData)
+static void blend_src_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
const Operator op = getOperator(data, nullptr, 0);
@@ -4153,7 +4198,7 @@ static void blend_src_generic_rgb64(int count, const QSpan *spans, void *userDat
#endif
#if QT_CONFIG(raster_fp)
-static void blend_src_generic_fp(int count, const QSpan *spans, void *userData)
+static void blend_src_generic_fp(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
const Operator op = getOperator(data, spans, count);
@@ -4166,7 +4211,7 @@ static void blend_src_generic_fp(int count, const QSpan *spans, void *userData)
}
#endif
-static void blend_untransformed_generic(int count, const QSpan *spans, void *userData)
+static void blend_untransformed_generic(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
@@ -4220,7 +4265,7 @@ static void blend_untransformed_generic(int count, const QSpan *spans, void *use
}
#if QT_CONFIG(raster_64bit)
-static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, void *userData)
+static void blend_untransformed_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
@@ -4279,7 +4324,7 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi
#endif
#if QT_CONFIG(raster_fp)
-static void blend_untransformed_generic_fp(int count, const QSpan *spans, void *userData)
+static void blend_untransformed_generic_fp(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
@@ -4336,7 +4381,7 @@ static void blend_untransformed_generic_fp(int count, const QSpan *spans, void *
}
#endif
-static void blend_untransformed_argb(int count, const QSpan *spans, void *userData)
+static void blend_untransformed_argb(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
if (data->texture.format != QImage::Format_ARGB32_Premultiplied
@@ -4433,7 +4478,7 @@ static inline void blend_sourceOver_rgb16_rgb16(quint16 *Q_DECL_RESTRICT dest,
}
}
-static void blend_untransformed_rgb565(int count, const QSpan *spans, void *userData)
+static void blend_untransformed_rgb565(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData*>(userData);
QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
@@ -4490,7 +4535,7 @@ static void blend_untransformed_rgb565(int count, const QSpan *spans, void *user
QT_THREAD_PARALLEL_FILLS(function);
}
-static void blend_tiled_generic(int count, const QSpan *spans, void *userData)
+static void blend_tiled_generic(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
@@ -4543,7 +4588,7 @@ static void blend_tiled_generic(int count, const QSpan *spans, void *userData)
}
#if QT_CONFIG(raster_64bit)
-static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userData)
+static void blend_tiled_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
@@ -4651,7 +4696,7 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD
#endif
#if QT_CONFIG(raster_fp)
-static void blend_tiled_generic_fp(int count, const QSpan *spans, void *userData)
+static void blend_tiled_generic_fp(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
@@ -4709,7 +4754,7 @@ static void blend_tiled_generic_fp(int count, const QSpan *spans, void *userData
}
#endif
-static void blend_tiled_argb(int count, const QSpan *spans, void *userData)
+static void blend_tiled_argb(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
if (data->texture.format != QImage::Format_ARGB32_Premultiplied
@@ -4762,7 +4807,7 @@ static void blend_tiled_argb(int count, const QSpan *spans, void *userData)
QT_THREAD_PARALLEL_FILLS(function);
}
-static void blend_tiled_rgb565(int count, const QSpan *spans, void *userData)
+static void blend_tiled_rgb565(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData*>(userData);
QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
@@ -4910,15 +4955,14 @@ static const ProcessSpans processTextureSpansGenericFP[NBlendTypes] = {
blend_src_generic_fp // TransformedBilinearTiled
};
#endif
-void qBlendTexture(int count, const QSpan *spans, void *userData)
+void qBlendTexture(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
TextureBlendType blendType = getBlendType(data);
ProcessSpans proc;
switch (data->rasterBuffer->format) {
case QImage::Format_Invalid:
- Q_UNREACHABLE();
- return;
+ Q_UNREACHABLE_RETURN();
case QImage::Format_ARGB32_Premultiplied:
proc = processTextureSpansARGB32PM[blendType];
break;
@@ -4966,16 +5010,11 @@ void qBlendTexture(int count, const QSpan *spans, void *userData)
proc(count, spans, userData);
}
-static void blend_vertical_gradient_argb(int count, const QSpan *spans, void *userData)
+static inline bool calculate_fixed_gradient_factors(int count, const QT_FT_Span *spans,
+ const QSpanData *data,
+ const LinearGradientValues &linear,
+ int *pyinc, int *poff)
{
- QSpanData *data = reinterpret_cast<QSpanData *>(userData);
-
- LinearGradientValues linear;
- getLinearGradientValues(&linear, data);
-
- CompositionFunctionSolid funcSolid =
- functionForModeSolid[data->rasterBuffer->compositionMode];
-
/*
The logic for vertical gradient calculations is a mathematically
reduced copy of that in fetchLinearGradient() - which is basically:
@@ -4990,8 +5029,32 @@ static void blend_vertical_gradient_argb(int count, const QSpan *spans, void *us
This has then been converted to fixed point to improve performance.
*/
const int gss = GRADIENT_STOPTABLE_SIZE - 1;
- int yinc = int((linear.dy * data->m22 * gss) * FIXPT_SIZE);
- int off = int((((linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss) * FIXPT_SIZE));
+ qreal ryinc = linear.dy * data->m22 * gss * FIXPT_SIZE;
+ qreal roff = (linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss * FIXPT_SIZE;
+ const int limit = std::numeric_limits<int>::max() - FIXPT_SIZE;
+ if (count && (std::fabs(ryinc) < limit) && (std::fabs(roff) < limit)
+ && (std::fabs(ryinc * spans->y + roff) < limit)
+ && (std::fabs(ryinc * (spans + count - 1)->y + roff) < limit)) {
+ *pyinc = int(ryinc);
+ *poff = int(roff);
+ return true;
+ }
+ return false;
+}
+
+static bool blend_vertical_gradient_argb(int count, const QT_FT_Span *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ LinearGradientValues linear;
+ getLinearGradientValues(&linear, data);
+
+ CompositionFunctionSolid funcSolid =
+ functionForModeSolid[data->rasterBuffer->compositionMode];
+
+ int yinc(0), off(0);
+ if (!calculate_fixed_gradient_factors(count, spans, data, linear, &yinc, &off))
+ return false;
while (count--) {
int y = spans->y;
@@ -5004,21 +5067,20 @@ static void blend_vertical_gradient_argb(int count, const QSpan *spans, void *us
funcSolid(dst, spans->len, color, spans->coverage);
++spans;
}
+ return true;
}
template<ProcessSpans blend_color>
-static void blend_vertical_gradient(int count, const QSpan *spans, void *userData)
+static bool blend_vertical_gradient(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
LinearGradientValues linear;
getLinearGradientValues(&linear, data);
- // Based on the same logic as blend_vertical_gradient_argb.
-
- const int gss = GRADIENT_STOPTABLE_SIZE - 1;
- int yinc = int((linear.dy * data->m22 * gss) * FIXPT_SIZE);
- int off = int((((linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss) * FIXPT_SIZE));
+ int yinc(0), off(0);
+ if (!calculate_fixed_gradient_factors(count, spans, data, linear, &yinc, &off))
+ return false;
while (count--) {
int y = spans->y;
@@ -5031,9 +5093,10 @@ static void blend_vertical_gradient(int count, const QSpan *spans, void *userDat
blend_color(1, spans, userData);
++spans;
}
+ return true;
}
-void qBlendGradient(int count, const QSpan *spans, void *userData)
+void qBlendGradient(int count, const QT_FT_Span *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
bool isVerticalGradient =
@@ -5045,8 +5108,8 @@ void qBlendGradient(int count, const QSpan *spans, void *userData)
break;
case QImage::Format_RGB32:
case QImage::Format_ARGB32_Premultiplied:
- if (isVerticalGradient)
- return blend_vertical_gradient_argb(count, spans, userData);
+ if (isVerticalGradient && blend_vertical_gradient_argb(count, spans, userData))
+ return;
return blend_src_generic(count, spans, userData);
#if defined(__SSE2__) || defined(__ARM_NEON__) || (Q_PROCESSOR_WORDSIZE == 8)
case QImage::Format_ARGB32:
@@ -5068,8 +5131,8 @@ void qBlendGradient(int count, const QSpan *spans, void *userData)
case QImage::Format_RGBA32FPx4_Premultiplied:
#endif
#if QT_CONFIG(raster_64bit)
- if (isVerticalGradient)
- return blend_vertical_gradient<blend_color_generic_rgb64>(count, spans, userData);
+ if (isVerticalGradient && blend_vertical_gradient<blend_color_generic_rgb64>(count, spans, userData))
+ return;
return blend_src_generic_rgb64(count, spans, userData);
#endif // QT_CONFIG(raster_64bit)
#if QT_CONFIG(raster_fp)
@@ -5079,13 +5142,13 @@ void qBlendGradient(int count, const QSpan *spans, void *userData)
case QImage::Format_RGBX32FPx4:
case QImage::Format_RGBA32FPx4:
case QImage::Format_RGBA32FPx4_Premultiplied:
- if (isVerticalGradient)
- return blend_vertical_gradient<blend_color_generic_fp>(count, spans, userData);
+ if (isVerticalGradient && blend_vertical_gradient<blend_color_generic_fp>(count, spans, userData))
+ return;
return blend_src_generic_fp(count, spans, userData);
#endif
default:
- if (isVerticalGradient)
- return blend_vertical_gradient<blend_color_generic>(count, spans, userData);
+ if (isVerticalGradient && blend_vertical_gradient<blend_color_generic>(count, spans, userData))
+ return;
return blend_src_generic(count, spans, userData);
}
Q_UNREACHABLE();
@@ -5101,7 +5164,7 @@ inline void qt_bitmapblit_template(QRasterBuffer *rasterBuffer,
const int destStride = rasterBuffer->stride<DST>();
if (mapWidth > 8) {
- while (mapHeight--) {
+ while (--mapHeight >= 0) {
int x0 = 0;
int n = 0;
for (int x = 0; x < mapWidth; x += 8) {
@@ -5131,7 +5194,7 @@ inline void qt_bitmapblit_template(QRasterBuffer *rasterBuffer,
map += mapStride;
}
} else {
- while (mapHeight--) {
+ while (--mapHeight >= 0) {
int x0 = 0;
int n = 0;
for (uchar s = *map; s; s <<= 1) {
@@ -5315,7 +5378,7 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer,
const QClipData::ClipLine &line = clip->m_clipLines[yp];
for (int i=0; i<line.count; ++i) {
- const QSpan &clip = line.spans[i];
+ const QT_FT_Span &clip = line.spans[i];
int start = qMax<int>(x, clip.x);
int end = qMin<int>(x + mapWidth, clip.x + clip.len);
@@ -5389,7 +5452,7 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer,
const QClipData::ClipLine &line = clip->m_clipLines[yp];
for (int i=0; i<line.count; ++i) {
- const QSpan &clip = line.spans[i];
+ const QT_FT_Span &clip = line.spans[i];
int start = qMax<int>(x, clip.x);
int end = qMin<int>(x + mapWidth, clip.x + clip.len);
@@ -5439,7 +5502,7 @@ void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer,
if (!clip) {
quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x;
const int destStride = rasterBuffer->stride<quint16>();
- while (mapHeight--) {
+ while (--mapHeight >= 0) {
for (int i = 0; i < mapWidth; ++i)
alphamapblend_quint16(map[i], dest, i, c);
dest += destStride;
@@ -5457,7 +5520,7 @@ void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer,
quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(yp));
for (int i=0; i<line.count; ++i) {
- const QSpan &clip = line.spans[i];
+ const QT_FT_Span &clip = line.spans[i];
int start = qMax<int>(x, clip.x);
int end = qMin<int>(x + mapWidth, clip.x + clip.len);
@@ -5493,7 +5556,7 @@ static void qt_alphamapblit_argb32(QRasterBuffer *rasterBuffer,
if (!clip) {
quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
- while (mapHeight--) {
+ while (--mapHeight >= 0) {
for (int i = 0; i < mapWidth; ++i) {
const int coverage = map[i];
alphamapblend_argb32(dest + i, coverage, srcColor, c, colorProfile);
@@ -5514,7 +5577,7 @@ static void qt_alphamapblit_argb32(QRasterBuffer *rasterBuffer,
quint32 *dest = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(yp));
for (int i=0; i<line.count; ++i) {
- const QSpan &clip = line.spans[i];
+ const QT_FT_Span &clip = line.spans[i];
int start = qMax<int>(x, clip.x);
int end = qMin<int>(x + mapWidth, clip.x + clip.len);
@@ -5615,7 +5678,7 @@ static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba
static inline void rgbBlendPixel(QRgba64 &dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile)
{
// Do a gammacorrected RGB alphablend...
- const QRgba64 dlinear = colorProfile ? colorProfile->toLinear64(dst) : dst;
+ const QRgba64 dlinear = colorProfile ? colorProfile->toLinear(dst) : dst;
QRgba64 blend = rgbBlend(dlinear, slinear, coverage);
@@ -5694,7 +5757,7 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
const QClipData::ClipLine &line = clip->m_clipLines[yp];
for (int i=0; i<line.count; ++i) {
- const QSpan &clip = line.spans[i];
+ const QT_FT_Span &clip = line.spans[i];
int start = qMax<int>(x, clip.x);
int end = qMin<int>(x + mapWidth, clip.x + clip.len);
@@ -5767,7 +5830,7 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
const QClipData::ClipLine &line = clip->m_clipLines[yp];
for (int i=0; i<line.count; ++i) {
- const QSpan &clip = line.spans[i];
+ const QT_FT_Span &clip = line.spans[i];
int start = qMax<int>(x, clip.x);
int end = qMin<int>(x + mapWidth, clip.x + clip.len);
@@ -5811,7 +5874,7 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer,
if (!clip) {
quint32 *dst = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
const int destStride = rasterBuffer->stride<quint32>();
- while (mapHeight--) {
+ while (--mapHeight >= 0) {
for (int i = 0; i < mapWidth; ++i) {
const uint coverage = src[i];
alphargbblend_argb32(dst + i, coverage, srcColor, c, colorProfile);
@@ -5833,7 +5896,7 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer,
quint32 *dst = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(yp));
for (int i=0; i<line.count; ++i) {
- const QSpan &clip = line.spans[i];
+ const QT_FT_Span &clip = line.spans[i];
int start = qMax<int>(x, clip.x);
int end = qMin<int>(x + mapWidth, clip.x + clip.len);
@@ -5955,7 +6018,7 @@ static void qt_rectfill_fp32x4(QRasterBuffer *rasterBuffer,
// Map table for destination image format. Contains function pointers
// for blends of various types unto the destination
-DrawHelper qDrawHelper[QImage::NImageFormats] =
+DrawHelper qDrawHelper[] =
{
// Format_Invalid,
{ nullptr, nullptr, nullptr, nullptr, nullptr },
@@ -6232,7 +6295,9 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
},
};
-#if !defined(__SSE2__)
+static_assert(std::size(qDrawHelper) == QImage::NImageFormats);
+
+#if !defined(Q_PROCESSOR_X86)
void qt_memfill64(quint64 *dest, quint64 color, qsizetype count)
{
qt_memfill_template<quint64>(dest, color, count);
@@ -6299,16 +6364,15 @@ void qt_memfill16(quint16 *dest, quint16 value, qsizetype count)
qt_memfill32(reinterpret_cast<quint32*>(dest), value32, count / 2);
}
-#if !defined(__SSE2__) && !defined(__ARM_NEON__) && !defined(__MIPS_DSP__)
+#if defined(Q_PROCESSOR_X86)
+void (*qt_memfill32)(quint32 *dest, quint32 value, qsizetype count) = nullptr;
+void (*qt_memfill64)(quint64 *dest, quint64 value, qsizetype count) = nullptr;
+#elif !defined(__ARM_NEON__) && !defined(__MIPS_DSP__)
void qt_memfill32(quint32 *dest, quint32 color, qsizetype count)
{
qt_memfill_template<quint32>(dest, color, count);
}
#endif
-#ifdef __SSE2__
-decltype(qt_memfill32_sse2) *qt_memfill32 = nullptr;
-decltype(qt_memfill64_sse2) *qt_memfill64 = nullptr;
-#endif
#ifdef QT_COMPILER_SUPPORTS_SSE4_1
template<QtPixelOrder> void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
@@ -6321,8 +6385,11 @@ static void qInitDrawhelperFunctions()
// Set up basic blend function tables.
qInitBlendFunctions();
-#ifdef __SSE2__
-# ifndef __AVX2__
+#if defined(Q_PROCESSOR_X86) && !defined(__SSE2__)
+ qt_memfill32 = qt_memfill_template<quint32>;
+ qt_memfill64 = qt_memfill_template<quint64>;
+#elif defined(__SSE2__)
+# ifndef __haswell__
qt_memfill32 = qt_memfill32_sse2;
qt_memfill64 = qt_memfill64_sse2;
# endif
@@ -6429,7 +6496,7 @@ static void qInitDrawhelperFunctions()
extern void QT_FASTCALL storeRGBx64FromRGBA64PM_sse4(uchar *, const QRgba64 *, int, int, const QList<QRgb> *, QDitherInfo *);
extern void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
extern void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
-# ifndef __AVX2__
+# ifndef __haswell__
qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_sse4;
qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_sse4;
qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_sse4;
diff --git a/src/gui/painting/qdrawhelper_avx2.cpp b/src/gui/painting/qdrawhelper_avx2.cpp
index 17a52402ae..34de69ecf4 100644
--- a/src/gui/painting/qdrawhelper_avx2.cpp
+++ b/src/gui/painting/qdrawhelper_avx2.cpp
@@ -442,14 +442,14 @@ void QT_FASTCALL comp_func_SourceOver_rgbafp_avx2(QRgbaFloat32 *dst, const QRgba
_mm256_storeu_ps((float *)(dst + x), dstVector);
}
if (x < length) {
- __m128 srcVector = _mm_load_ps((float *)(src + x));
- __m128 dstVector = _mm_load_ps((const float *)(dst + x));
+ __m128 srcVector = _mm_loadu_ps((const float *)&src[x]);
+ __m128 dstVector = _mm_loadu_ps((const float *)&dst[x]);
srcVector = _mm_mul_ps(srcVector, constAlphaVector);
__m128 alphaChannel = _mm_permute_ps(srcVector, _MM_SHUFFLE(3, 3, 3, 3));
alphaChannel = _mm_sub_ps(one, alphaChannel);
dstVector = _mm_mul_ps(dstVector, alphaChannel);
dstVector = _mm_add_ps(dstVector, srcVector);
- _mm_store_ps((float *)(dst + x), dstVector);
+ _mm_storeu_ps((float *)(dst + x), dstVector);
}
}
#endif
@@ -544,12 +544,12 @@ void QT_FASTCALL comp_func_Source_rgbafp_avx2(QRgbaFloat32 *dst, const QRgbaFloa
_mm256_storeu_ps((float *)&dst[x], dstVector);
}
if (x < length) {
- __m128 srcVector = _mm_load_ps((const float *)&src[x]);
- __m128 dstVector = _mm_load_ps((const float *)&dst[x]);
+ __m128 srcVector = _mm_loadu_ps((const float *)&src[x]);
+ __m128 dstVector = _mm_loadu_ps((const float *)&dst[x]);
srcVector = _mm_mul_ps(srcVector, constAlphaVector);
dstVector = _mm_mul_ps(dstVector, oneMinusConstAlpha);
dstVector = _mm_add_ps(dstVector, srcVector);
- _mm_store_ps((float *)&dst[x], dstVector);
+ _mm_storeu_ps((float *)&dst[x], dstVector);
}
}
}
@@ -630,7 +630,7 @@ void QT_FASTCALL comp_func_solid_Source_rgbafp_avx2(QRgbaFloat32 *dst, int lengt
const float a = const_alpha / 255.0f;
const __m128 alphaVector = _mm_set1_ps(a);
const __m128 minusAlphaVector = _mm_set1_ps(1.0f - a);
- __m128 colorVector = _mm_load_ps((const float *)&color);
+ __m128 colorVector = _mm_loadu_ps((const float *)&color);
colorVector = _mm_mul_ps(colorVector, alphaVector);
const __m256 colorVector256 = _mm256_insertf128_ps(_mm256_castps128_ps256(colorVector), colorVector, 1);
const __m256 minusAlphaVector256 = _mm256_set1_ps(1.0f - a);
@@ -642,10 +642,10 @@ void QT_FASTCALL comp_func_solid_Source_rgbafp_avx2(QRgbaFloat32 *dst, int lengt
_mm256_storeu_ps((float *)&dst[x], dstVector);
}
if (x < length) {
- __m128 dstVector = _mm_load_ps((const float *)&dst[x]);
+ __m128 dstVector = _mm_loadu_ps((const float *)&dst[x]);
dstVector = _mm_mul_ps(dstVector, minusAlphaVector);
dstVector = _mm_add_ps(dstVector, colorVector);
- _mm_store_ps((float *)&dst[x], dstVector);
+ _mm_storeu_ps((float *)&dst[x], dstVector);
}
}
}
@@ -657,7 +657,7 @@ void QT_FASTCALL comp_func_solid_SourceOver_rgbafp_avx2(QRgbaFloat32 *dst, int l
for (int i = 0; i < length; ++i)
dst[i] = color;
} else {
- __m128 colorVector = _mm_load_ps((const float *)&color);
+ __m128 colorVector = _mm_loadu_ps((const float *)&color);
if (const_alpha != 255)
colorVector = _mm_mul_ps(colorVector, _mm_set1_ps(const_alpha / 255.f));
__m128 minusAlphaOfColorVector =
@@ -673,10 +673,10 @@ void QT_FASTCALL comp_func_solid_SourceOver_rgbafp_avx2(QRgbaFloat32 *dst, int l
_mm256_storeu_ps((float *)&dst[x], dstVector);
}
if (x < length) {
- __m128 dstVector = _mm_load_ps((const float *)&dst[x]);
+ __m128 dstVector = _mm_loadu_ps((const float *)&dst[x]);
dstVector = _mm_mul_ps(dstVector, minusAlphaOfColorVector);
dstVector = _mm_add_ps(dstVector, colorVector);
- _mm_store_ps((float *)&dst[x], dstVector);
+ _mm_storeu_ps((float *)&dst[x], dstVector);
}
}
}
@@ -1345,13 +1345,16 @@ const QRgba64 *QT_FASTCALL fetchRGBA64ToRGBA64PM_avx2(QRgba64 *buffer, const uch
vslo = _mm256_srli_epi32(vslo, 16);
vshi = _mm256_srli_epi32(vshi, 16);
vs256 = _mm256_packus_epi32(vslo, vshi);
+ vs256 = _mm256_blend_epi16(vs256, va256, 0x88);
_mm256_storeu_si256((__m256i *)(buffer + i), vs256);
}
for (; i < count; ++i) {
+ const auto a = s[i].alpha();
__m128i vs = _mm_loadl_epi64((const __m128i *)(s + i));
__m128i va = _mm_shufflelo_epi16(vs, _MM_SHUFFLE(3, 3, 3, 3));
vs = multiplyAlpha65535(vs, va);
_mm_storel_epi64((__m128i *)(buffer + i), vs);
+ buffer[i].setAlpha(a);
}
return buffer;
}
@@ -1554,7 +1557,7 @@ const QRgbaFloat32 *QT_FASTCALL fetchRGBA16FToRGBA32F_avx2(QRgbaFloat32 *buffer,
__m128 vsa = _mm_permute_ps(vsf, _MM_SHUFFLE(3, 3, 3, 3));
vsf = _mm_mul_ps(vsf, vsa);
vsf = _mm_insert_ps(vsf, vsa, 0x30);
- _mm_store_ps((float *)(buffer + i), vsf);
+ _mm_storeu_ps((float *)(buffer + i), vsf);
}
return buffer;
}
@@ -1566,7 +1569,7 @@ void QT_FASTCALL storeRGBX16FFromRGBA32F_avx2(uchar *dest, const QRgbaFloat32 *s
const __m128 *s = reinterpret_cast<const __m128 *>(src);
const __m128 zero = _mm_set_ps(1.0f, 0.0f, 0.0f, 0.0f);
for (int i = 0; i < count; ++i) {
- __m128 vsf = _mm_load_ps(reinterpret_cast<const float *>(s + i));
+ __m128 vsf = _mm_loadu_ps(reinterpret_cast<const float *>(s + i));
const __m128 vsa = _mm_permute_ps(vsf, _MM_SHUFFLE(3, 3, 3, 3));
const float a = _mm_cvtss_f32(vsa);
if (a == 1.0f)
@@ -1590,7 +1593,7 @@ void QT_FASTCALL storeRGBA16FFromRGBA32F_avx2(uchar *dest, const QRgbaFloat32 *s
const __m128 *s = reinterpret_cast<const __m128 *>(src);
const __m128 zero = _mm_set1_ps(0.0f);
for (int i = 0; i < count; ++i) {
- __m128 vsf = _mm_load_ps(reinterpret_cast<const float *>(s + i));
+ __m128 vsf = _mm_loadu_ps(reinterpret_cast<const float *>(s + i));
const __m128 vsa = _mm_permute_ps(vsf, _MM_SHUFFLE(3, 3, 3, 3));
const float a = _mm_cvtss_f32(vsa);
if (a == 1.0f)
diff --git a/src/gui/painting/qdrawhelper_mips_dsp.cpp b/src/gui/painting/qdrawhelper_mips_dsp.cpp
index af2c6598b2..86dcde2b58 100644
--- a/src/gui/painting/qdrawhelper_mips_dsp.cpp
+++ b/src/gui/painting/qdrawhelper_mips_dsp.cpp
@@ -95,7 +95,7 @@ void qt_blend_rgb16_on_rgb16_mips_dspr2(uchar *destPixels, int dbpl,
}
else {
int length = w << 1;
- while (h--) {
+ while (--h >= 0) {
memcpy(destPixels, srcPixels, length);
destPixels += dbpl;
srcPixels += sbpl;
@@ -130,7 +130,7 @@ void qt_blend_rgb16_on_rgb16_mips_dsp(uchar *destPixels, int dbpl,
}
else {
int length = w << 1;
- while (h--) {
+ while (--h >= 0) {
memcpy(destPixels, srcPixels, length);
destPixels += dbpl;
srcPixels += sbpl;
diff --git a/src/gui/painting/qdrawhelper_neon.cpp b/src/gui/painting/qdrawhelper_neon.cpp
index 17ec693649..1fceb83710 100644
--- a/src/gui/painting/qdrawhelper_neon.cpp
+++ b/src/gui/painting/qdrawhelper_neon.cpp
@@ -185,7 +185,7 @@ void qt_blend_rgb16_on_argb32_neon(uchar *destPixels, int dbpl,
quint8 a = (255 * const_alpha) >> 8;
quint8 ia = 255 - a;
- while (h--) {
+ while (--h >= 0) {
for (int x=0; x<w; ++x)
dst[x] = INTERPOLATE_PIXEL_255(qConvertRgb16To32(src[x]), a, dst[x], ia);
dst += dbpl;
@@ -228,7 +228,7 @@ static inline void blockBlit16(quint16 *dst, quint16 *src, int dstride, int sstr
u.pointer = dst;
if (u.address & 2) {
- while (h--) {
+ while (--h >= 0) {
// align dst
dst[0] = src[0];
if (Width > 1)
@@ -237,7 +237,7 @@ static inline void blockBlit16(quint16 *dst, quint16 *src, int dstride, int sstr
src += sstride;
}
} else {
- while (h--) {
+ while (--h >= 0) {
scanLineBlit16<Width>(dst, src, dstride);
dst += dstride;
diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h
index 5b6f5ce216..833ddd7b16 100644
--- a/src/gui/painting/qdrawhelper_p.h
+++ b/src/gui/painting/qdrawhelper_p.h
@@ -29,7 +29,7 @@
#include "private/qrasterdefs_p.h"
#include <private/qsimd_p.h>
-#include <QtCore/qsharedpointer.h>
+#include <memory>
QT_BEGIN_NAMESPACE
@@ -53,13 +53,6 @@ static const uint RMASK = 0x00ff0000;
static const uint GMASK = 0x0000ff00;
static const uint BMASK = 0x000000ff;
-/*******************************************************************************
- * QSpan
- *
- * duplicate definition of FT_Span
- */
-typedef QT_FT_Span QSpan;
-
struct QSolidData;
struct QTextureData;
struct QGradientData;
@@ -147,9 +140,9 @@ struct quint24 {
uchar data[3];
};
-void qBlendGradient(int count, const QSpan *spans, void *userData);
-void qBlendTexture(int count, const QSpan *spans, void *userData);
-#ifdef __SSE2__
+void qBlendGradient(int count, const QT_FT_Span *spans, void *userData);
+void qBlendTexture(int count, const QT_FT_Span *spans, void *userData);
+#ifdef Q_PROCESSOR_X86
extern void (*qt_memfill64)(quint64 *dest, quint64 value, qsizetype count);
extern void (*qt_memfill32)(quint32 *dest, quint32 value, qsizetype count);
#else
@@ -181,7 +174,6 @@ struct RadialGradientValues
qreal dr;
qreal sqrfr;
qreal a;
- qreal inv2a;
bool extended;
};
@@ -337,11 +329,7 @@ struct QSpanData
QGradientData gradient;
QTextureData texture;
};
- class Pinnable {
- protected:
- ~Pinnable() {}
- }; // QSharedPointer<const void> is not supported
- QSharedPointer<const Pinnable> cachedGradient;
+ std::shared_ptr<const void> cachedGradient;
void init(QRasterBuffer *rb, const QRasterPaintEngine *pe);
@@ -413,12 +401,12 @@ const BlendType * QT_FASTCALL qt_fetch_radial_gradient_template(BlendType *buffe
bool affine = !data->m13 && !data->m23;
BlendType *end = buffer + length;
+ qreal inv_a = 1 / qreal(2 * op->radial.a);
+
if (affine) {
rx -= data->gradient.radial.focal.x;
ry -= data->gradient.radial.focal.y;
- qreal inv_a = 1 / qreal(2 * op->radial.a);
-
const qreal delta_rx = data->m11;
const qreal delta_ry = data->m12;
@@ -463,8 +451,8 @@ const BlendType * QT_FASTCALL qt_fetch_radial_gradient_template(BlendType *buffe
if (det >= 0) {
qreal detSqrt = qSqrt(det);
- qreal s0 = (-b - detSqrt) * op->radial.inv2a;
- qreal s1 = (-b + detSqrt) * op->radial.inv2a;
+ qreal s0 = (-b - detSqrt) * inv_a;
+ qreal s1 = (-b + detSqrt) * inv_a;
qreal s = qMax(s0, s1);
diff --git a/src/gui/painting/qdrawhelper_sse2.cpp b/src/gui/painting/qdrawhelper_sse2.cpp
index f26ad1a943..79590534f3 100644
--- a/src/gui/painting/qdrawhelper_sse2.cpp
+++ b/src/gui/painting/qdrawhelper_sse2.cpp
@@ -197,7 +197,7 @@ void QT_FASTCALL comp_func_Source_sse2(uint *dst, const uint *src, int length, u
}
}
-#ifndef __AVX2__
+#ifndef __haswell__
static Q_NEVER_INLINE
void Q_DECL_VECTORCALL qt_memfillXX_aligned(void *dest, __m128i value128, quintptr bytecount)
{
@@ -281,7 +281,7 @@ void qt_memfill32_sse2(quint32 *dest, quint32 value, qsizetype count)
qt_memfillXX_aligned(dest, _mm_set1_epi32(value), count * sizeof(quint32));
}
-#endif // !__AVX2__
+#endif // !__haswell__
void QT_FASTCALL comp_func_solid_Source_sse2(uint *destPixels, int length, uint color, uint const_alpha)
{
@@ -361,7 +361,7 @@ void qt_bitmapblit32_sse2_base(QRasterBuffer *rasterBuffer, int x, int y,
0x04040404, 0x08080808);
const __m128i maskadd2 = _mm_set_epi32(0x7f7f7f7f, 0x7e7e7e7e,
0x7c7c7c7c, 0x78787878);
- while (height--) {
+ while (--height >= 0) {
for (int x = 0; x < width; x += 8) {
const quint8 s = src[x >> 3];
if (!s)
@@ -380,7 +380,7 @@ void qt_bitmapblit32_sse2_base(QRasterBuffer *rasterBuffer, int x, int y,
src += stride;
}
} else {
- while (height--) {
+ while (--height >= 0) {
const quint8 s = *src;
if (s) {
__m128i mask1 = _mm_set1_epi8(s);
@@ -423,7 +423,7 @@ QT_WARNING_DISABLE_MSVC(4309) // truncation of constant value
const __m128i maskadd = _mm_set_epi16(0x7f7f, 0x7e7e, 0x7c7c, 0x7878,
0x7070, 0x6060, 0x4040, 0x0000);
- while (height--) {
+ while (--height >= 0) {
for (int x = 0; x < width; x += 8) {
const quint8 s = src[x >> 3];
if (!s)
@@ -558,7 +558,7 @@ void qt_scale_image_argb32_on_argb32_sse2(uchar *destPixels, int dbpl,
if (xend < 0 || xend >= (int)(sbpl/sizeof(quint32)))
--w;
- while (h--) {
+ while (--h >= 0) {
const uint *src = (const quint32 *) (srcPixels + (srcy >> 16) * sbpl);
int srcx = basex;
int x = 0;
@@ -571,13 +571,14 @@ void qt_scale_image_argb32_on_argb32_sse2(uchar *destPixels, int dbpl,
__m128i srcxVector = _mm_set_epi32(srcx, srcx + ix, srcx + ix + ix, srcx + ix + ix + ix);
- for (; x<w - 3; x += 4) {
- union Vect_buffer { __m128i vect; quint32 i[4]; };
- Vect_buffer addr;
- addr.vect = _mm_srli_epi32(srcxVector, 16);
+ for (; x < (w - 3); x += 4) {
+ const int idx0 = _mm_extract_epi16(srcxVector, 1);
+ const int idx1 = _mm_extract_epi16(srcxVector, 3);
+ const int idx2 = _mm_extract_epi16(srcxVector, 5);
+ const int idx3 = _mm_extract_epi16(srcxVector, 7);
srcxVector = _mm_add_epi32(srcxVector, ixVector);
- const __m128i srcVector = _mm_set_epi32(src[addr.i[0]], src[addr.i[1]], src[addr.i[2]], src[addr.i[3]]);
+ const __m128i srcVector = _mm_set_epi32(src[idx0], src[idx1], src[idx2], src[idx3]);
BLEND_SOURCE_OVER_ARGB32_SSE2_helper(dst, srcVector, nullVector, half, one, colorMask, alphaMask);
}
diff --git a/src/gui/painting/qdrawhelper_sse4.cpp b/src/gui/painting/qdrawhelper_sse4.cpp
index 7b9481eb3a..a7b4e6ba76 100644
--- a/src/gui/painting/qdrawhelper_sse4.cpp
+++ b/src/gui/painting/qdrawhelper_sse4.cpp
@@ -10,7 +10,7 @@
QT_BEGIN_NAMESPACE
-#ifndef __AVX2__
+#ifndef __haswell__
template<bool RGBA>
static void convertARGBToARGB32PM_sse4(uint *buffer, const uint *src, int count)
{
@@ -106,7 +106,7 @@ static void convertARGBToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int cou
buffer[i] = QRgba64::fromArgb32(s).premultiplied();
}
}
-#endif // __AVX2__
+#endif // __haswell__
static inline __m128 Q_DECL_VECTORCALL reciprocal_mul_ps(__m128 a, float mul)
{
@@ -375,7 +375,7 @@ static inline void convertRGBA64FromRGBA64PM_sse4(QRgba64 *buffer, const QRgba64
}
}
-#ifndef __AVX2__
+#ifndef __haswell__
void QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, int count, const QList<QRgb> *)
{
convertARGBToARGB32PM_sse4<false>(buffer, buffer, count);
@@ -427,7 +427,7 @@ const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const u
convertARGBToRGBA64PM_sse4<true>(buffer, reinterpret_cast<const uint *>(src) + index, count);
return buffer;
}
-#endif // __AVX2__
+#endif // __haswell__
void QT_FASTCALL storeRGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
const QList<QRgb> *, QDitherInfo *)
diff --git a/src/gui/painting/qfixed_p.h b/src/gui/painting/qfixed_p.h
index da69f901cd..c0a13d057f 100644
--- a/src/gui/painting/qfixed_p.h
+++ b/src/gui/painting/qfixed_p.h
@@ -18,6 +18,7 @@
#include <QtGui/private/qtguiglobal_p.h>
#include "QtCore/qdebug.h"
#include "QtCore/qpoint.h"
+#include "QtCore/qnumeric.h"
#include "QtCore/qsize.h"
QT_BEGIN_NAMESPACE
@@ -29,8 +30,7 @@ public:
constexpr QFixed() : val(0) {}
constexpr QFixed(int i) : val(i * 64) {}
constexpr QFixed(long i) : val(i * 64) {}
- QFixed &operator=(int i) { val = i * 64; return *this; }
- QFixed &operator=(long i) { val = i * 64; return *this; }
+ constexpr QFixed(long long i) : val(i * 64) {}
constexpr static QFixed fromReal(qreal r) { return fromFixed((int)(r*qreal(64))); }
constexpr static QFixed fromFixed(int fixed) { return QFixed(fixed,0); } // uses private ctor
@@ -48,28 +48,33 @@ public:
constexpr inline QFixed operator+(int i) const { return fromFixed(val + i * 64); }
constexpr inline QFixed operator+(uint i) const { return fromFixed((val + (i<<6))); }
- constexpr inline QFixed operator+(const QFixed &other) const { return fromFixed((val + other.val)); }
+ constexpr inline QFixed operator+(QFixed other) const { return fromFixed((val + other.val)); }
inline QFixed &operator+=(int i) { val += i * 64; return *this; }
inline QFixed &operator+=(uint i) { val += (i<<6); return *this; }
- inline QFixed &operator+=(const QFixed &other) { val += other.val; return *this; }
+ inline QFixed &operator+=(QFixed other) { val += other.val; return *this; }
constexpr inline QFixed operator-(int i) const { return fromFixed(val - i * 64); }
constexpr inline QFixed operator-(uint i) const { return fromFixed((val - (i<<6))); }
- constexpr inline QFixed operator-(const QFixed &other) const { return fromFixed((val - other.val)); }
+ constexpr inline QFixed operator-(QFixed other) const { return fromFixed((val - other.val)); }
inline QFixed &operator-=(int i) { val -= i * 64; return *this; }
inline QFixed &operator-=(uint i) { val -= (i<<6); return *this; }
- inline QFixed &operator-=(const QFixed &other) { val -= other.val; return *this; }
+ inline QFixed &operator-=(QFixed other) { val -= other.val; return *this; }
constexpr inline QFixed operator-() const { return fromFixed(-val); }
- constexpr inline bool operator==(const QFixed &other) const { return val == other.val; }
- constexpr inline bool operator!=(const QFixed &other) const { return val != other.val; }
- constexpr inline bool operator<(const QFixed &other) const { return val < other.val; }
- constexpr inline bool operator>(const QFixed &other) const { return val > other.val; }
- constexpr inline bool operator<=(const QFixed &other) const { return val <= other.val; }
- constexpr inline bool operator>=(const QFixed &other) const { return val >= other.val; }
+#define REL_OP(op) \
+ friend constexpr bool operator op(QFixed lhs, QFixed rhs) noexcept \
+ { return lhs.val op rhs.val; }
+ REL_OP(==)
+ REL_OP(!=)
+ REL_OP(< )
+ REL_OP(> )
+ REL_OP(<=)
+ REL_OP(>=)
+#undef REL_OP
+
constexpr inline bool operator!() const { return !val; }
inline QFixed &operator/=(int x) { val /= x; return *this; }
- inline QFixed &operator/=(const QFixed &o) {
+ inline QFixed &operator/=(QFixed o) {
if (o.val == 0) {
val = 0x7FFFFFFFL;
} else {
@@ -90,7 +95,7 @@ public:
inline QFixed operator>>(int d) const { QFixed f = *this; f.val >>= d; return f; }
inline QFixed &operator*=(int i) { val *= i; return *this; }
inline QFixed &operator*=(uint i) { val *= i; return *this; }
- inline QFixed &operator*=(const QFixed &o) {
+ inline QFixed &operator*=(QFixed o) {
bool neg = false;
qint64 a = val;
qint64 b = o.val;
@@ -103,11 +108,10 @@ public:
}
constexpr inline QFixed operator*(int i) const { return fromFixed(val * i); }
constexpr inline QFixed operator*(uint i) const { return fromFixed(val * i); }
- inline QFixed operator*(const QFixed &o) const { QFixed f = *this; return (f *= o); }
+ inline QFixed operator*(QFixed o) const { QFixed f = *this; return (f *= o); }
private:
constexpr QFixed(qreal i) : val((int)(i*qreal(64))) {}
- QFixed &operator=(qreal i) { val = (int)(i*qreal(64)); return *this; }
constexpr inline QFixed operator+(qreal i) const { return fromFixed((val + (int)(i*qreal(64)))); }
inline QFixed &operator+=(qreal i) { val += (int)(i*64); return *this; }
constexpr inline QFixed operator-(qreal i) const { return fromFixed((val - (int)(i*qreal(64)))); }
@@ -122,32 +126,35 @@ Q_DECLARE_TYPEINFO(QFixed, Q_PRIMITIVE_TYPE);
#define QFIXED_MAX (INT_MAX/256)
-constexpr inline int qRound(const QFixed &f) { return f.toInt(); }
-constexpr inline int qFloor(const QFixed &f) { return f.floor().truncate(); }
-
-constexpr inline QFixed operator*(int i, const QFixed &d) { return d*i; }
-constexpr inline QFixed operator+(int i, const QFixed &d) { return d+i; }
-constexpr inline QFixed operator-(int i, const QFixed &d) { return -(d-i); }
-constexpr inline QFixed operator*(uint i, const QFixed &d) { return d*i; }
-constexpr inline QFixed operator+(uint i, const QFixed &d) { return d+i; }
-constexpr inline QFixed operator-(uint i, const QFixed &d) { return -(d-i); }
-// constexpr inline QFixed operator*(qreal d, const QFixed &d2) { return d2*d; }
-
-constexpr inline bool operator==(const QFixed &f, int i) { return f.value() == i * 64; }
-constexpr inline bool operator==(int i, const QFixed &f) { return f.value() == i * 64; }
-constexpr inline bool operator!=(const QFixed &f, int i) { return f.value() != i * 64; }
-constexpr inline bool operator!=(int i, const QFixed &f) { return f.value() != i * 64; }
-constexpr inline bool operator<=(const QFixed &f, int i) { return f.value() <= i * 64; }
-constexpr inline bool operator<=(int i, const QFixed &f) { return i * 64 <= f.value(); }
-constexpr inline bool operator>=(const QFixed &f, int i) { return f.value() >= i * 64; }
-constexpr inline bool operator>=(int i, const QFixed &f) { return i * 64 >= f.value(); }
-constexpr inline bool operator<(const QFixed &f, int i) { return f.value() < i * 64; }
-constexpr inline bool operator<(int i, const QFixed &f) { return i * 64 < f.value(); }
-constexpr inline bool operator>(const QFixed &f, int i) { return f.value() > i * 64; }
-constexpr inline bool operator>(int i, const QFixed &f) { return i * 64 > f.value(); }
+constexpr inline int qRound(QFixed f) { return f.toInt(); }
+constexpr inline int qFloor(QFixed f) { return f.floor().truncate(); }
+
+constexpr inline QFixed operator*(int i, QFixed d) { return d*i; }
+constexpr inline QFixed operator+(int i, QFixed d) { return d+i; }
+constexpr inline QFixed operator-(int i, QFixed d) { return -(d-i); }
+constexpr inline QFixed operator*(uint i, QFixed d) { return d*i; }
+constexpr inline QFixed operator+(uint i, QFixed d) { return d+i; }
+constexpr inline QFixed operator-(uint i, QFixed d) { return -(d-i); }
+// constexpr inline QFixed operator*(qreal d, QFixed d2) { return d2*d; }
+
+inline bool qAddOverflow(QFixed v1, QFixed v2, QFixed *r)
+{
+ int val;
+ bool result = qAddOverflow(v1.value(), v2.value(), &val);
+ r->setValue(val);
+ return result;
+}
+
+inline bool qMulOverflow(QFixed v1, QFixed v2, QFixed *r)
+{
+ int val;
+ bool result = qMulOverflow(v1.value(), v2.value(), &val);
+ r->setValue(val);
+ return result;
+}
#ifndef QT_NO_DEBUG_STREAM
-inline QDebug &operator<<(QDebug &dbg, const QFixed &f)
+inline QDebug &operator<<(QDebug &dbg, QFixed f)
{ return dbg << f.toReal(); }
#endif
@@ -155,7 +162,7 @@ struct QFixedPoint {
QFixed x;
QFixed y;
constexpr inline QFixedPoint() {}
- constexpr inline QFixedPoint(const QFixed &_x, const QFixed &_y) : x(_x), y(_y) {}
+ constexpr inline QFixedPoint(QFixed _x, QFixed _y) : x(_x), y(_y) {}
constexpr QPointF toPointF() const { return QPointF(x.toReal(), y.toReal()); }
constexpr static QFixedPoint fromPointF(const QPointF &p) {
return QFixedPoint(QFixed::fromReal(p.x()), QFixed::fromReal(p.y()));
diff --git a/src/gui/painting/qicc.cpp b/src/gui/painting/qicc.cpp
index 9d95c80bf8..c01fa433ea 100644
--- a/src/gui/painting/qicc.cpp
+++ b/src/gui/painting/qicc.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qicc_p.h"
@@ -12,6 +12,8 @@
#include <qloggingcategory.h>
#include <qstring.h>
+#include "qcolorclut_p.h"
+#include "qcolormatrix_p.h"
#include "qcolorspace_p.h"
#include "qcolortrc_p.h"
@@ -20,6 +22,8 @@
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcIcc, "qt.gui.icc", QtWarningMsg)
+namespace QIcc {
+
struct ICCProfileHeader
{
quint32_be profileSize;
@@ -58,18 +62,23 @@ constexpr quint32 IccTag(uchar a, uchar b, uchar c, uchar d)
enum class ColorSpaceType : quint32 {
Rgb = IccTag('R', 'G', 'B', ' '),
Gray = IccTag('G', 'R', 'A', 'Y'),
+ Cmyk = IccTag('C', 'M', 'Y', 'K'),
};
enum class ProfileClass : quint32 {
- Input = IccTag('s', 'c', 'r', 'n'),
+ Input = IccTag('s', 'c', 'n', 'r'),
Display = IccTag('m', 'n', 't', 'r'),
- // Not supported:
Output = IccTag('p', 'r', 't', 'r'),
ColorSpace = IccTag('s', 'p', 'a', 'c'),
+ // Not supported:
+ DeviceLink = IccTag('l', 'i', 'n', 'k'),
+ Abstract = IccTag('a', 'b', 's', 't'),
+ NamedColor = IccTag('n', 'm', 'c', 'l'),
};
enum class Tag : quint32 {
acsp = IccTag('a', 'c', 's', 'p'),
+ Lab_ = IccTag('L', 'a', 'b', ' '),
RGB_ = IccTag('R', 'G', 'B', ' '),
XYZ_ = IccTag('X', 'Y', 'Z', ' '),
rXYZ = IccTag('r', 'X', 'Y', 'Z'),
@@ -81,8 +90,18 @@ enum class Tag : quint32 {
kTRC = IccTag('k', 'T', 'R', 'C'),
A2B0 = IccTag('A', '2', 'B', '0'),
A2B1 = IccTag('A', '2', 'B', '1'),
+ A2B2 = IccTag('A', '2', 'B', '2'),
B2A0 = IccTag('B', '2', 'A', '0'),
B2A1 = IccTag('B', '2', 'A', '1'),
+ B2A2 = IccTag('B', '2', 'A', '2'),
+ B2D0 = IccTag('B', '2', 'D', '0'),
+ B2D1 = IccTag('B', '2', 'D', '1'),
+ B2D2 = IccTag('B', '2', 'D', '2'),
+ B2D3 = IccTag('B', '2', 'D', '3'),
+ D2B0 = IccTag('D', '2', 'B', '0'),
+ D2B1 = IccTag('D', '2', 'B', '1'),
+ D2B2 = IccTag('D', '2', 'B', '2'),
+ D2B3 = IccTag('D', '2', 'B', '3'),
desc = IccTag('d', 'e', 's', 'c'),
text = IccTag('t', 'e', 'x', 't'),
cprt = IccTag('c', 'p', 'r', 't'),
@@ -93,9 +112,11 @@ enum class Tag : quint32 {
mft1 = IccTag('m', 'f', 't', '1'),
mft2 = IccTag('m', 'f', 't', '2'),
mluc = IccTag('m', 'l', 'u', 'c'),
+ mpet = IccTag('m', 'p', 'e', 't'),
mAB_ = IccTag('m', 'A', 'B', ' '),
mBA_ = IccTag('m', 'B', 'A', ' '),
chad = IccTag('c', 'h', 'a', 'd'),
+ gamt = IccTag('g', 'a', 'm', 't'),
sf32 = IccTag('s', 'f', '3', '2'),
// Apple extensions for ICCv2:
@@ -104,7 +125,9 @@ enum class Tag : quint32 {
aabg = IccTag('a', 'a', 'b', 'g'),
};
-inline size_t qHash(const Tag &key, size_t seed = 0)
+} // namespace QIcc
+
+inline size_t qHash(const QIcc::Tag &key, size_t seed = 0)
{
return qHash(quint32(key), seed);
}
@@ -159,6 +182,46 @@ struct MlucTagData : GenericTagData {
MlucTagRecord records[1];
};
+struct Lut8TagData : GenericTagData {
+ quint8 inputChannels;
+ quint8 outputChannels;
+ quint8 clutGridPoints;
+ quint8 padding;
+ qint32_be e1;
+ qint32_be e2;
+ qint32_be e3;
+ qint32_be e4;
+ qint32_be e5;
+ qint32_be e6;
+ qint32_be e7;
+ qint32_be e8;
+ qint32_be e9;
+ // followed by parameter values: quint8[inputChannels * 256];
+ // followed by parameter values: quint8[outputChannels * clutGridPoints^inputChannels];
+ // followed by parameter values: quint8[outputChannels * 256];
+};
+
+struct Lut16TagData : GenericTagData {
+ quint8 inputChannels;
+ quint8 outputChannels;
+ quint8 clutGridPoints;
+ quint8 padding;
+ qint32_be e1;
+ qint32_be e2;
+ qint32_be e3;
+ qint32_be e4;
+ qint32_be e5;
+ qint32_be e6;
+ qint32_be e7;
+ qint32_be e8;
+ qint32_be e9;
+ quint16_be inputTableEntries;
+ quint16_be outputTableEntries;
+ // followed by parameter values: quint16_be[inputChannels * inputTableEntries];
+ // followed by parameter values: quint16_be[outputChannels * clutGridPoints^inputChannels];
+ // followed by parameter values: quint16_be[outputChannels * outputTableEntries];
+};
+
// For both mAB and mBA
struct mABTagData : GenericTagData {
quint8 inputChannels;
@@ -169,10 +232,34 @@ struct mABTagData : GenericTagData {
quint32_be mCurvesOffset;
quint32_be clutOffset;
quint32_be aCurvesOffset;
+ // followed by embedded data for the offsets above
+};
+
+struct mpetTagData : GenericTagData {
+ quint16_be inputChannels;
+ quint16_be outputChannels;
+ quint32_be processingElements;
+ // element offset table
+ // element data
};
struct Sf32TagData : GenericTagData {
- quint32_be value[1];
+ quint32_be value[9];
+};
+
+struct MatrixElement {
+ qint32_be e0;
+ qint32_be e1;
+ qint32_be e2;
+ qint32_be e3;
+ qint32_be e4;
+ qint32_be e5;
+ qint32_be e6;
+ qint32_be e7;
+ qint32_be e8;
+ qint32_be e9;
+ qint32_be e10;
+ qint32_be e11;
};
static int toFixedS1516(float x)
@@ -204,19 +291,19 @@ static bool isValidIccProfile(const ICCProfileHeader &header)
if (header.profileClass != uint(ProfileClass::Input)
&& header.profileClass != uint(ProfileClass::Display)
- && (header.profileClass != uint(ProfileClass::Output)
- || header.inputColorSpace != uint(ColorSpaceType::Gray))) {
+ && header.profileClass != uint(ProfileClass::Output)
+ && header.profileClass != uint(ProfileClass::ColorSpace)) {
qCInfo(lcIcc, "Unsupported ICC profile class 0x%x", quint32(header.profileClass));
return false;
}
if (header.inputColorSpace != uint(ColorSpaceType::Rgb)
- && header.inputColorSpace != uint(ColorSpaceType::Gray)) {
+ && header.inputColorSpace != uint(ColorSpaceType::Gray)
+ && header.inputColorSpace != uint(ColorSpaceType::Cmyk)) {
qCInfo(lcIcc, "Unsupported ICC input color space 0x%x", quint32(header.inputColorSpace));
return false;
}
- if (header.pcs != 0x58595a20 /* 'XYZ '*/) {
- // ### support PCSLAB
- qCInfo(lcIcc, "Unsupported ICC profile connection space 0x%x", quint32(header.pcs));
+ if (header.pcs != uint(Tag::XYZ_) && header.pcs != uint(Tag::Lab_)) {
+ qCInfo(lcIcc, "Invalid ICC profile connection space 0x%x", quint32(header.pcs));
return false;
}
@@ -234,7 +321,7 @@ static bool isValidIccProfile(const ICCProfileHeader &header)
static int writeColorTrc(QDataStream &stream, const QColorTrc &trc)
{
- if (trc.isLinear()) {
+ if (trc.isIdentity()) {
stream << uint(Tag::curv) << uint(0);
stream << uint(0);
return 12;
@@ -274,6 +361,10 @@ static int writeColorTrc(QDataStream &stream, const QColorTrc &trc)
stream << ushort(trc.m_table.m_table8[i] * 257U);
}
}
+ if (trc.m_table.m_tableSize & 1) {
+ stream << ushort(0);
+ return 12 + 2 * trc.m_table.m_tableSize + 2;
+ }
return 12 + 2 * trc.m_table.m_tableSize;
}
@@ -283,10 +374,21 @@ QByteArray toIccProfile(const QColorSpace &space)
return QByteArray();
const QColorSpacePrivate *spaceDPtr = QColorSpacePrivate::get(space);
+ // This should catch anything not three component matrix based as we can only get that from parsed ICC
+ if (!spaceDPtr->iccProfile.isEmpty())
+ return spaceDPtr->iccProfile;
+ Q_ASSERT(spaceDPtr->isThreeComponentMatrix());
+
+ int fixedLengthTagCount = 5;
+ bool writeChad = false;
+ if (!spaceDPtr->whitePoint.isNull() && spaceDPtr->whitePoint != QColorVector::D50()) {
+ writeChad = true;
+ fixedLengthTagCount++;
+ }
- constexpr int tagCount = 9;
- constexpr uint profileDataOffset = 128 + 4 + 12 * tagCount;
- constexpr uint variableTagTableOffsets = 128 + 4 + 12 * 5;
+ const int tagCount = fixedLengthTagCount + 4;
+ const uint profileDataOffset = 128 + 4 + 12 * tagCount;
+ const uint variableTagTableOffsets = 128 + 4 + 12 * fixedLengthTagCount;
uint currentOffset = 0;
uint rTrcOffset, gTrcOffset, bTrcOffset;
uint rTrcSize, gTrcSize, bTrcSize;
@@ -299,10 +401,10 @@ QByteArray toIccProfile(const QColorSpace &space)
// Profile header:
stream << uint(0); // Size, we will update this later
stream << uint(0);
- stream << uint(0x02400000); // Version 2.4 (note we use 'para' from version 4)
+ stream << uint(0x04400000); // Version 4.4
stream << uint(ProfileClass::Display);
stream << uint(Tag::RGB_);
- stream << uint(Tag::XYZ_);
+ stream << (spaceDPtr->isPcsLab ? uint(Tag::Lab_) : uint(Tag::XYZ_));
stream << uint(0) << uint(0) << uint(0);
stream << uint(Tag::acsp);
stream << uint(0) << uint(0) << uint(0);
@@ -316,19 +418,23 @@ QByteArray toIccProfile(const QColorSpace &space)
stream << uint(0) << uint(0) << uint(0) << uint(0) << uint(0) << uint(0) << uint(0);
// Tag table:
+ currentOffset = profileDataOffset;
stream << uint(tagCount);
stream << uint(Tag::rXYZ) << uint(profileDataOffset + 00) << uint(20);
stream << uint(Tag::gXYZ) << uint(profileDataOffset + 20) << uint(20);
stream << uint(Tag::bXYZ) << uint(profileDataOffset + 40) << uint(20);
stream << uint(Tag::wtpt) << uint(profileDataOffset + 60) << uint(20);
- stream << uint(Tag::cprt) << uint(profileDataOffset + 80) << uint(12);
+ stream << uint(Tag::cprt) << uint(profileDataOffset + 80) << uint(34);
+ currentOffset += 20 + 20 + 20 + 20 + 34 + 2;
+ if (writeChad) {
+ stream << uint(Tag::chad) << uint(currentOffset) << uint(44);
+ currentOffset += 44;
+ }
// From here the offset and size will be updated later:
stream << uint(Tag::rTRC) << uint(0) << uint(0);
stream << uint(Tag::gTRC) << uint(0) << uint(0);
stream << uint(Tag::bTRC) << uint(0) << uint(0);
stream << uint(Tag::desc) << uint(0) << uint(0);
- // TODO: consider adding 'chad' tag (required in ICC >=4 when we have non-D50 whitepoint)
- currentOffset = profileDataOffset;
// Tag data:
stream << uint(Tag::XYZ_) << uint(0);
@@ -347,9 +453,25 @@ QByteArray toIccProfile(const QColorSpace &space)
stream << toFixedS1516(spaceDPtr->whitePoint.x);
stream << toFixedS1516(spaceDPtr->whitePoint.y);
stream << toFixedS1516(spaceDPtr->whitePoint.z);
- stream << uint(Tag::text) << uint(0);
- stream << uint(IccTag('N', '/', 'A', '\0'));
- currentOffset += 92;
+ stream << uint(Tag::mluc) << uint(0);
+ stream << uint(1) << uint(12);
+ stream << uchar('e') << uchar('n') << uchar('U') << uchar('S');
+ stream << uint(6) << uint(28);
+ stream << ushort('N') << ushort('/') << ushort('A');
+ stream << ushort(0); // 4-byte alignment
+ if (writeChad) {
+ QColorMatrix chad = QColorMatrix::chromaticAdaptation(spaceDPtr->whitePoint);
+ stream << uint(Tag::sf32) << uint(0);
+ stream << toFixedS1516(chad.r.x);
+ stream << toFixedS1516(chad.g.x);
+ stream << toFixedS1516(chad.b.x);
+ stream << toFixedS1516(chad.r.y);
+ stream << toFixedS1516(chad.g.y);
+ stream << toFixedS1516(chad.b.y);
+ stream << toFixedS1516(chad.r.z);
+ stream << toFixedS1516(chad.g.z);
+ stream << toFixedS1516(chad.b.z);
+ }
// From now on the data is variable sized:
rTrcOffset = currentOffset;
@@ -372,16 +494,20 @@ QByteArray toIccProfile(const QColorSpace &space)
currentOffset += bTrcSize;
}
+ // Writing description
descOffset = currentOffset;
- QByteArray description = space.description().toUtf8();
- stream << uint(Tag::desc) << uint(0);
- stream << uint(description.size() + 1);
- stream.writeRawData(description.constData(), description.size() + 1);
- stream << uint(0) << uint(0);
- stream << ushort(0) << uchar(0);
- QByteArray macdesc(67, '\0');
- stream.writeRawData(macdesc.constData(), 67);
- descSize = 90 + description.size() + 1;
+ const QString description = space.description();
+ stream << uint(Tag::mluc) << uint(0);
+ stream << uint(1) << uint(12);
+ stream << uchar('e') << uchar('n') << uchar('U') << uchar('S');
+ stream << uint(description.size() * 2) << uint(28);
+ for (QChar ch : description)
+ stream << ushort(ch.unicode());
+ descSize = 28 + description.size() * 2;
+ if (description.size() & 1) {
+ stream << ushort(0);
+ currentOffset += 2;
+ }
currentOffset += descSize;
buffer.close();
@@ -412,7 +538,7 @@ struct TagEntry {
quint32 size;
};
-bool parseXyzData(const QByteArray &data, const TagEntry &tagEntry, QColorVector &colorVector)
+static bool parseXyzData(const QByteArray &data, const TagEntry &tagEntry, QColorVector &colorVector)
{
if (tagEntry.size < sizeof(XYZTagData)) {
qCWarning(lcIcc) << "Undersized XYZ tag";
@@ -431,23 +557,22 @@ bool parseXyzData(const QByteArray &data, const TagEntry &tagEntry, QColorVector
return true;
}
-bool parseTRC(const QByteArray &data, const TagEntry &tagEntry, QColorTrc &gamma)
+static quint32 parseTRC(const QByteArrayView &tagData, QColorTrc &gamma, QColorTransferTable::Type type = QColorTransferTable::TwoWay)
{
- const GenericTagData trcData = qFromUnaligned<GenericTagData>(data.constData()
- + tagEntry.offset);
+ const GenericTagData trcData = qFromUnaligned<GenericTagData>(tagData.constData());
if (trcData.type == quint32(Tag::curv)) {
Q_STATIC_ASSERT(sizeof(CurvTagData) == 12);
- const CurvTagData curv = qFromUnaligned<CurvTagData>(data.constData() + tagEntry.offset);
+ const CurvTagData curv = qFromUnaligned<CurvTagData>(tagData.constData());
if (curv.valueCount > (1 << 16))
- return false;
- if (tagEntry.size - 12 < 2 * curv.valueCount)
- return false;
- const auto valueOffset = tagEntry.offset + sizeof(CurvTagData);
+ return 0;
+ if (tagData.size() < qsizetype(12 + 2 * curv.valueCount))
+ return 0;
+ const auto valueOffset = sizeof(CurvTagData);
if (curv.valueCount == 0) {
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(); // Linear
} else if (curv.valueCount == 1) {
- const quint16 v = qFromBigEndian<quint16>(data.constData() + valueOffset);
+ const quint16 v = qFromBigEndian<quint16>(tagData.constData() + valueOffset);
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction::fromGamma(v * (1.0f / 256.0f));
} else {
@@ -455,12 +580,12 @@ bool parseTRC(const QByteArray &data, const TagEntry &tagEntry, QColorTrc &gamma
tabl.resize(curv.valueCount);
static_assert(sizeof(GenericTagData) == 2 * sizeof(quint32_be),
"GenericTagData has padding. The following code is a subject to UB.");
- qFromBigEndian<quint16>(data.constData() + valueOffset, curv.valueCount, tabl.data());
- QColorTransferTable table = QColorTransferTable(curv.valueCount, std::move(tabl));
+ qFromBigEndian<quint16>(tagData.constData() + valueOffset, curv.valueCount, tabl.data());
+ QColorTransferTable table(curv.valueCount, tabl, type);
QColorTransferFunction curve;
if (!table.checkValidity()) {
qCWarning(lcIcc) << "Invalid curv table";
- return false;
+ return 0;
} else if (!table.asColorTransferFunction(&curve)) {
gamma.m_type = QColorTrc::Type::Table;
gamma.m_table = table;
@@ -470,43 +595,43 @@ bool parseTRC(const QByteArray &data, const TagEntry &tagEntry, QColorTrc &gamma
gamma.m_fun = curve;
}
}
- return true;
+ return 12 + 2 * curv.valueCount;
}
if (trcData.type == quint32(Tag::para)) {
Q_STATIC_ASSERT(sizeof(ParaTagData) == 12);
- const ParaTagData para = qFromUnaligned<ParaTagData>(data.constData() + tagEntry.offset);
- const auto parametersOffset = tagEntry.offset + sizeof(ParaTagData);
+ const ParaTagData para = qFromUnaligned<ParaTagData>(tagData.constData());
+ const auto parametersOffset = sizeof(ParaTagData);
quint32 parameters[7];
switch (para.curveType) {
case 0: {
- if (tagEntry.size < sizeof(ParaTagData) + 1 * 4)
- return false;
- qFromBigEndian<quint32>(data.constData() + parametersOffset, 1, parameters);
+ if (tagData.size() < 12 + 1 * 4)
+ return 0;
+ qFromBigEndian<quint32>(tagData.constData() + parametersOffset, 1, parameters);
float g = fromFixedS1516(parameters[0]);
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction::fromGamma(g);
- break;
+ return 12 + 1 * 4;
}
case 1: {
- if (tagEntry.size < sizeof(ParaTagData) + 3 * 4)
- return false;
- qFromBigEndian<quint32>(data.constData() + parametersOffset, 3, parameters);
+ if (tagData.size() < 12 + 3 * 4)
+ return 0;
+ qFromBigEndian<quint32>(tagData.constData() + parametersOffset, 3, parameters);
if (parameters[1] == 0)
- return false;
+ return 0;
float g = fromFixedS1516(parameters[0]);
float a = fromFixedS1516(parameters[1]);
float b = fromFixedS1516(parameters[2]);
float d = -b / a;
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, 0.0f, 0.0f, g);
- break;
+ return 12 + 3 * 4;
}
case 2: {
- if (tagEntry.size < sizeof(ParaTagData) + 4 * 4)
- return false;
- qFromBigEndian<quint32>(data.constData() + parametersOffset, 4, parameters);
+ if (tagData.size() < 12 + 4 * 4)
+ return 0;
+ qFromBigEndian<quint32>(tagData.constData() + parametersOffset, 4, parameters);
if (parameters[1] == 0)
- return false;
+ return 0;
float g = fromFixedS1516(parameters[0]);
float a = fromFixedS1516(parameters[1]);
float b = fromFixedS1516(parameters[2]);
@@ -514,12 +639,12 @@ bool parseTRC(const QByteArray &data, const TagEntry &tagEntry, QColorTrc &gamma
float d = -b / a;
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, c, c, g);
- break;
+ return 12 + 4 * 4;
}
case 3: {
- if (tagEntry.size < sizeof(ParaTagData) + 5 * 4)
- return false;
- qFromBigEndian<quint32>(data.constData() + parametersOffset, 5, parameters);
+ if (tagData.size() < 12 + 5 * 4)
+ return 0;
+ qFromBigEndian<quint32>(tagData.constData() + parametersOffset, 5, parameters);
float g = fromFixedS1516(parameters[0]);
float a = fromFixedS1516(parameters[1]);
float b = fromFixedS1516(parameters[2]);
@@ -527,12 +652,12 @@ bool parseTRC(const QByteArray &data, const TagEntry &tagEntry, QColorTrc &gamma
float d = fromFixedS1516(parameters[4]);
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(a, b, c, d, 0.0f, 0.0f, g);
- break;
+ return 12 + 5 * 4;
}
case 4: {
- if (tagEntry.size < sizeof(ParaTagData) + 7 * 4)
- return false;
- qFromBigEndian<quint32>(data.constData() + parametersOffset, 7, parameters);
+ if (tagData.size() < 12 + 7 * 4)
+ return 0;
+ qFromBigEndian<quint32>(tagData.constData() + parametersOffset, 7, parameters);
float g = fromFixedS1516(parameters[0]);
float a = fromFixedS1516(parameters[1]);
float b = fromFixedS1516(parameters[2]);
@@ -542,19 +667,401 @@ bool parseTRC(const QByteArray &data, const TagEntry &tagEntry, QColorTrc &gamma
float f = fromFixedS1516(parameters[6]);
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(a, b, c, d, e, f, g);
- break;
+ return 12 + 7 * 4;
}
default:
qCWarning(lcIcc) << "Unknown para type" << uint(para.curveType);
+ return 0;
+ }
+ return true;
+ }
+ qCWarning(lcIcc) << "Invalid TRC data type" << Qt::hex << trcData.type;
+ return 0;
+}
+
+template<typename T>
+static void parseCLUT(const T *tableData, const float f, QColorCLUT *clut, uchar outputChannels)
+{
+ if (outputChannels == 4) {
+ for (qsizetype index = 0; index < clut->table.size(); ++index) {
+ QColorVector v(tableData[index * 4 + 0] * f,
+ tableData[index * 4 + 1] * f,
+ tableData[index * 4 + 2] * f,
+ tableData[index * 4 + 3] * f);
+ clut->table[index] = v;
+ };
+ } else {
+ for (qsizetype index = 0; index < clut->table.size(); ++index) {
+ QColorVector v(tableData[index * 3 + 0] * f,
+ tableData[index * 3 + 1] * f,
+ tableData[index * 3 + 2] * f);
+ clut->table[index] = v;
+ };
+ }
+}
+
+// very simple version for small values (<=4) of exp.
+static constexpr qsizetype intPow(qsizetype x, qsizetype exp)
+{
+ return (exp <= 1) ? x : x * intPow(x, exp - 1);
+}
+
+// Parses lut8 and lut16 type elements
+template<typename T>
+static bool parseLutData(const QByteArray &data, const TagEntry &tagEntry, QColorSpacePrivate *colorSpacePrivate, bool isAb)
+{
+ if (tagEntry.size < sizeof(T)) {
+ qCWarning(lcIcc) << "Undersized lut8/lut16 tag";
+ return false;
+ }
+ if (qsizetype(tagEntry.size) > data.size()) {
+ qCWarning(lcIcc) << "Truncated lut8/lut16 tag";
+ return false;
+ }
+ using S = std::conditional_t<std::is_same_v<T, Lut8TagData>, uint8_t, uint16_t>;
+ const T lut = qFromUnaligned<T>(data.constData() + tagEntry.offset);
+ int inputTableEntries, outputTableEntries, precision;
+ if constexpr (std::is_same_v<T, Lut8TagData>) {
+ Q_ASSERT(lut.type == quint32(Tag::mft1));
+ if (!colorSpacePrivate->isPcsLab && isAb) {
+ qCWarning(lcIcc) << "Lut8 can not output XYZ values";
+ return false;
+ }
+ inputTableEntries = 256;
+ outputTableEntries = 256;
+ precision = 1;
+ } else {
+ Q_ASSERT(lut.type == quint32(Tag::mft2));
+ inputTableEntries = lut.inputTableEntries;
+ outputTableEntries = lut.outputTableEntries;
+ if (inputTableEntries < 2 || inputTableEntries > 4096)
+ return false;
+ if (outputTableEntries < 2 || outputTableEntries > 4096)
+ return false;
+ precision = 2;
+ }
+
+ bool inTableIsLinear = true, outTableIsLinear = true;
+ QColorSpacePrivate::TransferElement inTableElement;
+ QColorSpacePrivate::TransferElement outTableElement;
+ QColorCLUT clutElement;
+ QColorMatrix matrixElement;
+
+ matrixElement.r.x = fromFixedS1516(lut.e1);
+ matrixElement.g.x = fromFixedS1516(lut.e2);
+ matrixElement.b.x = fromFixedS1516(lut.e3);
+ matrixElement.r.y = fromFixedS1516(lut.e4);
+ matrixElement.g.y = fromFixedS1516(lut.e5);
+ matrixElement.b.y = fromFixedS1516(lut.e6);
+ matrixElement.r.z = fromFixedS1516(lut.e7);
+ matrixElement.g.z = fromFixedS1516(lut.e8);
+ matrixElement.b.z = fromFixedS1516(lut.e9);
+ if (!colorSpacePrivate->isPcsLab && !isAb && !matrixElement.isValid()) {
+ qCWarning(lcIcc) << "Invalid matrix values in lut8/lut16";
+ return false;
+ }
+
+ if (lut.inputChannels != 3 && !(isAb && colorSpacePrivate->colorModel == QColorSpace::ColorModel::Cmyk && lut.inputChannels == 4)) {
+ qCWarning(lcIcc) << "Unsupported lut8/lut16 input channel count" << lut.inputChannels;
+ return false;
+ }
+
+ if (lut.outputChannels != 3 && !(!isAb && colorSpacePrivate->colorModel == QColorSpace::ColorModel::Cmyk && lut.outputChannels == 4)) {
+ qCWarning(lcIcc) << "Unsupported lut8/lut16 output channel count" << lut.outputChannels;
+ return false;
+ }
+
+ const qsizetype clutTableSize = intPow(lut.clutGridPoints, lut.inputChannels);
+ if (tagEntry.size < (sizeof(T) + precision * lut.inputChannels * inputTableEntries
+ + precision * lut.outputChannels * outputTableEntries
+ + precision * lut.outputChannels * clutTableSize)) {
+ qCWarning(lcIcc) << "Undersized lut8/lut16 tag, no room for tables";
+ return false;
+ }
+ if (colorSpacePrivate->colorModel == QColorSpace::ColorModel::Cmyk && clutTableSize == 0) {
+ qCWarning(lcIcc) << "Cmyk conversion must have a CLUT";
+ return false;
+ }
+
+ const uint8_t *tableData = reinterpret_cast<const uint8_t *>(data.constData() + tagEntry.offset + sizeof(T));
+
+ for (int j = 0; j < lut.inputChannels; ++j) {
+ QList<S> input(inputTableEntries);
+ qFromBigEndian<S>(tableData, inputTableEntries, input.data());
+ QColorTransferTable table(inputTableEntries, input, QColorTransferTable::OneWay);
+ if (!table.checkValidity()) {
+ qCWarning(lcIcc) << "Bad input table in lut8/lut16";
+ return false;
+ }
+ if (!table.isIdentity())
+ inTableIsLinear = false;
+ inTableElement.trc[j] = std::move(table);
+ tableData += inputTableEntries * precision;
+ }
+
+ clutElement.table.resize(clutTableSize);
+ clutElement.gridPointsX = clutElement.gridPointsY = clutElement.gridPointsZ = lut.clutGridPoints;
+ if (lut.inputChannels == 4)
+ clutElement.gridPointsW = lut.clutGridPoints;
+
+ if constexpr (std::is_same_v<T, Lut8TagData>) {
+ parseCLUT(tableData, 1.f / 255.f, &clutElement, lut.outputChannels);
+ } else {
+ float f = 1.0f / 65535.f;
+ if (colorSpacePrivate->isPcsLab && isAb) // Legacy lut16 conversion to Lab
+ f = 1.0f / 65280.f;
+ QList<S> clutTable(clutTableSize * lut.outputChannels);
+ qFromBigEndian<S>(tableData, clutTable.size(), clutTable.data());
+ parseCLUT(clutTable.constData(), f, &clutElement, lut.outputChannels);
+ }
+ tableData += clutTableSize * lut.outputChannels * precision;
+
+ for (int j = 0; j < lut.outputChannels; ++j) {
+ QList<S> output(outputTableEntries);
+ qFromBigEndian<S>(tableData, outputTableEntries, output.data());
+ QColorTransferTable table(outputTableEntries, output, QColorTransferTable::OneWay);
+ if (!table.checkValidity()) {
+ qCWarning(lcIcc) << "Bad output table in lut8/lut16";
return false;
}
+ if (!table.isIdentity())
+ outTableIsLinear = false;
+ outTableElement.trc[j] = std::move(table);
+ tableData += outputTableEntries * precision;
+ }
+
+ if (isAb) {
+ if (!inTableIsLinear)
+ colorSpacePrivate->mAB.append(inTableElement);
+ if (!clutElement.isEmpty())
+ colorSpacePrivate->mAB.append(clutElement);
+ if (!outTableIsLinear || colorSpacePrivate->mAB.isEmpty())
+ colorSpacePrivate->mAB.append(outTableElement);
+ } else {
+ // The matrix is only to be applied if the input color-space is XYZ
+ if (!colorSpacePrivate->isPcsLab && !matrixElement.isIdentity())
+ colorSpacePrivate->mBA.append(matrixElement);
+ if (!inTableIsLinear)
+ colorSpacePrivate->mBA.append(inTableElement);
+ if (!clutElement.isEmpty())
+ colorSpacePrivate->mBA.append(clutElement);
+ if (!outTableIsLinear || colorSpacePrivate->mBA.isEmpty())
+ colorSpacePrivate->mBA.append(outTableElement);
+ }
+ return true;
+}
+
+// Parses mAB and mBA type elements
+static bool parseMabData(const QByteArray &data, const TagEntry &tagEntry, QColorSpacePrivate *colorSpacePrivate, bool isAb)
+{
+ if (tagEntry.size < sizeof(mABTagData)) {
+ qCWarning(lcIcc) << "Undersized mAB/mBA tag";
+ return false;
+ }
+ if (qsizetype(tagEntry.size) > data.size()) {
+ qCWarning(lcIcc) << "Truncated mAB/mBA tag";
+ return false;
+ }
+ const mABTagData mab = qFromUnaligned<mABTagData>(data.constData() + tagEntry.offset);
+ if ((mab.type != quint32(Tag::mAB_) && isAb) || (mab.type != quint32(Tag::mBA_) && !isAb)){
+ qCWarning(lcIcc) << "Bad mAB/mBA content type";
+ return false;
+ }
+
+ if (mab.inputChannels != 3 && !(isAb && colorSpacePrivate->colorModel == QColorSpace::ColorModel::Cmyk && mab.inputChannels == 4)) {
+ qCWarning(lcIcc) << "Unsupported mAB/mBA input channel count" << mab.inputChannels;
+ return false;
+ }
+
+ if (mab.outputChannels != 3 && !(!isAb && colorSpacePrivate->colorModel == QColorSpace::ColorModel::Cmyk && mab.outputChannels == 4)) {
+ qCWarning(lcIcc) << "Unsupported mAB/mBA output channel count" << mab.outputChannels;
+ return false;
+ }
+
+ // These combinations are legal: B, M + Matrix + B, A + Clut + B, A + Clut + M + Matrix + B
+ if (!mab.bCurvesOffset) {
+ qCWarning(lcIcc) << "Illegal mAB/mBA without B table";
+ return false;
+ }
+ if (((bool)mab.matrixOffset != (bool)mab.mCurvesOffset) ||
+ ((bool)mab.aCurvesOffset != (bool)mab.clutOffset)) {
+ qCWarning(lcIcc) << "Illegal mAB/mBA element combination";
+ return false;
+ }
+
+ if (mab.aCurvesOffset > (tagEntry.size - 3 * sizeof(GenericTagData)) ||
+ mab.bCurvesOffset > (tagEntry.size - 3 * sizeof(GenericTagData)) ||
+ mab.mCurvesOffset > (tagEntry.size - 3 * sizeof(GenericTagData)) ||
+ mab.matrixOffset > (tagEntry.size - 4 * 12) ||
+ mab.clutOffset > (tagEntry.size - 20)) {
+ qCWarning(lcIcc) << "Illegal mAB/mBA element offset";
+ return false;
+ }
+
+ QColorSpacePrivate::TransferElement bTableElement;
+ QColorSpacePrivate::TransferElement aTableElement;
+ QColorCLUT clutElement;
+ QColorSpacePrivate::TransferElement mTableElement;
+ QColorMatrix matrixElement;
+ QColorVector offsetElement;
+
+ auto parseCurves = [&data, &tagEntry] (uint curvesOffset, QColorTrc *table, int channels) {
+ for (int i = 0; i < channels; ++i) {
+ if (qsizetype(tagEntry.offset + curvesOffset + 12) > data.size() || curvesOffset + 12 > tagEntry.size) {
+ qCWarning(lcIcc) << "Space missing for channel curves in mAB/mBA";
+ return false;
+ }
+ auto size = parseTRC(QByteArrayView(data).sliced(tagEntry.offset + curvesOffset, tagEntry.size - curvesOffset), table[i], QColorTransferTable::OneWay);
+ if (!size)
+ return false;
+ if (size & 2) size += 2; // possible padding
+ curvesOffset += size;
+ }
return true;
+ };
+
+ bool bCurvesAreLinear = true, aCurvesAreLinear = true, mCurvesAreLinear = true;
+
+ // B Curves
+ if (!parseCurves(mab.bCurvesOffset, bTableElement.trc, isAb ? mab.outputChannels : mab.inputChannels)) {
+ qCWarning(lcIcc) << "Invalid B curves";
+ return false;
+ } else {
+ bCurvesAreLinear = bTableElement.trc[0].isIdentity() && bTableElement.trc[1].isIdentity() && bTableElement.trc[2].isIdentity();
}
- qCWarning(lcIcc) << "Invalid TRC data type";
+
+ // A Curves
+ if (mab.aCurvesOffset) {
+ if (!parseCurves(mab.aCurvesOffset, aTableElement.trc, isAb ? mab.inputChannels : mab.outputChannels)) {
+ qCWarning(lcIcc) << "Invalid A curves";
+ return false;
+ } else {
+ aCurvesAreLinear = aTableElement.trc[0].isIdentity() && aTableElement.trc[1].isIdentity() && aTableElement.trc[2].isIdentity();
+ }
+ }
+
+ // M Curves
+ if (mab.mCurvesOffset) {
+ if (!parseCurves(mab.mCurvesOffset, mTableElement.trc, 3)) {
+ qCWarning(lcIcc) << "Invalid M curves";
+ return false;
+ } else {
+ mCurvesAreLinear = mTableElement.trc[0].isIdentity() && mTableElement.trc[1].isIdentity() && mTableElement.trc[2].isIdentity();
+ }
+ }
+
+ // Matrix
+ if (mab.matrixOffset) {
+ const MatrixElement matrix = qFromUnaligned<MatrixElement>(data.constData() + tagEntry.offset + mab.matrixOffset);
+ matrixElement.r.x = fromFixedS1516(matrix.e0);
+ matrixElement.g.x = fromFixedS1516(matrix.e1);
+ matrixElement.b.x = fromFixedS1516(matrix.e2);
+ matrixElement.r.y = fromFixedS1516(matrix.e3);
+ matrixElement.g.y = fromFixedS1516(matrix.e4);
+ matrixElement.b.y = fromFixedS1516(matrix.e5);
+ matrixElement.r.z = fromFixedS1516(matrix.e6);
+ matrixElement.g.z = fromFixedS1516(matrix.e7);
+ matrixElement.b.z = fromFixedS1516(matrix.e8);
+ offsetElement.x = fromFixedS1516(matrix.e9);
+ offsetElement.y = fromFixedS1516(matrix.e10);
+ offsetElement.z = fromFixedS1516(matrix.e11);
+ if (!matrixElement.isValid() || !offsetElement.isValid()) {
+ qCWarning(lcIcc) << "Invalid matrix values in mAB/mBA element";
+ return false;
+ }
+ }
+
+ // CLUT
+ if (mab.clutOffset) {
+ clutElement.gridPointsX = uint8_t(data[tagEntry.offset + mab.clutOffset]);
+ clutElement.gridPointsY = uint8_t(data[tagEntry.offset + mab.clutOffset + 1]);
+ clutElement.gridPointsZ = uint8_t(data[tagEntry.offset + mab.clutOffset + 2]);
+ clutElement.gridPointsW = std::max(uint8_t(data[tagEntry.offset + mab.clutOffset + 3]), uint8_t(1));
+ const uchar precision = data[tagEntry.offset + mab.clutOffset + 16];
+ if (precision > 2 || precision < 1) {
+ qCWarning(lcIcc) << "Invalid mAB/mBA element CLUT precision";
+ return false;
+ }
+ if (clutElement.gridPointsX < 2 || clutElement.gridPointsY < 2 || clutElement.gridPointsZ < 2) {
+ qCWarning(lcIcc) << "Empty CLUT";
+ return false;
+ }
+ const qsizetype clutTableSize = clutElement.gridPointsX * clutElement.gridPointsY * clutElement.gridPointsZ * clutElement.gridPointsW;
+ if ((mab.clutOffset + 20 + clutTableSize * mab.outputChannels * precision) > tagEntry.size) {
+ qCWarning(lcIcc) << "CLUT oversized for tag";
+ return false;
+ }
+
+ clutElement.table.resize(clutTableSize);
+ if (precision == 2) {
+ QList<uint16_t> clutTable(clutTableSize * mab.outputChannels);
+ qFromBigEndian<uint16_t>(data.constData() + tagEntry.offset + mab.clutOffset + 20, clutTable.size(), clutTable.data());
+ parseCLUT(clutTable.constData(), (1.f/65535.f), &clutElement, mab.outputChannels);
+ } else {
+ const uint8_t *clutTable = reinterpret_cast<const uint8_t *>(data.constData() + tagEntry.offset + mab.clutOffset + 20);
+ parseCLUT(clutTable, (1.f/255.f), &clutElement, mab.outputChannels);
+ }
+ } else if (colorSpacePrivate->colorModel == QColorSpace::ColorModel::Cmyk) {
+ qCWarning(lcIcc) << "Cmyk conversion must have a CLUT";
+ return false;
+ }
+
+ if (isAb) {
+ if (mab.aCurvesOffset) {
+ if (!aCurvesAreLinear)
+ colorSpacePrivate->mAB.append(std::move(aTableElement));
+ if (!clutElement.isEmpty())
+ colorSpacePrivate->mAB.append(std::move(clutElement));
+ }
+ if (mab.mCurvesOffset && mab.outputChannels == 3) {
+ if (!mCurvesAreLinear)
+ colorSpacePrivate->mAB.append(std::move(mTableElement));
+ if (!matrixElement.isIdentity())
+ colorSpacePrivate->mAB.append(std::move(matrixElement));
+ if (!offsetElement.isNull())
+ colorSpacePrivate->mAB.append(std::move(offsetElement));
+ }
+ if (!bCurvesAreLinear|| colorSpacePrivate->mAB.isEmpty())
+ colorSpacePrivate->mAB.append(std::move(bTableElement));
+ } else {
+ if (!bCurvesAreLinear)
+ colorSpacePrivate->mBA.append(std::move(bTableElement));
+ if (mab.mCurvesOffset && mab.inputChannels == 3) {
+ if (!matrixElement.isIdentity())
+ colorSpacePrivate->mBA.append(std::move(matrixElement));
+ if (!offsetElement.isNull())
+ colorSpacePrivate->mBA.append(std::move(offsetElement));
+ if (!mCurvesAreLinear)
+ colorSpacePrivate->mBA.append(std::move(mTableElement));
+ }
+ if (mab.aCurvesOffset) {
+ if (!clutElement.isEmpty())
+ colorSpacePrivate->mBA.append(std::move(clutElement));
+ if (!aCurvesAreLinear)
+ colorSpacePrivate->mBA.append(std::move(aTableElement));
+ }
+ if (colorSpacePrivate->mBA.isEmpty()) // Ensure non-empty to indicate valid empty transform
+ colorSpacePrivate->mBA.append(std::move(bTableElement));
+ }
+
+ return true;
+}
+
+static bool parseA2B(const QByteArray &data, const TagEntry &tagEntry, QColorSpacePrivate *privat, bool isAb)
+{
+ const GenericTagData a2bData = qFromUnaligned<GenericTagData>(data.constData() + tagEntry.offset);
+ if (a2bData.type == quint32(Tag::mft1))
+ return parseLutData<Lut8TagData>(data, tagEntry, privat, isAb);
+ else if (a2bData.type == quint32(Tag::mft2))
+ return parseLutData<Lut16TagData>(data, tagEntry, privat, isAb);
+ else if (a2bData.type == quint32(Tag::mAB_) || a2bData.type == quint32(Tag::mBA_))
+ return parseMabData(data, tagEntry, privat, isAb);
+
+ qCWarning(lcIcc) << "fromIccProfile: Unknown A2B/B2A data type";
return false;
}
-bool parseDesc(const QByteArray &data, const TagEntry &tagEntry, QString &descName)
+static bool parseDesc(const QByteArray &data, const TagEntry &tagEntry, QString &descName)
{
const GenericTagData tag = qFromUnaligned<GenericTagData>(data.constData() + tagEntry.offset);
@@ -581,7 +1088,7 @@ bool parseDesc(const QByteArray &data, const TagEntry &tagEntry, QString &descNa
const MlucTagData mluc = qFromUnaligned<MlucTagData>(data.constData() + tagEntry.offset);
if (mluc.recordCount < 1)
return false;
- if (mluc.recordSize < 12)
+ if (mluc.recordSize != 12)
return false;
// We just use the primary record regardless of language or country.
const quint32 stringOffset = mluc.records[0].offset;
@@ -601,6 +1108,147 @@ bool parseDesc(const QByteArray &data, const TagEntry &tagEntry, QString &descNa
return true;
}
+static bool parseRgbMatrix(const QByteArray &data, const QHash<Tag, TagEntry> &tagIndex, QColorSpacePrivate *colorspaceDPtr)
+{
+ // Parse XYZ tags
+ if (!parseXyzData(data, tagIndex[Tag::rXYZ], colorspaceDPtr->toXyz.r))
+ return false;
+ if (!parseXyzData(data, tagIndex[Tag::gXYZ], colorspaceDPtr->toXyz.g))
+ return false;
+ if (!parseXyzData(data, tagIndex[Tag::bXYZ], colorspaceDPtr->toXyz.b))
+ return false;
+ if (!parseXyzData(data, tagIndex[Tag::wtpt], colorspaceDPtr->whitePoint))
+ return false;
+ if (!colorspaceDPtr->toXyz.isValid() || !colorspaceDPtr->whitePoint.isValid() || colorspaceDPtr->whitePoint.isNull()) {
+ qCWarning(lcIcc) << "Invalid XYZ values in RGB matrix";
+ return false;
+ }
+
+ colorspaceDPtr->primaries = QColorSpace::Primaries::Custom;
+ if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromSRgb()) {
+ qCDebug(lcIcc) << "fromIccProfile: sRGB primaries detected";
+ colorspaceDPtr->primaries = QColorSpace::Primaries::SRgb;
+ } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromAdobeRgb()) {
+ qCDebug(lcIcc) << "fromIccProfile: Adobe RGB primaries detected";
+ colorspaceDPtr->primaries = QColorSpace::Primaries::AdobeRgb;
+ } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromDciP3D65()) {
+ qCDebug(lcIcc) << "fromIccProfile: DCI-P3 D65 primaries detected";
+ colorspaceDPtr->primaries = QColorSpace::Primaries::DciP3D65;
+ }
+ if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromProPhotoRgb()) {
+ qCDebug(lcIcc) << "fromIccProfile: ProPhoto RGB primaries detected";
+ colorspaceDPtr->primaries = QColorSpace::Primaries::ProPhotoRgb;
+ }
+ return true;
+}
+
+static bool parseGrayMatrix(const QByteArray &data, const QHash<Tag, TagEntry> &tagIndex, QColorSpacePrivate *colorspaceDPtr)
+{
+ QColorVector whitePoint;
+ if (!parseXyzData(data, tagIndex[Tag::wtpt], whitePoint))
+ return false;
+ if (!whitePoint.isValid() || !qFuzzyCompare(whitePoint.y, 1.0f) || (1.0f + whitePoint.z + whitePoint.x) == 0.0f) {
+ qCWarning(lcIcc) << "fromIccProfile: Invalid ICC profile - gray white-point not normalized";
+ return false;
+ }
+ colorspaceDPtr->primaries = QColorSpace::Primaries::Custom;
+ colorspaceDPtr->whitePoint = whitePoint;
+ return true;
+}
+
+static bool parseChad(const QByteArray &data, const TagEntry &tagEntry, QColorSpacePrivate *colorspaceDPtr)
+{
+ if (tagEntry.size < sizeof(Sf32TagData) || qsizetype(tagEntry.size) > data.size())
+ return false;
+ const Sf32TagData chadtag = qFromUnaligned<Sf32TagData>(data.constData() + tagEntry.offset);
+ if (chadtag.type != uint32_t(Tag::sf32)) {
+ qCWarning(lcIcc, "fromIccProfile: bad chad data type");
+ return false;
+ }
+ QColorMatrix chad;
+ chad.r.x = fromFixedS1516(chadtag.value[0]);
+ chad.g.x = fromFixedS1516(chadtag.value[1]);
+ chad.b.x = fromFixedS1516(chadtag.value[2]);
+ chad.r.y = fromFixedS1516(chadtag.value[3]);
+ chad.g.y = fromFixedS1516(chadtag.value[4]);
+ chad.b.y = fromFixedS1516(chadtag.value[5]);
+ chad.r.z = fromFixedS1516(chadtag.value[6]);
+ chad.g.z = fromFixedS1516(chadtag.value[7]);
+ chad.b.z = fromFixedS1516(chadtag.value[8]);
+
+ if (!chad.isValid()) {
+ qCWarning(lcIcc, "fromIccProfile: invalid chad matrix");
+ return false;
+ }
+ colorspaceDPtr->chad = chad;
+ return true;
+}
+
+static bool parseTRCs(const QByteArray &data, const QHash<Tag, TagEntry> &tagIndex, QColorSpacePrivate *colorspaceDPtr, bool isColorSpaceTypeGray)
+{
+ TagEntry rTrc;
+ TagEntry gTrc;
+ TagEntry bTrc;
+ if (isColorSpaceTypeGray) {
+ rTrc = tagIndex[Tag::kTRC];
+ gTrc = tagIndex[Tag::kTRC];
+ bTrc = tagIndex[Tag::kTRC];
+ } else if (tagIndex.contains(Tag::aarg) && tagIndex.contains(Tag::aagg) && tagIndex.contains(Tag::aabg)) {
+ // Apple extension for parametric version of TRCs in ICCv2:
+ rTrc = tagIndex[Tag::aarg];
+ gTrc = tagIndex[Tag::aagg];
+ bTrc = tagIndex[Tag::aabg];
+ } else {
+ rTrc = tagIndex[Tag::rTRC];
+ gTrc = tagIndex[Tag::gTRC];
+ bTrc = tagIndex[Tag::bTRC];
+ }
+
+ QColorTrc rCurve;
+ QColorTrc gCurve;
+ QColorTrc bCurve;
+ if (!parseTRC(QByteArrayView(data).sliced(rTrc.offset, rTrc.size), rCurve, QColorTransferTable::TwoWay)) {
+ qCWarning(lcIcc) << "fromIccProfile: Invalid rTRC";
+ return false;
+ }
+ if (!parseTRC(QByteArrayView(data).sliced(gTrc.offset, gTrc.size), gCurve, QColorTransferTable::TwoWay)) {
+ qCWarning(lcIcc) << "fromIccProfile: Invalid gTRC";
+ return false;
+ }
+ if (!parseTRC(QByteArrayView(data).sliced(bTrc.offset, bTrc.size), bCurve, QColorTransferTable::TwoWay)) {
+ qCWarning(lcIcc) << "fromIccProfile: Invalid bTRC";
+ return false;
+ }
+ if (rCurve == gCurve && gCurve == bCurve) {
+ if (rCurve.isIdentity()) {
+ qCDebug(lcIcc) << "fromIccProfile: Linear gamma detected";
+ colorspaceDPtr->trc[0] = QColorTransferFunction();
+ colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Linear;
+ colorspaceDPtr->gamma = 1.0f;
+ } else if (rCurve.m_type == QColorTrc::Type::Function && rCurve.m_fun.isGamma()) {
+ qCDebug(lcIcc) << "fromIccProfile: Simple gamma detected";
+ colorspaceDPtr->trc[0] = QColorTransferFunction::fromGamma(rCurve.m_fun.m_g);
+ colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Gamma;
+ colorspaceDPtr->gamma = rCurve.m_fun.m_g;
+ } else if (rCurve.m_type == QColorTrc::Type::Function && rCurve.m_fun.isSRgb()) {
+ qCDebug(lcIcc) << "fromIccProfile: sRGB gamma detected";
+ colorspaceDPtr->trc[0] = QColorTransferFunction::fromSRgb();
+ colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::SRgb;
+ } else {
+ colorspaceDPtr->trc[0] = rCurve;
+ colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Custom;
+ }
+ colorspaceDPtr->trc[1] = colorspaceDPtr->trc[0];
+ colorspaceDPtr->trc[2] = colorspaceDPtr->trc[0];
+ } else {
+ colorspaceDPtr->trc[0] = rCurve;
+ colorspaceDPtr->trc[1] = gCurve;
+ colorspaceDPtr->trc[2] = bCurve;
+ colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Custom;
+ }
+ return true;
+}
+
bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
{
if (data.size() < qsizetype(sizeof(ICCProfileHeader))) {
@@ -657,146 +1305,92 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
tagIndex.insert(Tag(quint32(tagTable.signature)), { tagTable.offset, tagTable.size });
}
- // Check the profile is three-component matrix based (what we currently support):
+ bool threeComponentMatrix = true;
+
if (header.inputColorSpace == uint(ColorSpaceType::Rgb)) {
+ // Check the profile is three-component matrix based:
if (!tagIndex.contains(Tag::rXYZ) || !tagIndex.contains(Tag::gXYZ) || !tagIndex.contains(Tag::bXYZ) ||
!tagIndex.contains(Tag::rTRC) || !tagIndex.contains(Tag::gTRC) || !tagIndex.contains(Tag::bTRC) ||
- !tagIndex.contains(Tag::wtpt)) {
- qCInfo(lcIcc) << "fromIccProfile: Unsupported ICC profile - not three component matrix based";
- return false;
+ !tagIndex.contains(Tag::wtpt) || header.pcs == uint(Tag::Lab_)) {
+ threeComponentMatrix = false;
+ // Check if the profile is valid n-LUT based:
+ if (!tagIndex.contains(Tag::A2B0)) {
+ qCWarning(lcIcc) << "fromIccProfile: Invalid ICC profile - neither valid three component nor n-LUT";
+ return false;
+ }
}
- } else {
- Q_ASSERT(header.inputColorSpace == uint(ColorSpaceType::Gray));
+ } else if (header.inputColorSpace == uint(ColorSpaceType::Gray)) {
if (!tagIndex.contains(Tag::kTRC) || !tagIndex.contains(Tag::wtpt)) {
qCWarning(lcIcc) << "fromIccProfile: Invalid ICC profile - not valid gray scale based";
return false;
}
+ } else if (header.inputColorSpace == uint(ColorSpaceType::Cmyk)) {
+ threeComponentMatrix = false;
+ if (!tagIndex.contains(Tag::A2B0)) {
+ qCWarning(lcIcc) << "fromIccProfile: Invalid ICC profile - CMYK, not n-LUT";
+ return false;
+ }
+ } else {
+ Q_UNREACHABLE();
}
colorSpace->detach();
QColorSpacePrivate *colorspaceDPtr = QColorSpacePrivate::get(*colorSpace);
- if (header.inputColorSpace == uint(ColorSpaceType::Rgb)) {
- // Parse XYZ tags
- if (!parseXyzData(data, tagIndex[Tag::rXYZ], colorspaceDPtr->toXyz.r))
- return false;
- if (!parseXyzData(data, tagIndex[Tag::gXYZ], colorspaceDPtr->toXyz.g))
- return false;
- if (!parseXyzData(data, tagIndex[Tag::bXYZ], colorspaceDPtr->toXyz.b))
- return false;
- if (!parseXyzData(data, tagIndex[Tag::wtpt], colorspaceDPtr->whitePoint))
- return false;
+ if (threeComponentMatrix) {
+ colorspaceDPtr->isPcsLab = false;
+ colorspaceDPtr->transformModel = QColorSpace::TransformModel::ThreeComponentMatrix;
- colorspaceDPtr->primaries = QColorSpace::Primaries::Custom;
- if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromSRgb()) {
- qCDebug(lcIcc) << "fromIccProfile: sRGB primaries detected";
- colorspaceDPtr->primaries = QColorSpace::Primaries::SRgb;
- } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromAdobeRgb()) {
- qCDebug(lcIcc) << "fromIccProfile: Adobe RGB primaries detected";
- colorspaceDPtr->primaries = QColorSpace::Primaries::AdobeRgb;
- } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromDciP3D65()) {
- qCDebug(lcIcc) << "fromIccProfile: DCI-P3 D65 primaries detected";
- colorspaceDPtr->primaries = QColorSpace::Primaries::DciP3D65;
- }
- if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromProPhotoRgb()) {
- qCDebug(lcIcc) << "fromIccProfile: ProPhoto RGB primaries detected";
- colorspaceDPtr->primaries = QColorSpace::Primaries::ProPhotoRgb;
- }
- } else {
- // We will use sRGB primaries and fit to match the given white-point if
- // it doesn't match sRGB's.
- QColorVector whitePoint;
- if (!parseXyzData(data, tagIndex[Tag::wtpt], whitePoint))
- return false;
- if (!qFuzzyCompare(whitePoint.y, 1.0f) || (1.0f + whitePoint.z + whitePoint.x) == 0.0f) {
- qCWarning(lcIcc) << "fromIccProfile: Invalid ICC profile - gray white-point not normalized";
- return false;
- }
- if (whitePoint == QColorVector::D65()) {
- colorspaceDPtr->primaries = QColorSpace::Primaries::SRgb;
+ if (header.inputColorSpace == uint(ColorSpaceType::Rgb)) {
+ if (!parseRgbMatrix(data, tagIndex, colorspaceDPtr))
+ return false;
+ colorspaceDPtr->colorModel = QColorSpace::ColorModel::Rgb;
+ } else if (header.inputColorSpace == uint(ColorSpaceType::Gray)) {
+ if (!parseGrayMatrix(data, tagIndex, colorspaceDPtr))
+ return false;
+ colorspaceDPtr->colorModel = QColorSpace::ColorModel::Gray;
} else {
- colorspaceDPtr->primaries = QColorSpace::Primaries::Custom;
- // Calculate chromaticity from xyz (assuming y == 1.0f).
- float y = 1.0f / (1.0f + whitePoint.z + whitePoint.x);
- float x = whitePoint.x * y;
- QColorSpacePrimaries primaries(QColorSpace::Primaries::SRgb);
- primaries.whitePoint = QPointF(x,y);
- if (!primaries.areValid()) {
- qCWarning(lcIcc, "fromIccProfile: Invalid ICC profile - invalid white-point(%f, %f)", x, y);
+ Q_UNREACHABLE();
+ }
+ if (auto it = tagIndex.constFind(Tag::chad); it != tagIndex.constEnd()) {
+ if (!parseChad(data, it.value(), colorspaceDPtr))
return false;
- }
- colorspaceDPtr->toXyz = primaries.toXyzMatrix();
+ } else {
+ colorspaceDPtr->chad = QColorMatrix::chromaticAdaptation(colorspaceDPtr->whitePoint);
}
- }
- // Reset the matrix to our canonical values:
- if (colorspaceDPtr->primaries != QColorSpace::Primaries::Custom)
- colorspaceDPtr->setToXyzMatrix();
+ if (colorspaceDPtr->colorModel == QColorSpace::ColorModel::Gray)
+ colorspaceDPtr->toXyz = colorspaceDPtr->chad;
- // Parse TRC tags
- TagEntry rTrc;
- TagEntry gTrc;
- TagEntry bTrc;
- if (header.inputColorSpace == uint(ColorSpaceType::Gray)) {
- rTrc = tagIndex[Tag::kTRC];
- gTrc = tagIndex[Tag::kTRC];
- bTrc = tagIndex[Tag::kTRC];
- } else if (tagIndex.contains(Tag::aarg) && tagIndex.contains(Tag::aagg) && tagIndex.contains(Tag::aabg)) {
- // Apple extension for parametric version of TRCs in ICCv2:
- rTrc = tagIndex[Tag::aarg];
- gTrc = tagIndex[Tag::aagg];
- bTrc = tagIndex[Tag::aabg];
+ // Reset the matrix to our canonical values:
+ if (colorspaceDPtr->primaries != QColorSpace::Primaries::Custom)
+ colorspaceDPtr->setToXyzMatrix();
+
+ if (!parseTRCs(data, tagIndex, colorspaceDPtr, header.inputColorSpace == uint(ColorSpaceType::Gray)))
+ return false;
} else {
- rTrc = tagIndex[Tag::rTRC];
- gTrc = tagIndex[Tag::gTRC];
- bTrc = tagIndex[Tag::bTRC];
- }
+ colorspaceDPtr->isPcsLab = (header.pcs == uint(Tag::Lab_));
+ colorspaceDPtr->transformModel = QColorSpace::TransformModel::ElementListProcessing;
+ if (header.inputColorSpace == uint(ColorSpaceType::Cmyk))
+ colorspaceDPtr->colorModel = QColorSpace::ColorModel::Cmyk;
+ else
+ colorspaceDPtr->colorModel = QColorSpace::ColorModel::Rgb;
- QColorTrc rCurve;
- QColorTrc gCurve;
- QColorTrc bCurve;
- if (!parseTRC(data, rTrc, rCurve)) {
- qCWarning(lcIcc) << "fromIccProfile: Invalid rTRC";
- return false;
- }
- if (!parseTRC(data, gTrc, gCurve)) {
- qCWarning(lcIcc) << "fromIccProfile: Invalid gTRC";
- return false;
- }
- if (!parseTRC(data, bTrc, bCurve)) {
- qCWarning(lcIcc) << "fromIccProfile: Invalid bTRC";
- return false;
- }
- if (rCurve == gCurve && gCurve == bCurve && rCurve.m_type == QColorTrc::Type::Function) {
- if (rCurve.m_fun.isLinear()) {
- qCDebug(lcIcc) << "fromIccProfile: Linear gamma detected";
- colorspaceDPtr->trc[0] = QColorTransferFunction();
- colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Linear;
- colorspaceDPtr->gamma = 1.0f;
- } else if (rCurve.m_fun.isGamma()) {
- qCDebug(lcIcc) << "fromIccProfile: Simple gamma detected";
- colorspaceDPtr->trc[0] = QColorTransferFunction::fromGamma(rCurve.m_fun.m_g);
- colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Gamma;
- colorspaceDPtr->gamma = rCurve.m_fun.m_g;
- } else if (rCurve.m_fun.isSRgb()) {
- qCDebug(lcIcc) << "fromIccProfile: sRGB gamma detected";
- colorspaceDPtr->trc[0] = QColorTransferFunction::fromSRgb();
- colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::SRgb;
- } else {
- colorspaceDPtr->trc[0] = rCurve;
- colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Custom;
+ // Only parse the default perceptual transform for now
+ if (!parseA2B(data, tagIndex[Tag::A2B0], colorspaceDPtr, true))
+ return false;
+ if (auto it = tagIndex.constFind(Tag::B2A0); it != tagIndex.constEnd()) {
+ if (!parseA2B(data, it.value(), colorspaceDPtr, false))
+ return false;
}
- colorspaceDPtr->trc[1] = colorspaceDPtr->trc[0];
- colorspaceDPtr->trc[2] = colorspaceDPtr->trc[0];
- } else {
- colorspaceDPtr->trc[0] = rCurve;
- colorspaceDPtr->trc[1] = gCurve;
- colorspaceDPtr->trc[2] = bCurve;
- colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Custom;
+ if (auto it = tagIndex.constFind(Tag::wtpt); it != tagIndex.constEnd()) {
+ if (!parseXyzData(data, it.value(), colorspaceDPtr->whitePoint))
+ return false;
+ }
}
- if (tagIndex.contains(Tag::desc)) {
- if (!parseDesc(data, tagIndex[Tag::desc], colorspaceDPtr->description))
+ if (auto it = tagIndex.constFind(Tag::desc); it != tagIndex.constEnd()) {
+ if (!parseDesc(data, it.value(), colorspaceDPtr->description))
qCWarning(lcIcc) << "fromIccProfile: Failed to parse description";
else
qCDebug(lcIcc) << "fromIccProfile: Description" << colorspaceDPtr->description;
@@ -808,6 +1402,7 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
colorspaceDPtr->iccProfile = data;
+ Q_ASSERT(colorspaceDPtr->isValid());
return true;
}
diff --git a/src/gui/painting/qimageeffects.cpp b/src/gui/painting/qimageeffects.cpp
new file mode 100644
index 0000000000..7c2b947e08
--- /dev/null
+++ b/src/gui/painting/qimageeffects.cpp
@@ -0,0 +1,327 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qmath.h"
+#include "qdrawhelper_p.h"
+#include "qmemrotate_p.h"
+#include "qpainter.h"
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+template <int shift>
+inline int qt_static_shift(int value)
+{
+ if (shift == 0)
+ return value;
+ else if (shift > 0)
+ return value << (uint(shift) & 0x1f);
+ else
+ return value >> (uint(-shift) & 0x1f);
+}
+
+template<int aprec, int zprec>
+inline void qt_blurinner(uchar *bptr, int &zR, int &zG, int &zB, int &zA, int alpha)
+{
+ QRgb *pixel = (QRgb *)bptr;
+
+#define Z_MASK (0xff << zprec)
+ const int A_zprec = qt_static_shift<zprec - 24>(*pixel) & Z_MASK;
+ const int R_zprec = qt_static_shift<zprec - 16>(*pixel) & Z_MASK;
+ const int G_zprec = qt_static_shift<zprec - 8>(*pixel) & Z_MASK;
+ const int B_zprec = qt_static_shift<zprec>(*pixel) & Z_MASK;
+#undef Z_MASK
+
+ const int zR_zprec = zR >> aprec;
+ const int zG_zprec = zG >> aprec;
+ const int zB_zprec = zB >> aprec;
+ const int zA_zprec = zA >> aprec;
+
+ zR += alpha * (R_zprec - zR_zprec);
+ zG += alpha * (G_zprec - zG_zprec);
+ zB += alpha * (B_zprec - zB_zprec);
+ zA += alpha * (A_zprec - zA_zprec);
+
+#define ZA_MASK (0xff << (zprec + aprec))
+ *pixel =
+ qt_static_shift<24 - zprec - aprec>(zA & ZA_MASK)
+ | qt_static_shift<16 - zprec - aprec>(zR & ZA_MASK)
+ | qt_static_shift<8 - zprec - aprec>(zG & ZA_MASK)
+ | qt_static_shift<-zprec - aprec>(zB & ZA_MASK);
+#undef ZA_MASK
+}
+
+const int alphaIndex = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3);
+
+template<int aprec, int zprec>
+inline void qt_blurinner_alphaOnly(uchar *bptr, int &z, int alpha)
+{
+ const int A_zprec = int(*(bptr)) << zprec;
+ const int z_zprec = z >> aprec;
+ z += alpha * (A_zprec - z_zprec);
+ *(bptr) = z >> (zprec + aprec);
+}
+
+template<int aprec, int zprec, bool alphaOnly>
+inline void qt_blurrow(QImage & im, int line, int alpha)
+{
+ uchar *bptr = im.scanLine(line);
+
+ int zR = 0, zG = 0, zB = 0, zA = 0;
+
+ if (alphaOnly && im.format() != QImage::Format_Indexed8)
+ bptr += alphaIndex;
+
+ const int stride = im.depth() >> 3;
+ const int im_width = im.width();
+ for (int index = 0; index < im_width; ++index) {
+ if (alphaOnly)
+ qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
+ else
+ qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
+ bptr += stride;
+ }
+
+ bptr -= stride;
+
+ for (int index = im_width - 2; index >= 0; --index) {
+ bptr -= stride;
+ if (alphaOnly)
+ qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
+ else
+ qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
+ }
+}
+
+/*
+* expblur(QImage &img, int radius)
+*
+* Based on exponential blur algorithm by Jani Huhtanen
+*
+* In-place blur of image 'img' with kernel
+* of approximate radius 'radius'.
+*
+* Blurs with two sided exponential impulse
+* response.
+*
+* aprec = precision of alpha parameter
+* in fixed-point format 0.aprec
+*
+* zprec = precision of state parameters
+* zR,zG,zB and zA in fp format 8.zprec
+*/
+template <int aprec, int zprec, bool alphaOnly>
+void expblur(QImage &img, qreal radius, bool improvedQuality = false, int transposed = 0)
+{
+ // halve the radius if we're using two passes
+ if (improvedQuality)
+ radius *= qreal(0.5);
+
+ Q_ASSERT(img.format() == QImage::Format_ARGB32_Premultiplied
+ || img.format() == QImage::Format_RGB32
+ || img.format() == QImage::Format_Indexed8
+ || img.format() == QImage::Format_Grayscale8);
+
+ // choose the alpha such that pixels at radius distance from a fully
+ // saturated pixel will have an alpha component of no greater than
+ // the cutOffIntensity
+ const qreal cutOffIntensity = 2;
+ int alpha = radius <= qreal(1e-5)
+ ? ((1 << aprec)-1)
+ : qRound((1<<aprec)*(1 - qPow(cutOffIntensity * (1 / qreal(255)), 1 / radius)));
+
+ int img_height = img.height();
+ for (int row = 0; row < img_height; ++row) {
+ for (int i = 0; i <= int(improvedQuality); ++i)
+ qt_blurrow<aprec, zprec, alphaOnly>(img, row, alpha);
+ }
+
+ QImage temp(img.height(), img.width(), img.format());
+ temp.setDevicePixelRatio(img.devicePixelRatio());
+ if (transposed >= 0) {
+ if (img.depth() == 8) {
+ qt_memrotate270(reinterpret_cast<const quint8*>(img.bits()),
+ img.width(), img.height(), img.bytesPerLine(),
+ reinterpret_cast<quint8*>(temp.bits()),
+ temp.bytesPerLine());
+ } else {
+ qt_memrotate270(reinterpret_cast<const quint32*>(img.bits()),
+ img.width(), img.height(), img.bytesPerLine(),
+ reinterpret_cast<quint32*>(temp.bits()),
+ temp.bytesPerLine());
+ }
+ } else {
+ if (img.depth() == 8) {
+ qt_memrotate90(reinterpret_cast<const quint8*>(img.bits()),
+ img.width(), img.height(), img.bytesPerLine(),
+ reinterpret_cast<quint8*>(temp.bits()),
+ temp.bytesPerLine());
+ } else {
+ qt_memrotate90(reinterpret_cast<const quint32*>(img.bits()),
+ img.width(), img.height(), img.bytesPerLine(),
+ reinterpret_cast<quint32*>(temp.bits()),
+ temp.bytesPerLine());
+ }
+ }
+
+ img_height = temp.height();
+ for (int row = 0; row < img_height; ++row) {
+ for (int i = 0; i <= int(improvedQuality); ++i)
+ qt_blurrow<aprec, zprec, alphaOnly>(temp, row, alpha);
+ }
+
+ if (transposed == 0) {
+ if (img.depth() == 8) {
+ qt_memrotate90(reinterpret_cast<const quint8*>(temp.bits()),
+ temp.width(), temp.height(), temp.bytesPerLine(),
+ reinterpret_cast<quint8*>(img.bits()),
+ img.bytesPerLine());
+ } else {
+ qt_memrotate90(reinterpret_cast<const quint32*>(temp.bits()),
+ temp.width(), temp.height(), temp.bytesPerLine(),
+ reinterpret_cast<quint32*>(img.bits()),
+ img.bytesPerLine());
+ }
+ } else {
+ img = temp;
+ }
+}
+
+} // namespace
+
+#define AVG(a,b) ( ((((a)^(b)) & 0xfefefefeUL) >> 1) + ((a)&(b)) )
+#define AVG16(a,b) ( ((((a)^(b)) & 0xf7deUL) >> 1) + ((a)&(b)) )
+
+QImage qt_halfScaled(const QImage &source)
+{
+ if (source.width() < 2 || source.height() < 2)
+ return QImage();
+
+ QImage srcImage = source;
+
+ if (source.format() == QImage::Format_Indexed8 || source.format() == QImage::Format_Grayscale8) {
+ // assumes grayscale
+ QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
+ dest.setDevicePixelRatio(source.devicePixelRatio());
+
+ const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
+ qsizetype sx = srcImage.bytesPerLine();
+ qsizetype sx2 = sx << 1;
+
+ uchar *dst = reinterpret_cast<uchar*>(dest.bits());
+ qsizetype dx = dest.bytesPerLine();
+ int ww = dest.width();
+ int hh = dest.height();
+
+ for (int y = hh; y; --y, dst += dx, src += sx2) {
+ const uchar *p1 = src;
+ const uchar *p2 = src + sx;
+ uchar *q = dst;
+ for (int x = ww; x; --x, ++q, p1 += 2, p2 += 2)
+ *q = ((int(p1[0]) + int(p1[1]) + int(p2[0]) + int(p2[1])) + 2) >> 2;
+ }
+
+ return dest;
+ } else if (source.format() == QImage::Format_ARGB8565_Premultiplied) {
+ QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
+ dest.setDevicePixelRatio(source.devicePixelRatio());
+
+ const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
+ qsizetype sx = srcImage.bytesPerLine();
+ qsizetype sx2 = sx << 1;
+
+ uchar *dst = reinterpret_cast<uchar*>(dest.bits());
+ qsizetype dx = dest.bytesPerLine();
+ int ww = dest.width();
+ int hh = dest.height();
+
+ for (int y = hh; y; --y, dst += dx, src += sx2) {
+ const uchar *p1 = src;
+ const uchar *p2 = src + sx;
+ uchar *q = dst;
+ for (int x = ww; x; --x, q += 3, p1 += 6, p2 += 6) {
+ // alpha
+ q[0] = AVG(AVG(p1[0], p1[3]), AVG(p2[0], p2[3]));
+ // rgb
+ const quint16 p16_1 = (p1[2] << 8) | p1[1];
+ const quint16 p16_2 = (p1[5] << 8) | p1[4];
+ const quint16 p16_3 = (p2[2] << 8) | p2[1];
+ const quint16 p16_4 = (p2[5] << 8) | p2[4];
+ const quint16 result = AVG16(AVG16(p16_1, p16_2), AVG16(p16_3, p16_4));
+ q[1] = result & 0xff;
+ q[2] = result >> 8;
+ }
+ }
+
+ return dest;
+ } else if (source.format() != QImage::Format_ARGB32_Premultiplied
+ && source.format() != QImage::Format_RGB32)
+ {
+ srcImage = source.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ }
+
+ QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
+ dest.setDevicePixelRatio(source.devicePixelRatio());
+
+ const quint32 *src = reinterpret_cast<const quint32*>(const_cast<const QImage &>(srcImage).bits());
+ qsizetype sx = srcImage.bytesPerLine() >> 2;
+ qsizetype sx2 = sx << 1;
+
+ quint32 *dst = reinterpret_cast<quint32*>(dest.bits());
+ qsizetype dx = dest.bytesPerLine() >> 2;
+ int ww = dest.width();
+ int hh = dest.height();
+
+ for (int y = hh; y; --y, dst += dx, src += sx2) {
+ const quint32 *p1 = src;
+ const quint32 *p2 = src + sx;
+ quint32 *q = dst;
+ for (int x = ww; x; --x, q++, p1 += 2, p2 += 2)
+ *q = AVG(AVG(p1[0], p1[1]), AVG(p2[0], p2[1]));
+ }
+
+ return dest;
+}
+
+#undef AVG
+#undef AVG16
+
+Q_GUI_EXPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0)
+{
+ if (blurImage.format() != QImage::Format_ARGB32_Premultiplied
+ && blurImage.format() != QImage::Format_RGB32)
+ {
+ blurImage = std::move(blurImage).convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ }
+
+ qreal scale = 1;
+ if (radius >= 4 && blurImage.width() >= 2 && blurImage.height() >= 2) {
+ blurImage = qt_halfScaled(blurImage);
+ scale = 2;
+ radius *= qreal(0.5);
+ }
+
+ if (alphaOnly)
+ expblur<12, 10, true>(blurImage, radius, quality, transposed);
+ else
+ expblur<12, 10, false>(blurImage, radius, quality, transposed);
+
+ if (p) {
+ p->scale(scale, scale);
+ p->setRenderHint(QPainter::SmoothPixmapTransform);
+ p->drawImage(QRect(QPoint(0, 0), blurImage.deviceIndependentSize().toSize()), blurImage);
+ }
+}
+
+Q_GUI_EXPORT void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0)
+{
+ if (blurImage.format() == QImage::Format_Indexed8 || blurImage.format() == QImage::Format_Grayscale8)
+ expblur<12, 10, true>(blurImage, radius, quality, transposed);
+ else
+ expblur<12, 10, false>(blurImage, radius, quality, transposed);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qimagescale.cpp b/src/gui/painting/qimagescale.cpp
index 80d1a67a3f..a636635fd5 100644
--- a/src/gui/painting/qimagescale.cpp
+++ b/src/gui/painting/qimagescale.cpp
@@ -10,8 +10,9 @@
#include "qrgbafloat.h"
#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
-#include "qsemaphore.h"
-#include "qthreadpool.h"
+#include <qsemaphore.h>
+#include <qthreadpool.h>
+#include <private/qthreadpool_p.h>
#endif
QT_BEGIN_NAMESPACE
@@ -273,7 +274,7 @@ static inline void multithread_pixels_function(QImageScaleInfo *isi, int dh, con
#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16);
segments = std::min(segments, dh);
- QThreadPool *threadPool = QThreadPool::globalInstance();
+ QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) {
QSemaphore semaphore;
int y = 0;
diff --git a/src/gui/painting/qimagescale_neon.cpp b/src/gui/painting/qimagescale_neon.cpp
index c8d364d56c..074b819862 100644
--- a/src/gui/painting/qimagescale_neon.cpp
+++ b/src/gui/painting/qimagescale_neon.cpp
@@ -6,8 +6,9 @@
#include <private/qsimd_p.h>
#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
-#include "qsemaphore.h"
-#include "qthreadpool.h"
+#include <qsemaphore.h>
+#include <qthreadpool.h>
+#include <private/qthreadpool_p.h>
#endif
#if defined(__ARM_NEON__)
@@ -22,7 +23,7 @@ static inline void multithread_pixels_function(QImageScaleInfo *isi, int dh, con
#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16);
segments = std::min(segments, dh);
- QThreadPool *threadPool = QThreadPool::globalInstance();
+ QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) {
QSemaphore semaphore;
int y = 0;
diff --git a/src/gui/painting/qimagescale_sse4.cpp b/src/gui/painting/qimagescale_sse4.cpp
index f55290b46c..982e533a32 100644
--- a/src/gui/painting/qimagescale_sse4.cpp
+++ b/src/gui/painting/qimagescale_sse4.cpp
@@ -7,8 +7,9 @@
#include <private/qsimd_p.h>
#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
-#include "qsemaphore.h"
-#include "qthreadpool.h"
+#include <qsemaphore.h>
+#include <qthreadpool.h>
+#include <private/qthreadpool_p.h>
#endif
#if defined(QT_COMPILER_SUPPORTS_SSE4_1)
@@ -23,7 +24,7 @@ static inline void multithread_pixels_function(QImageScaleInfo *isi, int dh, con
#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16);
segments = std::min(segments, dh);
- QThreadPool *threadPool = QThreadPool::globalInstance();
+ QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) {
QSemaphore semaphore;
int y = 0;
diff --git a/src/gui/painting/qoutlinemapper.cpp b/src/gui/painting/qoutlinemapper.cpp
index 0dfb310ee9..2f87ff43b7 100644
--- a/src/gui/painting/qoutlinemapper.cpp
+++ b/src/gui/painting/qoutlinemapper.cpp
@@ -37,6 +37,24 @@ static const QRectF boundingRect(const QPointF *points, int pointCount)
return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
}
+void QOutlineMapper::setClipRect(QRect clipRect)
+{
+ auto limitCoords = [](QRect r) {
+ const QRect limitRect(QPoint(-QT_RASTER_COORD_LIMIT, -QT_RASTER_COORD_LIMIT),
+ QPoint(QT_RASTER_COORD_LIMIT, QT_RASTER_COORD_LIMIT));
+ r &= limitRect;
+ r.setWidth(qMin(r.width(), QT_RASTER_COORD_LIMIT));
+ r.setHeight(qMin(r.height(), QT_RASTER_COORD_LIMIT));
+ return r;
+ };
+
+ if (clipRect != m_clip_rect) {
+ m_clip_rect = limitCoords(clipRect);
+ const int mw = 1 << 10; // margin width. No need to trigger clipping for slight overshooting
+ m_clip_trigger_rect = QRectF(limitCoords(m_clip_rect.adjusted(-mw, -mw, mw, mw)));
+ }
+}
+
void QOutlineMapper::curveTo(const QPointF &cp1, const QPointF &cp2, const QPointF &ep) {
#ifdef QT_DEBUG_CONVERT
printf("QOutlineMapper::curveTo() (%f, %f)\n", ep.x(), ep.y());
@@ -200,16 +218,8 @@ void QOutlineMapper::endOutline()
m_clip_rect.x(), m_clip_rect.y(), m_clip_rect.width(), m_clip_rect.height());
#endif
-
- // Check for out of dev bounds...
- const bool do_clip = !m_in_clip_elements && ((controlPointRect.left() < -QT_RASTER_COORD_LIMIT
- || controlPointRect.right() > QT_RASTER_COORD_LIMIT
- || controlPointRect.top() < -QT_RASTER_COORD_LIMIT
- || controlPointRect.bottom() > QT_RASTER_COORD_LIMIT
- || controlPointRect.width() > QT_RASTER_COORD_LIMIT
- || controlPointRect.height() > QT_RASTER_COORD_LIMIT));
-
- if (do_clip) {
+ // Avoid rasterizing outside cliprect: faster, and ensures coords < QT_RASTER_COORD_LIMIT
+ if (!m_in_clip_elements && !m_clip_trigger_rect.contains(controlPointRect)) {
clipElements(elements, elementTypes(), m_elements.size());
} else {
convertElements(elements, elementTypes(), m_elements.size());
diff --git a/src/gui/painting/qoutlinemapper_p.h b/src/gui/painting/qoutlinemapper_p.h
index 372f9b4ec2..ff2fff6bac 100644
--- a/src/gui/painting/qoutlinemapper_p.h
+++ b/src/gui/painting/qoutlinemapper_p.h
@@ -45,7 +45,7 @@ Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale
* class QOutlineMapper
*
* Used to map between QPainterPath and the QT_FT_Outline structure used by the
- * freetype scanconvertor.
+ * freetype scanconverter.
*
* The outline mapper uses a path iterator to get points from the path,
* so that it is possible to transform the points as they are converted. The
@@ -79,6 +79,8 @@ public:
m_curve_threshold = scale == 0 ? qreal(0.25) : (qreal(0.25) / scale);
}
+ void setClipRect(QRect clipRect);
+
void beginOutline(Qt::FillRule fillRule)
{
#ifdef QT_DEBUG_CONVERT
@@ -163,6 +165,7 @@ public:
QDataBuffer<int> m_contours;
QRect m_clip_rect;
+ QRectF m_clip_trigger_rect;
QRectF controlPointRect; // only valid after endOutline()
QT_FT_Outline m_outline;
diff --git a/src/gui/painting/qpagelayout.cpp b/src/gui/painting/qpagelayout.cpp
index 456fcf8802..e60f464d6e 100644
--- a/src/gui/painting/qpagelayout.cpp
+++ b/src/gui/painting/qpagelayout.cpp
@@ -39,41 +39,19 @@ Q_GUI_EXPORT qreal qt_pointMultiplier(QPageLayout::Unit unit)
// Multiplier for converting pixels to points.
extern qreal qt_pixelMultiplier(int resolution);
-QPointF qt_convertPoint(const QPointF &xy, QPageLayout::Unit fromUnits, QPageLayout::Unit toUnits)
-{
- // If the size have the same units, or are all 0, then don't need to convert
- if (fromUnits == toUnits || xy.isNull())
- return xy;
-
- // If converting to points then convert and round to 0 decimal places
- if (toUnits == QPageLayout::Point) {
- const qreal multiplier = qt_pointMultiplier(fromUnits);
- return QPointF(qRound(xy.x() * multiplier),
- qRound(xy.y() * multiplier));
- }
-
- // If converting to other units, need to convert to unrounded points first
- QPointF pointXy = (fromUnits == QPageLayout::Point) ? xy : xy * qt_pointMultiplier(fromUnits);
-
- // Then convert from points to required units rounded to 2 decimal places
- const qreal multiplier = qt_pointMultiplier(toUnits);
- return QPointF(qRound(pointXy.x() * 100 / multiplier) / 100.0,
- qRound(pointXy.y() * 100 / multiplier) / 100.0);
-}
-
Q_GUI_EXPORT QMarginsF qt_convertMargins(const QMarginsF &margins, QPageLayout::Unit fromUnits, QPageLayout::Unit toUnits)
{
// If the margins have the same units, or are all 0, then don't need to convert
if (fromUnits == toUnits || margins.isNull())
return margins;
- // If converting to points then convert and round to 0 decimal places
+ // If converting to points then convert and round up to 2 decimal places
if (toUnits == QPageLayout::Point) {
- const qreal multiplier = qt_pointMultiplier(fromUnits);
- return QMarginsF(qRound(margins.left() * multiplier),
- qRound(margins.top() * multiplier),
- qRound(margins.right() * multiplier),
- qRound(margins.bottom() * multiplier));
+ const qreal multiplierX100 = qt_pointMultiplier(fromUnits) * 100;
+ return QMarginsF(qCeil(margins.left() * multiplierX100) / 100.0,
+ qCeil(margins.top() * multiplierX100) / 100.0,
+ qCeil(margins.right() * multiplierX100) / 100.0,
+ qCeil(margins.bottom() * multiplierX100) / 100.0);
}
// If converting to other units, need to convert to unrounded points first
@@ -101,10 +79,10 @@ public:
bool isValid() const;
- void clampMargins(const QMarginsF &margins);
+ QMarginsF clampMargins(const QMarginsF &margins) const;
QMarginsF margins(QPageLayout::Unit units) const;
- QMargins marginsPoints() const;
+ QMarginsF marginsPoints() const;
QMargins marginsPixels(int resolution) const;
void setDefaultMargins(const QMarginsF &minMargins);
@@ -173,12 +151,12 @@ bool QPageLayoutPrivate::isValid() const
return m_pageSize.isValid();
}
-void QPageLayoutPrivate::clampMargins(const QMarginsF &margins)
+QMarginsF QPageLayoutPrivate::clampMargins(const QMarginsF &margins) const
{
- m_margins = QMarginsF(qBound(m_minMargins.left(), margins.left(), m_maxMargins.left()),
- qBound(m_minMargins.top(), margins.top(), m_maxMargins.top()),
- qBound(m_minMargins.right(), margins.right(), m_maxMargins.right()),
- qBound(m_minMargins.bottom(), margins.bottom(), m_maxMargins.bottom()));
+ return QMarginsF(qBound(m_minMargins.left(), margins.left(), m_maxMargins.left()),
+ qBound(m_minMargins.top(), margins.top(), m_maxMargins.top()),
+ qBound(m_minMargins.right(), margins.right(), m_maxMargins.right()),
+ qBound(m_minMargins.bottom(), margins.bottom(), m_maxMargins.bottom()));
}
QMarginsF QPageLayoutPrivate::margins(QPageLayout::Unit units) const
@@ -186,14 +164,14 @@ QMarginsF QPageLayoutPrivate::margins(QPageLayout::Unit units) const
return qt_convertMargins(m_margins, m_units, units);
}
-QMargins QPageLayoutPrivate::marginsPoints() const
+QMarginsF QPageLayoutPrivate::marginsPoints() const
{
- return qt_convertMargins(m_margins, m_units, QPageLayout::Point).toMargins();
+ return qt_convertMargins(m_margins, m_units, QPageLayout::Point);
}
QMargins QPageLayoutPrivate::marginsPixels(int resolution) const
{
- return marginsPoints() / qt_pixelMultiplier(resolution);
+ return QMarginsF(marginsPoints() / qt_pixelMultiplier(resolution)).toMargins();
}
void QPageLayoutPrivate::setDefaultMargins(const QMarginsF &minMargins)
@@ -204,7 +182,7 @@ void QPageLayoutPrivate::setDefaultMargins(const QMarginsF &minMargins)
qMax(m_fullSize.width() - m_minMargins.left(), qreal(0)),
qMax(m_fullSize.height() - m_minMargins.top(), qreal(0)));
if (m_mode == QPageLayout::StandardMode)
- clampMargins(m_margins);
+ m_margins = clampMargins(m_margins);
}
QSizeF QPageLayoutPrivate::fullSizeUnits(QPageLayout::Unit units) const
@@ -310,6 +288,27 @@ QRectF QPageLayoutPrivate::paintRect() const
\value StandardMode Paint Rect includes margins, margins must fall between the minimum and maximum.
\value FullPageMode Paint Rect excludes margins, margins can be any value and must be managed manually.
+
+ In StandardMode, when setting margins, use \l{QPageLayout::OutOfBoundsPolicy::}{Clamp} to
+ automatically clamp the margins to fall between the minimum and maximum
+ allowed values.
+
+ \sa OutOfBoundsPolicy
+*/
+
+/*!
+ \enum QPageLayout::OutOfBoundsPolicy
+ \since 6.8
+
+ Defines the policy for margins that are out of bounds
+
+ \value Reject The margins must fall within the minimum and maximum values,
+ otherwise they will be rejected.
+ \value Clamp The margins are clamped between the minimum and maximum
+ values to ensure they are valid.
+
+ \note The policy has no effect in \l{QPageLayout::Mode}{FullPageMode},
+ where all margins are accepted.
*/
/*!
@@ -547,39 +546,52 @@ QPageLayout::Unit QPageLayout::units() const
}
/*!
- Sets the page margins of the page layout to \a margins
+ Sets the page margins of the page layout to \a margins.
Returns true if the margins were successfully set.
The units used are those currently defined for the layout. To use different
units then call setUnits() first.
- If in the default StandardMode then all the new margins must fall between the
- minimum margins set and the maximum margins allowed by the page size,
- otherwise the margins will not be set.
-
- If in FullPageMode then any margin values will be accepted.
+ Since Qt 6.8, the optional \a outOfBoundsPolicy can be used to specify how
+ margins that are out of bounds are handled.
\sa margins(), units()
*/
-bool QPageLayout::setMargins(const QMarginsF &margins)
+bool QPageLayout::setMargins(const QMarginsF &margins, OutOfBoundsPolicy outOfBoundsPolicy)
{
if (d->m_mode == FullPageMode) {
- d.detach();
- d->m_margins = margins;
+ if (margins != d->m_margins) {
+ d.detach();
+ d->m_margins = margins;
+ }
return true;
- } else if (margins.left() >= d->m_minMargins.left()
- && margins.right() >= d->m_minMargins.right()
- && margins.top() >= d->m_minMargins.top()
- && margins.bottom() >= d->m_minMargins.bottom()
- && margins.left() <= d->m_maxMargins.left()
- && margins.right() <= d->m_maxMargins.right()
- && margins.top() <= d->m_maxMargins.top()
- && margins.bottom() <= d->m_maxMargins.bottom()) {
- d.detach();
- d->m_margins = margins;
+ }
+
+ if (outOfBoundsPolicy == OutOfBoundsPolicy::Clamp) {
+ const QMarginsF clampedMargins = d->clampMargins(margins);
+ if (clampedMargins != d->m_margins) {
+ d.detach();
+ d->m_margins = clampedMargins;
+ }
return true;
}
+
+ if (margins.left() >= d->m_minMargins.left()
+ && margins.right() >= d->m_minMargins.right()
+ && margins.top() >= d->m_minMargins.top()
+ && margins.bottom() >= d->m_minMargins.bottom()
+ && margins.left() <= d->m_maxMargins.left()
+ && margins.right() <= d->m_maxMargins.right()
+ && margins.top() <= d->m_maxMargins.top()
+ && margins.bottom() <= d->m_maxMargins.bottom()) {
+ if (margins != d->m_margins) {
+ d.detach();
+ d->m_margins = margins;
+ }
+ return true;
+ }
+
return false;
}
@@ -590,23 +602,27 @@ bool QPageLayout::setMargins(const QMarginsF &margins)
The units used are those currently defined for the layout. To use different
units call setUnits() first.
- If in the default StandardMode then the new margin must fall between the
- minimum margin set and the maximum margin allowed by the page size,
- otherwise the margin will not be set.
-
- If in FullPageMode then any margin values will be accepted.
+ Since Qt 6.8, the optional \a outOfBoundsPolicy can be used to specify how
+ margins that are out of bounds are handled.
\sa setMargins(), margins()
*/
-bool QPageLayout::setLeftMargin(qreal leftMargin)
+bool QPageLayout::setLeftMargin(qreal leftMargin, OutOfBoundsPolicy outOfBoundsPolicy)
{
+ if (d->m_mode == StandardMode && outOfBoundsPolicy == OutOfBoundsPolicy::Clamp)
+ leftMargin = qBound(d->m_minMargins.left(), leftMargin, d->m_maxMargins.left());
+
+ if (qFuzzyCompare(leftMargin, d->m_margins.left()))
+ return true;
+
if (d->m_mode == FullPageMode
|| (leftMargin >= d->m_minMargins.left() && leftMargin <= d->m_maxMargins.left())) {
d.detach();
d->m_margins.setLeft(leftMargin);
return true;
}
+
return false;
}
@@ -617,23 +633,27 @@ bool QPageLayout::setLeftMargin(qreal leftMargin)
The units used are those currently defined for the layout. To use different
units call setUnits() first.
- If in the default StandardMode then the new margin must fall between the
- minimum margin set and the maximum margin allowed by the page size,
- otherwise the margin will not be set.
-
- If in FullPageMode then any margin values will be accepted.
+ Since Qt 6.8, the optional \a outOfBoundsPolicy can be used to specify how
+ margins that are out of bounds are handled.
\sa setMargins(), margins()
*/
-bool QPageLayout::setRightMargin(qreal rightMargin)
+bool QPageLayout::setRightMargin(qreal rightMargin, OutOfBoundsPolicy outOfBoundsPolicy)
{
+ if (d->m_mode == StandardMode && outOfBoundsPolicy == OutOfBoundsPolicy::Clamp)
+ rightMargin = qBound(d->m_minMargins.right(), rightMargin, d->m_maxMargins.right());
+
+ if (qFuzzyCompare(rightMargin, d->m_margins.right()))
+ return true;
+
if (d->m_mode == FullPageMode
|| (rightMargin >= d->m_minMargins.right() && rightMargin <= d->m_maxMargins.right())) {
d.detach();
d->m_margins.setRight(rightMargin);
return true;
}
+
return false;
}
@@ -644,23 +664,27 @@ bool QPageLayout::setRightMargin(qreal rightMargin)
The units used are those currently defined for the layout. To use different
units call setUnits() first.
- If in the default StandardMode then the new margin must fall between the
- minimum margin set and the maximum margin allowed by the page size,
- otherwise the margin will not be set.
-
- If in FullPageMode then any margin values will be accepted.
+ Since Qt 6.8, the optional \a outOfBoundsPolicy can be used to specify how
+ margins that are out of bounds are handled.
\sa setMargins(), margins()
*/
-bool QPageLayout::setTopMargin(qreal topMargin)
+bool QPageLayout::setTopMargin(qreal topMargin, OutOfBoundsPolicy outOfBoundsPolicy)
{
+ if (d->m_mode == StandardMode && outOfBoundsPolicy == OutOfBoundsPolicy::Clamp)
+ topMargin = qBound(d->m_minMargins.top(), topMargin, d->m_maxMargins.top());
+
+ if (qFuzzyCompare(topMargin, d->m_margins.top()))
+ return true;
+
if (d->m_mode == FullPageMode
|| (topMargin >= d->m_minMargins.top() && topMargin <= d->m_maxMargins.top())) {
d.detach();
d->m_margins.setTop(topMargin);
return true;
}
+
return false;
}
@@ -671,23 +695,27 @@ bool QPageLayout::setTopMargin(qreal topMargin)
The units used are those currently defined for the layout. To use different
units call setUnits() first.
- If in the default StandardMode then the new margin must fall between the
- minimum margin set and the maximum margin allowed by the page size,
- otherwise the margin will not be set.
-
- If in FullPageMode then any margin values will be accepted.
+ Since Qt 6.8, the optional \a outOfBoundsPolicy can be used to specify how
+ margins that are out of bounds are handled.
\sa setMargins(), margins()
*/
-bool QPageLayout::setBottomMargin(qreal bottomMargin)
+bool QPageLayout::setBottomMargin(qreal bottomMargin, OutOfBoundsPolicy outOfBoundsPolicy)
{
+ if (d->m_mode == StandardMode && outOfBoundsPolicy == OutOfBoundsPolicy::Clamp)
+ bottomMargin = qBound(d->m_minMargins.bottom(), bottomMargin, d->m_maxMargins.bottom());
+
+ if (qFuzzyCompare(bottomMargin, d->m_margins.bottom()))
+ return true;
+
if (d->m_mode == FullPageMode
|| (bottomMargin >= d->m_minMargins.bottom() && bottomMargin <= d->m_maxMargins.bottom())) {
d.detach();
d->m_margins.setBottom(bottomMargin);
return true;
}
+
return false;
}
@@ -721,7 +749,7 @@ QMarginsF QPageLayout::margins(Unit units) const
QMargins QPageLayout::marginsPoints() const
{
- return d->marginsPoints();
+ return d->marginsPoints().toMargins();
}
/*!
@@ -888,7 +916,7 @@ QRect QPageLayout::paintRectPoints() const
if (!isValid())
return QRect();
return d->m_mode == FullPageMode ? d->fullRectPoints()
- : d->fullRectPoints() - d->marginsPoints();
+ : d->fullRectPoints() - d->marginsPoints().toMargins();
}
/*!
diff --git a/src/gui/painting/qpagelayout.h b/src/gui/painting/qpagelayout.h
index 04f58bd403..1e340b6d75 100644
--- a/src/gui/painting/qpagelayout.h
+++ b/src/gui/painting/qpagelayout.h
@@ -5,7 +5,7 @@
#define QPAGELAYOUT_H
#include <QtGui/qtguiglobal.h>
-#include <QtCore/qsharedpointer.h>
+#include <QtCore/qshareddata.h>
#include <QtCore/qstring.h>
#include <QtCore/qmargins.h>
@@ -40,6 +40,11 @@ public:
FullPageMode // Paint Rect excludes margins
};
+ enum class OutOfBoundsPolicy {
+ Reject,
+ Clamp
+ };
+
QPageLayout();
QPageLayout(const QPageSize &pageSize, Orientation orientation,
const QMarginsF &margins, Unit units = Point,
@@ -68,11 +73,19 @@ public:
void setUnits(Unit units);
Unit units() const;
+#if QT_GUI_REMOVED_SINCE(6, 8)
bool setMargins(const QMarginsF &margins);
bool setLeftMargin(qreal leftMargin);
bool setRightMargin(qreal rightMargin);
bool setTopMargin(qreal topMargin);
bool setBottomMargin(qreal bottomMargin);
+#endif
+
+ bool setMargins(const QMarginsF &margins, OutOfBoundsPolicy outOfBoundsPolicy = OutOfBoundsPolicy::Reject);
+ bool setLeftMargin(qreal leftMargin, OutOfBoundsPolicy outOfBoundsPolicy = OutOfBoundsPolicy::Reject);
+ bool setRightMargin(qreal rightMargin, OutOfBoundsPolicy outOfBoundsPolicy = OutOfBoundsPolicy::Reject);
+ bool setTopMargin(qreal topMargin, OutOfBoundsPolicy outOfBoundsPolicy = OutOfBoundsPolicy::Reject);
+ bool setBottomMargin(qreal bottomMargin, OutOfBoundsPolicy outOfBoundsPolicy = OutOfBoundsPolicy::Reject);
QMarginsF margins() const;
QMarginsF margins(Unit units) const;
diff --git a/src/gui/painting/qpageranges.cpp b/src/gui/painting/qpageranges.cpp
index 7d62c7c321..99a0009883 100644
--- a/src/gui/painting/qpageranges.cpp
+++ b/src/gui/painting/qpageranges.cpp
@@ -14,7 +14,7 @@ QT_IMPL_METATYPE_EXTERN(QPageRanges)
void QPageRangesPrivate::mergeIntervals()
{
- const int count = intervals.count();
+ const int count = intervals.size();
if (count <= 1)
return;
@@ -162,7 +162,7 @@ QPageRanges QPageRanges::fromString(const QString &ranges)
if (item.contains(u'-')) {
const QStringList rangeItems = item.split(u'-');
- if (rangeItems.count() != 2)
+ if (rangeItems.size() != 2)
return QPageRanges();
bool ok;
@@ -255,7 +255,7 @@ int QPageRanges::firstPage() const
{
if (isEmpty())
return 0;
- return d->intervals.first().from;
+ return d->intervals.constFirst().from;
}
/*!
@@ -266,7 +266,7 @@ int QPageRanges::lastPage() const
{
if (isEmpty())
return 0;
- return d->intervals.last().to;
+ return d->intervals.constLast().to;
}
/*!
diff --git a/src/gui/painting/qpagesize.cpp b/src/gui/painting/qpagesize.cpp
index 3ed633bf99..37f66fe63d 100644
--- a/src/gui/painting/qpagesize.cpp
+++ b/src/gui/painting/qpagesize.cpp
@@ -543,7 +543,7 @@ static QSize qt_convertPointsToPixels(const QSize &size, int resolution)
if (!size.isValid() || resolution <= 0)
return QSize();
const qreal multiplier = qt_pixelMultiplier(resolution);
- return QSize(qRound(size.width() / multiplier), qRound(size.height() / multiplier));
+ return QSize(qFloor(size.width() / multiplier), qFloor(size.height() / multiplier));
}
static QSizeF qt_convertPointsToUnits(const QSize &size, QPageSize::Unit units)
@@ -869,7 +869,7 @@ QSizeF QPageSizePrivate::size(QPageSize::Unit units) const
QSize QPageSizePrivate::sizePixels(int resolution) const
{
- return qt_convertPointsToPixels(m_pointSize, resolution);;
+ return qt_convertPointsToPixels(m_pointSize, resolution);
}
diff --git a/src/gui/painting/qpaintengine.cpp b/src/gui/painting/qpaintengine.cpp
index 839be3bd41..aface3744d 100644
--- a/src/gui/painting/qpaintengine.cpp
+++ b/src/gui/painting/qpaintengine.cpp
@@ -481,6 +481,8 @@ void QPaintEngine::drawEllipse(const QRectF &rect)
}
/*!
+ \overload
+
The default implementation of this function calls the floating
point version of this function
*/
diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
index 52ee689188..f62373d4ef 100644
--- a/src/gui/painting/qpaintengine_raster.cpp
+++ b/src/gui/painting/qpaintengine_raster.cpp
@@ -98,7 +98,6 @@ public:
Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
-#define qreal_to_fixed_26_6(f) (int(f * 64))
#define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
#define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
@@ -144,9 +143,9 @@ bool QRasterPaintEngine::clearTypeFontsEnabled()
/********************************************************************************
* Span functions
*/
-static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
-static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
-static void qt_span_clip(int count, const QSpan *spans, void *userData);
+static void qt_span_fill_clipRect(int count, const QT_FT_Span *spans, void *userData);
+static void qt_span_fill_clipped(int count, const QT_FT_Span *spans, void *userData);
+static void qt_span_clip(int count, const QT_FT_Span *spans, void *userData);
struct ClipData
{
@@ -241,8 +240,7 @@ QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
/*!
\class QRasterPaintEngine
- \preliminary
- \ingroup qws
+ \internal
\inmodule QtGui
\since 4.2
@@ -277,15 +275,6 @@ QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
*/
/*!
- \typedef QSpan
- \relates QRasterPaintEngine
-
- A struct equivalent to QT_FT_Span, containing a position (x,
- y), the span's length in pixels and its color/coverage (a value
- ranging from 0 to 255).
-*/
-
-/*!
\since 4.5
Creates a raster based paint engine for operating on the given
@@ -416,13 +405,7 @@ bool QRasterPaintEngine::begin(QPaintDevice *device)
QRasterPaintEngineState *s = state();
ensureOutlineMapper();
- d->outlineMapper->m_clip_rect = d->deviceRect;
-
- if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
- d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
- if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
- d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
-
+ d->outlineMapper->setClipRect(d->deviceRect);
d->rasterizer->setClipRect(d->deviceRect);
s->penData.init(d->rasterBuffer.data(), this);
@@ -516,6 +499,7 @@ QRasterPaintEngineState::QRasterPaintEngineState()
txscale = 1.;
+ flag_bits = 0;
flags.fast_pen = true;
flags.non_complex_pen = false;
flags.antialiased = false;
@@ -2277,7 +2261,7 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe
|| d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source))
{
RotationType rotationType = qRotationType(s->matrix);
- Q_ASSUME(d->rasterBuffer->format < QImage::NImageFormats);
+ Q_ASSERT(d->rasterBuffer->format < QImage::NImageFormats);
const QPixelLayout::BPP plBpp = qPixelLayouts[d->rasterBuffer->format].bpp;
if (rotationType != NoRotation && qMemRotateFunctions[plBpp][rotationType] && img.rect().contains(sr.toAlignedRect())) {
@@ -2358,9 +2342,16 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe
}
SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
if (func && (!clip || clip->hasRectClip)) {
+ QRectF tr = qt_mapRect_non_normalizing(r, s->matrix);
+ if (!s->flags.antialiased) {
+ tr.setX(qRound(tr.x()));
+ tr.setY(qRound(tr.y()));
+ tr.setWidth(qRound(tr.width()));
+ tr.setHeight(qRound(tr.height()));
+ }
func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
img.bits(), img.bytesPerLine(), img.height(),
- qt_mapRect_non_normalizing(r, s->matrix), sr,
+ tr, sr,
!clip ? d->deviceRect : clip->clipRect,
s->intOpacity);
return;
@@ -2670,7 +2661,7 @@ void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx
return;
const int NSPANS = 512;
- QSpan spans[NSPANS];
+ QT_FT_Span spans[NSPANS];
int current = 0;
const int x1 = x0 + w;
@@ -3008,8 +2999,14 @@ QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
}
-static QPair<int, int> visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
- glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
+struct VisibleGlyphRange
+{
+ int begin;
+ int end;
+};
+
+static VisibleGlyphRange visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
+ glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
{
QFixed clipLeft = QFixed::fromReal(clip.left() - 1);
QFixed clipRight = QFixed::fromReal(clip.right() + 1);
@@ -3038,7 +3035,7 @@ static QPair<int, int> visibleGlyphRange(const QRectF &clip, QFontEngine *fontEn
break;
--last;
}
- return QPair<int, int>(first, last + 1);
+ return {first, last + 1};
}
/*!
@@ -3064,13 +3061,13 @@ void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
if (!invertible)
return;
- QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
- textItem->fontEngine(), textItem->glyphs,
- textItem->glyphPositions, textItem->numGlyphs);
+ const auto range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
+ textItem->fontEngine(), textItem->glyphs,
+ textItem->glyphPositions, textItem->numGlyphs);
QStaticTextItem copy = *textItem;
- copy.glyphs += range.first;
- copy.glyphPositions += range.first;
- copy.numGlyphs = range.second - range.first;
+ copy.glyphs += range.begin;
+ copy.glyphPositions += range.begin;
+ copy.numGlyphs = range.end - range.begin;
QPaintEngineEx::drawStaticTextItem(&copy);
} else {
QPaintEngineEx::drawStaticTextItem(textItem);
@@ -3119,20 +3116,20 @@ void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte
ti.fontEngine->getGlyphPositions(ti.glyphs, QTransform::fromTranslate(p.x(), p.y()),
ti.flags, glyphs, positions);
- QPair<int, int> range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
- ti.fontEngine, glyphs.data(), positions.data(),
- glyphs.size());
+ const auto range = visibleGlyphRange(invMat.mapRect(clipBoundingRect()),
+ ti.fontEngine, glyphs.data(), positions.data(),
+ glyphs.size());
- if (range.first >= range.second)
+ if (range.begin >= range.end)
return;
QStaticTextItem staticTextItem;
staticTextItem.color = s->pen.color();
staticTextItem.font = s->font;
staticTextItem.setFontEngine(ti.fontEngine);
- staticTextItem.numGlyphs = range.second - range.first;
- staticTextItem.glyphs = glyphs.data() + range.first;
- staticTextItem.glyphPositions = positions.data() + range.first;
+ staticTextItem.numGlyphs = range.end - range.begin;
+ staticTextItem.glyphs = glyphs.data() + range.begin;
+ staticTextItem.glyphPositions = positions.data() + range.begin;
QPaintEngineEx::drawStaticTextItem(&staticTextItem);
} else {
QPaintEngineEx::drawTextItem(p, ti);
@@ -3400,16 +3397,18 @@ void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSp
// Boundaries
int w = image.width();
int h = image.height();
- int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
- int ymin = qMax(qRound(pos.y()), 0);
- int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
- int xmin = qMax(qRound(pos.x()), 0);
+ int px = qRound(pos.x());
+ int py = qRound(pos.y());
+ int ymax = qMin(py + h, d->rasterBuffer->height());
+ int ymin = qMax(py, 0);
+ int xmax = qMin(px + w, d->rasterBuffer->width());
+ int xmin = qMax(px, 0);
- int x_offset = xmin - qRound(pos.x());
+ int x_offset = xmin - px;
QImage::Format format = image.format();
for (int y = ymin; y < ymax; ++y) {
- const uchar *src = image.scanLine(y - qRound(pos.y()));
+ const uchar *src = image.scanLine(y - py);
if (format == QImage::Format_MonoLSB) {
for (int x = 0; x < xmax - xmin; ++x) {
int src_x = x + x_offset;
@@ -3714,15 +3713,9 @@ bool QRasterPaintEnginePrivate::canUseImageBlitting(QPainter::CompositionMode mo
QImage::Format dFormat = rasterBuffer->format;
QImage::Format sFormat = image.format();
- // Formats must match or source format must be a subset of destination format
- if (dFormat != sFormat && image.pixelFormat().alphaUsage() == QPixelFormat::IgnoresAlpha) {
- if ((sFormat == QImage::Format_RGB32 && dFormat == QImage::Format_ARGB32)
- || (sFormat == QImage::Format_RGBX8888 && dFormat == QImage::Format_RGBA8888)
- || (sFormat == QImage::Format_RGBX64 && dFormat == QImage::Format_RGBA64))
- sFormat = dFormat;
- else
- sFormat = qt_maybeAlphaVersionWithSameDepth(sFormat); // this returns premul formats
- }
+ // Formats must match or source format must be an opaque version of destination format
+ if (dFormat != sFormat && image.pixelFormat().alphaUsage() == QPixelFormat::IgnoresAlpha)
+ dFormat = qt_maybeDataCompatibleOpaqueVersion(dFormat);
return (dFormat == sFormat);
}
@@ -3809,7 +3802,7 @@ void QClipData::initialize()
return;
if (!m_clipLines)
- m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
+ m_clipLines = (ClipLine *)calloc(clipSpanHeight, sizeof(ClipLine));
Q_CHECK_PTR(m_clipLines);
QT_TRY {
@@ -3821,7 +3814,7 @@ void QClipData::initialize()
const int numRects = clipRegion.rectCount();
const int maxSpans = (ymax - ymin) * numRects;
allocated = qMax(allocated, maxSpans);
- m_spans = (QSpan *)malloc(allocated * sizeof(QSpan));
+ m_spans = (QT_FT_Span *)malloc(allocated * sizeof(QT_FT_Span));
Q_CHECK_PTR(m_spans);
int y = 0;
@@ -3847,7 +3840,7 @@ void QClipData::initialize()
for (int r = firstInBand; r <= lastInBand; ++r) {
const QRect &currRect = rects[r];
- QSpan *span = m_spans + count;
+ QT_FT_Span *span = m_spans + count;
span->x = currRect.x();
span->len = currRect.width();
span->y = y;
@@ -3871,7 +3864,7 @@ void QClipData::initialize()
return;
}
- m_spans = (QSpan *)malloc(allocated * sizeof(QSpan));
+ m_spans = (QT_FT_Span *)malloc(allocated * sizeof(QT_FT_Span));
Q_CHECK_PTR(m_spans);
if (hasRectClip) {
@@ -3884,7 +3877,7 @@ void QClipData::initialize()
const int len = clipRect.width();
while (y < ymax) {
- QSpan *span = m_spans + count;
+ QT_FT_Span *span = m_spans + count;
span->x = xmin;
span->len = len;
span->y = y;
@@ -4023,16 +4016,16 @@ void QClipData::setClipRegion(const QRegion &region)
\internal
spans must be sorted on y
*/
-static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
- const QSpan *spans, const QSpan *end,
- QSpan **outSpans, int available)
+static const QT_FT_Span *qt_intersect_spans(const QClipData *clip, int *currentClip,
+ const QT_FT_Span *spans, const QT_FT_Span *end,
+ QT_FT_Span **outSpans, int available)
{
const_cast<QClipData *>(clip)->initialize();
- QSpan *out = *outSpans;
+ QT_FT_Span *out = *outSpans;
- const QSpan *clipSpans = clip->m_spans + *currentClip;
- const QSpan *clipEnd = clip->m_spans + clip->count;
+ const QT_FT_Span *clipSpans = clip->m_spans + *currentClip;
+ const QT_FT_Span *clipEnd = clip->m_spans + clip->count;
while (available && spans < end ) {
if (clipSpans >= clipEnd) {
@@ -4086,7 +4079,7 @@ static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
return spans;
}
-static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
+static void qt_span_fill_clipped(int spanCount, const QT_FT_Span *spans, void *userData)
{
// qDebug() << "qt_span_fill_clipped" << spanCount;
QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
@@ -4094,11 +4087,11 @@ static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userDa
Q_ASSERT(fillData->blend && fillData->unclipped_blend);
const int NSPANS = 512;
- QSpan cspans[NSPANS];
+ QT_FT_Span cspans[NSPANS];
int currentClip = 0;
- const QSpan *end = spans + spanCount;
+ const QT_FT_Span *end = spans + spanCount;
while (spans < end) {
- QSpan *clipped = cspans;
+ QT_FT_Span *clipped = cspans;
spans = qt_intersect_spans(fillData->clip, &currentClip, spans, end, &clipped, NSPANS);
// qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans
// << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
@@ -4150,7 +4143,7 @@ static int qt_intersect_spans(QT_FT_Span *&spans, int numSpans,
}
-static void qt_span_fill_clipRect(int count, const QSpan *spans,
+static void qt_span_fill_clipRect(int count, const QT_FT_Span *spans,
void *userData)
{
QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
@@ -4159,7 +4152,7 @@ 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);
+ QT_FT_Span *s = const_cast<QT_FT_Span *>(spans);
// hw: check if this const_cast<> is safe!!!
count = qt_intersect_spans(s, count,
fillData->clip->clipRect);
@@ -4167,7 +4160,7 @@ static void qt_span_fill_clipRect(int count, const QSpan *spans,
fillData->unclipped_blend(count, s, fillData);
}
-static void qt_span_clip(int count, const QSpan *spans, void *userData)
+static void qt_span_clip(int count, const QT_FT_Span *spans, void *userData)
{
ClipData *clipData = reinterpret_cast<ClipData *>(userData);
@@ -4184,14 +4177,14 @@ static void qt_span_clip(int count, const QSpan *spans, void *userData)
newClip->initialize();
int currentClip = 0;
- const QSpan *end = spans + count;
+ const QT_FT_Span *end = spans + count;
while (spans < end) {
- QSpan *newspans = newClip->m_spans + newClip->count;
+ QT_FT_Span *newspans = newClip->m_spans + newClip->count;
spans = qt_intersect_spans(clipData->oldClip, &currentClip, spans, end,
&newspans, newClip->allocated - newClip->count);
newClip->count = newspans - newClip->m_spans;
if (spans < end) {
- newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
+ newClip->m_spans = q_check_ptr((QT_FT_Span *)realloc(newClip->m_spans, newClip->allocated * 2 * sizeof(QT_FT_Span)));
newClip->allocated *= 2;
}
}
@@ -4209,7 +4202,7 @@ static void qt_span_clip(int count, const QSpan *spans, void *userData)
class QGradientCache
{
public:
- struct CacheInfo : QSpanData::Pinnable
+ struct CacheInfo
{
inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
stops(std::move(s)), opacity(op), interpolationMode(mode) {}
@@ -4220,9 +4213,9 @@ public:
QGradient::InterpolationMode interpolationMode;
};
- typedef QMultiHash<quint64, QSharedPointer<const CacheInfo>> QGradientColorTableHash;
+ using QGradientColorTableHash = QMultiHash<quint64, std::shared_ptr<const CacheInfo>>;
- inline QSharedPointer<const CacheInfo> getBuffer(const QGradient &gradient, int opacity) {
+ std::shared_ptr<const CacheInfo> getBuffer(const QGradient &gradient, int opacity) {
quint64 hash_val = 0;
const QGradientStops stops = gradient.stops();
@@ -4252,16 +4245,16 @@ protected:
inline void generateGradientColorTable(const QGradient& g,
QRgba64 *colorTable,
int size, int opacity) const;
- QSharedPointer<const CacheInfo> addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
+ std::shared_ptr<const CacheInfo> addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
if (cache.size() == maxCacheSize()) {
// may remove more than 1, but OK
cache.erase(std::next(cache.begin(), QRandomGenerator::global()->bounded(maxCacheSize())));
}
- auto cache_entry = QSharedPointer<CacheInfo>::create(gradient.stops(), opacity, gradient.interpolationMode());
+ auto cache_entry = std::make_shared<CacheInfo>(gradient.stops(), opacity, gradient.interpolationMode());
generateGradientColorTable(gradient, cache_entry->buffer64, paletteSize(), opacity);
for (int i = 0; i < GRADIENT_STOPTABLE_SIZE; ++i)
cache_entry->buffer32[i] = cache_entry->buffer64[i].toArgb32();
- return cache.insert(hash_val, cache_entry).value();
+ return cache.insert(hash_val, std::move(cache_entry)).value();
}
QGradientColorTableHash cache;
@@ -4271,7 +4264,7 @@ protected:
void QGradientCache::generateGradientColorTable(const QGradient& gradient, QRgba64 *colorTable, int size, int opacity) const
{
const QGradientStops stops = gradient.stops();
- int stopCount = stops.count();
+ int stopCount = stops.size();
Q_ASSERT(stopCount > 0);
bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
@@ -4872,7 +4865,7 @@ void dumpClip(int width, int height, const QClipData *clip)
((QClipData *) clip)->spans(); // Force allocation of the spans structure...
for (int i = 0; i < clip->count; ++i) {
- const QSpan *span = ((QClipData *) clip)->spans() + i;
+ const QT_FT_Span *span = ((QClipData *) clip)->spans() + i;
for (int j = 0; j < span->len; ++j)
clipImg.setPixel(span->x + j, span->y, 0xffffff00);
x0 = qMin(x0, int(span->x));
diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h
index c6d723e0c3..ab0048af52 100644
--- a/src/gui/painting/qpaintengine_raster_p.h
+++ b/src/gui/painting/qpaintengine_raster_p.h
@@ -315,7 +315,7 @@ public:
int clipSpanHeight;
struct ClipLine {
int count;
- QSpan *spans;
+ QT_FT_Span *spans;
} *m_clipLines;
void initialize();
@@ -326,7 +326,7 @@ public:
return m_clipLines;
}
- inline QSpan *spans() {
+ inline QT_FT_Span *spans() {
if (!m_spans)
initialize();
return m_spans;
@@ -334,7 +334,7 @@ public:
int allocated;
int count;
- QSpan *m_spans;
+ QT_FT_Span *m_spans;
int xmin, xmax, ymin, ymax;
QRect clipRect;
@@ -345,7 +345,7 @@ public:
uint hasRegionClip : 1;
void appendSpan(int x, int length, int y, int coverage);
- void appendSpans(const QSpan *s, int num);
+ void appendSpans(const QT_FT_Span *s, int num);
// ### Should optimize and actually kill the QSpans if the rect is
// ### a subset of The current region. Thus the "fast" clipspan
@@ -361,7 +361,7 @@ inline void QClipData::appendSpan(int x, int length, int y, int coverage)
if (count == allocated) {
allocated *= 2;
- m_spans = (QSpan *)realloc(m_spans, allocated*sizeof(QSpan));
+ m_spans = (QT_FT_Span *)realloc(m_spans, allocated*sizeof(QT_FT_Span));
}
m_spans[count].x = x;
m_spans[count].len = length;
@@ -370,7 +370,7 @@ inline void QClipData::appendSpan(int x, int length, int y, int coverage)
++count;
}
-inline void QClipData::appendSpans(const QSpan *s, int num)
+inline void QClipData::appendSpans(const QT_FT_Span *s, int num)
{
Q_ASSERT(m_spans);
@@ -378,9 +378,9 @@ inline void QClipData::appendSpans(const QSpan *s, int num)
do {
allocated *= 2;
} while (count + num > allocated);
- m_spans = (QSpan *)realloc(m_spans, allocated*sizeof(QSpan));
+ m_spans = (QT_FT_Span *)realloc(m_spans, allocated*sizeof(QT_FT_Span));
}
- memcpy(m_spans+count, s, num*sizeof(QSpan));
+ memcpy(m_spans+count, s, num*sizeof(QT_FT_Span));
count += num;
}
diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp
index 7f1c7e356d..9468876c23 100644
--- a/src/gui/painting/qpaintengineex.cpp
+++ b/src/gui/painting/qpaintengineex.cpp
@@ -902,7 +902,7 @@ void QPaintEngineEx::drawPoints(const QPoint *points, int pointCount)
void QPaintEngineEx::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
{
- Q_ASSUME(pointCount >= 2);
+ Q_ASSERT(pointCount >= 2);
QVectorPath path((const qreal *) points, pointCount, nullptr, QVectorPath::polygonFlags(mode));
if (mode == PolylineMode)
@@ -913,7 +913,7 @@ void QPaintEngineEx::drawPolygon(const QPointF *points, int pointCount, PolygonD
void QPaintEngineEx::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
{
- Q_ASSUME(pointCount >= 2);
+ Q_ASSERT(pointCount >= 2);
int count = pointCount<<1;
QVarLengthArray<qreal> pts(count);
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
index 6995f7d180..f4b9741eed 100644
--- a/src/gui/painting/qpainter.cpp
+++ b/src/gui/painting/qpainter.cpp
@@ -39,6 +39,7 @@
#include <private/qhexstring_p.h>
#include <private/qguiapplication_p.h>
#include <private/qrawfont_p.h>
+#include <private/qfont_p.h>
QT_BEGIN_NAMESPACE
@@ -220,7 +221,7 @@ qreal QPainterPrivate::effectiveDevicePixelRatio() const
if (device->devType() == QInternal::Printer)
return qreal(1);
- return qMax(qreal(1), device->devicePixelRatio());
+ return device->devicePixelRatio();
}
QTransform QPainterPrivate::hidpiScaleTransform() const
@@ -380,7 +381,7 @@ void QPainterPrivate::draw_helper(const QPainterPath &originalPath, DrawOperatio
if (q->hasClipping()) {
bool hasPerspectiveTransform = false;
- for (const QPainterClipInfo &info : qAsConst(state->clipInfo)) {
+ for (const QPainterClipInfo &info : std::as_const(state->clipInfo)) {
if (info.matrix.type() == QTransform::TxProject) {
hasPerspectiveTransform = true;
break;
@@ -1124,24 +1125,22 @@ void QPainterPrivate::updateState(QPainterState *newState)
The QPainter class also provides a means of controlling the
rendering quality through its RenderHint enum and the support for
floating point precision: All the functions for drawing primitives
- has a floating point version. These are often used in combination
+ have floating point versions.
+
+ \snippet code/src_gui_painting_qpainter.cpp floatBased
+
+ These are often used in combination
with the \l {RenderHint}{QPainter::Antialiasing} render hint.
+ \snippet code/src_gui_painting_qpainter.cpp renderHint
+
\table 100%
\row
+ \li Comparing concentric circles with int and float, and with or without
+ anti-aliased rendering. Using the floating point precision versions
+ produces evenly spaced rings. Anti-aliased rendering results in
+ smooth circles.
\li \inlineimage qpainter-concentriccircles.png
- \li
- \b {Concentric Circles Example}
-
- The \l {painting/concentriccircles}{Concentric Circles} example
- shows the improved rendering quality that can be obtained using
- floating point precision and anti-aliasing when drawing custom
- widgets.
-
- The application's main window displays several widgets which are
- drawn using the various combinations of precision and
- anti-aliasing.
-
\endtable
The RenderHint enum specifies flags to QPainter that may or may
@@ -1419,7 +1418,7 @@ void QPainterPrivate::updateState(QPainterState *newState)
This value was added in Qt 6.4.
\sa renderHints(), setRenderHint(), {QPainter#Rendering
- Quality}{Rendering Quality}, {Concentric Circles Example}
+ Quality}{Rendering Quality}
*/
@@ -1615,9 +1614,8 @@ void QPainter::restore()
tmp->clipPath = QPainterPath();
d->engine->updateState(*tmp);
// replay the list of clip states,
- for (const QPainterClipInfo &info : qAsConst(d->state->clipInfo)) {
+ for (const QPainterClipInfo &info : std::as_const(d->state->clipInfo)) {
tmp->matrix = info.matrix;
- tmp->matrix *= d->state->redirectionMatrix;
tmp->clipOperation = info.operation;
if (info.clipType == QPainterClipInfo::RectClip) {
tmp->dirtyFlags = QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyTransform;
@@ -1767,9 +1765,12 @@ bool QPainter::begin(QPaintDevice *pd)
qWarning("QPainter::begin: Cannot paint on a null image");
qt_cleanup_painter_state(d);
return false;
- } else if (img->format() == QImage::Format_Indexed8) {
- // Painting on indexed8 images is not supported.
- qWarning("QPainter::begin: Cannot paint on an image with the QImage::Format_Indexed8 format");
+ } else if (img->format() == QImage::Format_Indexed8 ||
+ img->format() == QImage::Format_CMYK8888) {
+ // Painting on these formats is not supported.
+ qWarning() << "QPainter::begin: Cannot paint on an image with the"
+ << img->format()
+ << "format";
qt_cleanup_painter_state(d);
return false;
}
@@ -1824,7 +1825,7 @@ bool QPainter::begin(QPaintDevice *pd)
Q_ASSERT(d->engine->isActive());
- if (!d->state->redirectionMatrix.isIdentity() || d->effectiveDevicePixelRatio() > 1)
+ if (!d->state->redirectionMatrix.isIdentity() || !qFuzzyCompare(d->effectiveDevicePixelRatio(), qreal(1.0)))
d->updateMatrix();
Q_ASSERT(d->engine->isActive());
@@ -2477,7 +2478,7 @@ QRegion QPainter::clipRegion() const
const_cast<QPainter *>(this)->d_ptr->updateInvMatrix();
// ### Falcon: Use QPainterPath
- for (const QPainterClipInfo &info : qAsConst(d->state->clipInfo)) {
+ for (const QPainterClipInfo &info : std::as_const(d->state->clipInfo)) {
switch (info.clipType) {
case QPainterClipInfo::RegionClip: {
@@ -2644,7 +2645,7 @@ QRectF QPainter::clipBoundingRect() const
// fast.
QRectF bounds;
bool first = true;
- for (const QPainterClipInfo &info : qAsConst(d->state->clipInfo)) {
+ for (const QPainterClipInfo &info : std::as_const(d->state->clipInfo)) {
QRectF r;
if (info.clipType == QPainterClipInfo::RectClip)
@@ -3901,8 +3902,10 @@ void QPainter::drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius,
#endif
Q_D(QPainter);
- if (!d->engine)
+ if (!d->engine) {
+ qWarning("QPainter::drawRoundedRect: Painter not active");
return;
+ }
if (xRadius <= 0 || yRadius <= 0) { // draw normal rectangle
drawRect(rect);
@@ -3963,8 +3966,10 @@ void QPainter::drawEllipse(const QRectF &r)
#endif
Q_D(QPainter);
- if (!d->engine)
+ if (!d->engine) {
+ qWarning("QPainter::drawEllipse: Painter not active");
return;
+ }
QRectF rect(r.normalized());
@@ -4004,8 +4009,10 @@ void QPainter::drawEllipse(const QRect &r)
#endif
Q_D(QPainter);
- if (!d->engine)
+ if (!d->engine) {
+ qWarning("QPainter::drawEllipse: Painter not active");
return;
+ }
QRect rect(r.normalized());
@@ -4091,8 +4098,10 @@ void QPainter::drawArc(const QRectF &r, int a, int alen)
#endif
Q_D(QPainter);
- if (!d->engine)
+ if (!d->engine) {
+ qWarning("QPainter::drawArc: Painter not active");
return;
+ }
QRectF rect = r.normalized();
@@ -4153,8 +4162,10 @@ void QPainter::drawPie(const QRectF &r, int a, int alen)
#endif
Q_D(QPainter);
- if (!d->engine)
+ if (!d->engine) {
+ qWarning("QPainter::drawPie: Painter not active");
return;
+ }
if (a > (360*16)) {
a = a % (360*16);
@@ -4222,8 +4233,10 @@ void QPainter::drawChord(const QRectF &r, int a, int alen)
#endif
Q_D(QPainter);
- if (!d->engine)
+ if (!d->engine) {
+ qWarning("QPainter::drawChord: Painter not active");
return;
+ }
QRectF rect = r.normalized();
@@ -5448,9 +5461,13 @@ void QPainter::drawStaticText(const QPointF &topLeftPosition, const QStaticText
QStaticTextPrivate *staticText_d =
const_cast<QStaticTextPrivate *>(QStaticTextPrivate::get(&staticText));
- if (font() != staticText_d->font) {
+ QFontPrivate *fp = QFontPrivate::get(font());
+ QFontPrivate *stfp = QFontPrivate::get(staticText_d->font);
+ if (font() != staticText_d->font || fp == nullptr || stfp == nullptr || fp->dpi != stfp->dpi) {
staticText_d->font = font();
staticText_d->needsRelayout = true;
+ } else if (stfp->engineData == nullptr || stfp->engineData->fontCacheId != QFontCache::instance()->id()) {
+ staticText_d->needsRelayout = true;
}
QFontEngine *fe = staticText_d->font.d->engineForScript(QChar::Script_Common);
@@ -5588,7 +5605,7 @@ void QPainter::drawText(const QPointF &p, const QString &str, int tf, int justif
}
engine.itemize();
QScriptLine line;
- line.length = str.length();
+ line.length = str.size();
engine.shapeLine(line);
int nItems = engine.layoutData->items.size();
@@ -5643,7 +5660,7 @@ void QPainter::drawText(const QRect &r, int flags, const QString &str, QRect *br
Q_D(QPainter);
- if (!d->engine || str.length() == 0 || pen().style() == Qt::NoPen)
+ if (!d->engine || str.size() == 0 || pen().style() == Qt::NoPen)
return;
if (!d->extended)
@@ -5730,7 +5747,7 @@ void QPainter::drawText(const QRectF &r, int flags, const QString &str, QRectF *
Q_D(QPainter);
- if (!d->engine || str.length() == 0 || pen().style() == Qt::NoPen)
+ if (!d->engine || str.size() == 0 || pen().style() == Qt::NoPen)
return;
if (!d->extended)
@@ -5849,7 +5866,7 @@ void QPainter::drawText(const QRectF &r, const QString &text, const QTextOption
Q_D(QPainter);
- if (!d->engine || text.length() == 0 || pen().style() == Qt::NoPen)
+ if (!d->engine || text.size() == 0 || pen().style() == Qt::NoPen)
return;
if (!d->extended)
@@ -6340,7 +6357,7 @@ QRectF QPainter::boundingRect(const QRectF &r, const QString &text, const QTextO
{
Q_D(QPainter);
- if (!d->engine || text.length() == 0)
+ if (!d->engine || text.size() == 0)
return QRectF(r.x(),r.y(), 0,0);
QRectF br;
@@ -6505,8 +6522,10 @@ void QPainter::drawPicture(const QPointF &p, const QPicture &picture)
{
Q_D(QPainter);
- if (!d->engine)
+ if (!d->engine) {
+ qWarning("QPainter::drawPicture: Painter not active");
return;
+ }
if (!d->extended)
d->updateState(d->state);
@@ -6617,8 +6636,10 @@ void QPainter::fillRect(const QRectF &r, const QBrush &brush)
{
Q_D(QPainter);
- if (!d->engine)
+ if (!d->engine) {
+ qWarning("QPainter::fillRect: Painter not active");
return;
+ }
if (d->extended && !needsEmulation(brush)) {
d->extended->fillRect(r, brush);
@@ -6652,8 +6673,10 @@ void QPainter::fillRect(const QRect &r, const QBrush &brush)
{
Q_D(QPainter);
- if (!d->engine)
+ if (!d->engine) {
+ qWarning("QPainter::fillRect: Painter not active");
return;
+ }
if (d->extended && !needsEmulation(brush)) {
d->extended->fillRect(r, brush);
@@ -6690,8 +6713,10 @@ void QPainter::fillRect(const QRect &r, const QColor &color)
{
Q_D(QPainter);
- if (!d->engine)
+ if (!d->engine) {
+ qWarning("QPainter::fillRect: Painter not active");
return;
+ }
if (d->extended) {
d->extended->fillRect(r, color);
@@ -7130,7 +7155,7 @@ start_lengthVariant:
// compatible behaviour to the old implementation. Replace
// tabs by spaces
int old_offset = offset;
- for (; offset < text.length(); offset++) {
+ for (; offset < text.size(); offset++) {
QChar chr = text.at(offset);
if (chr == u'\r' || (singleline && chr == u'\n')) {
text[offset] = u' ';
diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp
index 851df3e7ae..8d23d167b0 100644
--- a/src/gui/painting/qpainterpath.cpp
+++ b/src/gui/painting/qpainterpath.cpp
@@ -517,10 +517,8 @@ QPainterPath::QPainterPath(const QPainterPath &other) = default;
*/
QPainterPath::QPainterPath(const QPointF &startPoint)
- : d_ptr(new QPainterPathPrivate)
+ : d_ptr(new QPainterPathPrivate(startPoint))
{
- Element e = { startPoint.x(), startPoint.y(), MoveToElement };
- d_func()->elements << e;
}
void QPainterPath::detach()
@@ -1135,6 +1133,9 @@ void QPainterPath::addEllipse(const QRectF &boundingRect)
that the left end of the text's baseline lies at the specified \a
point.
+ Some fonts may yield overlapping subpaths and will require the
+ \c Qt::WindingFill fill rule for correct rendering.
+
\table 100%
\row
\li \inlineimage qpainterpath-addtext.png
@@ -1143,7 +1144,7 @@ void QPainterPath::addEllipse(const QRectF &boundingRect)
\endtable
\sa QPainter::drawText(), {QPainterPath#Composing a
- QPainterPath}{Composing a QPainterPath}
+ QPainterPath}{Composing a QPainterPath}, setFillRule()
*/
void QPainterPath::addText(const QPointF &point, const QFont &f, const QString &text)
{
@@ -1488,7 +1489,7 @@ QRectF QPainterPath::controlPointRect() const
bool QPainterPath::isEmpty() const
{
- return !d_ptr || (d_ptr->elements.size() == 1 && d_ptr->elements.first().type == MoveToElement);
+ return !d_ptr || (d_ptr->elements.size() == 1 && d_ptr->elements.constFirst().type == MoveToElement);
}
/*!
@@ -2441,14 +2442,14 @@ QDataStream &operator>>(QDataStream &s, QPainterPath &p)
int size;
s >> size;
- if (size == 0)
+ if (size == 0) {
+ p = {};
return s;
+ }
p.ensureData(); // in case if p.d_func() == 0
- if (p.d_func()->elements.size() == 1) {
- Q_ASSERT(p.d_func()->elements.at(0).type == QPainterPath::MoveToElement);
- p.d_func()->elements.clear();
- }
+ p.detach();
+ p.d_func()->elements.clear();
for (int i=0; i<size; ++i) {
int type;
double x, y;
@@ -2471,9 +2472,7 @@ QDataStream &operator>>(QDataStream &s, QPainterPath &p)
s >> fillRule;
Q_ASSERT(fillRule == Qt::OddEvenFill || fillRule == Qt::WindingFill);
p.d_func()->fillRule = Qt::FillRule(fillRule);
- p.d_func()->dirtyBounds = true;
- p.d_func()->dirtyControlBounds = true;
- if (errorDetected)
+ if (errorDetected || p.d_func()->elements.isEmpty())
p = QPainterPath(); // Better than to return path with possibly corrupt datastructure, which would likely cause crash
return s;
}
diff --git a/src/gui/painting/qpainterpath_p.h b/src/gui/painting/qpainterpath_p.h
index 55164bc347..a07b6cca37 100644
--- a/src/gui/painting/qpainterpath_p.h
+++ b/src/gui/painting/qpainterpath_p.h
@@ -119,6 +119,21 @@ public:
{
}
+ QPainterPathPrivate(QPointF startPoint)
+ : QSharedData(),
+ elements{ { startPoint.x(), startPoint.y(), QPainterPath::MoveToElement } },
+ cStart(0),
+ fillRule(Qt::OddEvenFill),
+ bounds(startPoint, QSizeF(0, 0)),
+ controlBounds(startPoint, QSizeF(0, 0)),
+ require_moveTo(false),
+ dirtyBounds(false),
+ dirtyControlBounds(false),
+ convex(false),
+ pathConverter(nullptr)
+ {
+ }
+
QPainterPathPrivate(const QPainterPathPrivate &other) noexcept
: QSharedData(other),
elements(other.elements),
diff --git a/src/gui/painting/qpathclipper.cpp b/src/gui/painting/qpathclipper.cpp
index e0b4e5152c..10a0639b53 100644
--- a/src/gui/painting/qpathclipper.cpp
+++ b/src/gui/painting/qpathclipper.cpp
@@ -63,6 +63,7 @@ struct QIntersection
QPointF pos;
};
+Q_DECLARE_TYPEINFO(QIntersection, Q_PRIMITIVE_TYPE);
class QIntersectionFinder
{
@@ -649,7 +650,8 @@ int QKdPointTree::build(int begin, int end, int depth)
}
}
- qSwap(m_nodes.at(last), m_nodes.at(begin));
+ if (last != begin)
+ qSwap(m_nodes.at(last), m_nodes.at(begin));
if (last > begin)
m_nodes.at(last).left = &m_nodes.at(build(begin, last, depth + 1));
diff --git a/src/gui/painting/qpathclipper_p.h b/src/gui/painting/qpathclipper_p.h
index b6cb65630f..f13b96e0ef 100644
--- a/src/gui/painting/qpathclipper_p.h
+++ b/src/gui/painting/qpathclipper_p.h
@@ -79,6 +79,7 @@ public:
qreal x;
qreal y;
};
+Q_DECLARE_TYPEINFO(QPathVertex, Q_PRIMITIVE_TYPE);
class QPathEdge
{
@@ -122,6 +123,7 @@ public:
private:
int m_next[2][2] = { { -1, -1 }, { -1, -1 } };
};
+Q_DECLARE_TYPEINFO(QPathEdge, Q_PRIMITIVE_TYPE);
class QPathSegments
{
@@ -135,6 +137,7 @@ public:
return t < o.t;
}
};
+ friend class QTypeInfo<Intersection>;
struct Segment {
Segment(int pathId, int vertexA, int vertexB)
@@ -156,6 +159,7 @@ public:
QRectF bounds;
};
+ friend class QTypeInfo<Segment>;
QPathSegments(int reserve);
@@ -187,6 +191,8 @@ private:
int m_pathId;
};
+Q_DECLARE_TYPEINFO(QPathSegments::Intersection, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(QPathSegments::Segment, Q_PRIMITIVE_TYPE);
class Q_AUTOTEST_EXPORT QWingedEdge
{
diff --git a/src/gui/painting/qpathsimplifier.cpp b/src/gui/painting/qpathsimplifier.cpp
index af702435c5..969308f0bb 100644
--- a/src/gui/painting/qpathsimplifier.cpp
+++ b/src/gui/painting/qpathsimplifier.cpp
@@ -8,7 +8,9 @@
#include <QtCore/qpoint.h>
#include <QtCore/qalgorithms.h>
-#include <private/qopengl_p.h>
+#if QT_CONFIG(opengl)
+# include <private/qopengl_p.h>
+#endif
#include <private/qrbtree_p.h>
QT_BEGIN_NAMESPACE
@@ -303,6 +305,7 @@ private:
Type type;
Element *element;
};
+ friend class QTypeInfo<Event>;
typedef QRBTree<Element *>::Node RBNode;
typedef BoundingVolumeHierarchy::Node BVHNode;
@@ -341,6 +344,10 @@ private:
uint m_hints;
};
+} // unnamed namespace
+
+Q_DECLARE_TYPEINFO(PathSimplifier::Event, Q_PRIMITIVE_TYPE);
+
inline PathSimplifier::BoundingVolumeHierarchy::BoundingVolumeHierarchy()
: root(nullptr)
, nodeBlock(nullptr)
@@ -1614,9 +1621,6 @@ void PathSimplifier::sortEvents(Event *events, int count)
}
}
-} // end anonymous namespace
-
-
void qSimplifyPath(const QVectorPath &path, QDataBuffer<QPoint> &vertices,
QDataBuffer<quint32> &indices, const QTransform &matrix)
{
@@ -1631,3 +1635,5 @@ void qSimplifyPath(const QPainterPath &path, QDataBuffer<QPoint> &vertices,
QT_END_NAMESPACE
+
+#undef Q_FIXED_POINT_SCALE
diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp
index cfb9481ab3..38f2a9b803 100644
--- a/src/gui/painting/qpdf.cpp
+++ b/src/gui/painting/qpdf.cpp
@@ -7,6 +7,7 @@
#include "qplatformdefs.h"
+#include <private/qcmyk_p.h>
#include <private/qfont_p.h>
#include <private/qmath_p.h>
#include <private/qpainter_p.h>
@@ -44,7 +45,7 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-inline QPaintEngine::PaintEngineFeatures qt_pdf_decide_features()
+constexpr QPaintEngine::PaintEngineFeatures qt_pdf_decide_features()
{
QPaintEngine::PaintEngineFeatures f = QPaintEngine::AllFeatures;
f &= ~(QPaintEngine::PorterDuff
@@ -99,7 +100,7 @@ static void removeTransparencyFromBrush(QBrush &brush)
const char *qt_real_to_string(qreal val, char *buf) {
const char *ret = buf;
- if (qIsNaN(val)) {
+ if (!qIsFinite(val) || std::abs(val) > std::numeric_limits<quint32>::max()) {
*(buf++) = '0';
*(buf++) = ' ';
*buf = 0;
@@ -110,8 +111,8 @@ const char *qt_real_to_string(qreal val, char *buf) {
*(buf++) = '-';
val = -val;
}
- unsigned int ival = (unsigned int) val;
- qreal frac = val - (qreal)ival;
+ qreal frac = std::modf(val, &val);
+ quint32 ival(val);
int ifrac = (int)(frac * 1000000000);
if (ifrac == 1000000000) {
@@ -270,13 +271,6 @@ namespace QPdf {
dev->open(QIODevice::ReadWrite | QIODevice::Truncate);
}
- void ByteStream::constructor_helper(QByteArray *ba)
- {
- delete dev;
- dev = new QBuffer(ba);
- dev->open(QIODevice::ReadWrite);
- }
-
void ByteStream::prepareBuffer()
{
Q_ASSERT(!dev->isSequential());
@@ -285,16 +279,17 @@ namespace QPdf {
&& size > maxMemorySize()) {
// Switch to file backing.
QTemporaryFile *newFile = new QTemporaryFile;
- newFile->open();
- dev->reset();
- while (!dev->atEnd()) {
- QByteArray buf = dev->read(chunkSize());
- newFile->write(buf);
+ if (newFile->open()) {
+ dev->reset();
+ while (!dev->atEnd()) {
+ QByteArray buf = dev->read(chunkSize());
+ newFile->write(buf);
+ }
+ delete dev;
+ dev = newFile;
+ ba.clear();
+ fileBackingActive = true;
}
- delete dev;
- dev = newFile;
- ba.clear();
- fileBackingActive = true;
}
if (dev->pos() != size) {
dev->seek(size);
@@ -1216,20 +1211,18 @@ void QPdfEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op)
QPainterPath path = d->stroker.matrix.map(p);
//qDebug() << "updateClipPath: " << d->stroker.matrix << p.boundingRect() << path.boundingRect() << op;
- if (op == Qt::NoClip) {
+ switch (op) {
+ case Qt::NoClip:
d->clipEnabled = false;
d->clips.clear();
- } else if (op == Qt::ReplaceClip) {
+ break;
+ case Qt::ReplaceClip:
d->clips.clear();
d->clips.append(path);
- } else if (op == Qt::IntersectClip) {
- d->clips.append(path);
- } else { // UniteClip
- // ask the painter for the current clipping path. that's the easiest solution
- path = painter()->clipPath();
- path = d->stroker.matrix.map(path);
- d->clips.clear();
+ break;
+ case Qt::IntersectClip:
d->clips.append(path);
+ break;
}
}
@@ -1241,17 +1234,8 @@ void QPdfEngine::setPen()
QBrush b = d->pen.brush();
Q_ASSERT(b.style() == Qt::SolidPattern && b.isOpaque());
- QColor rgba = b.color();
- if (d->grayscale) {
- qreal gray = qGray(rgba.rgba())/255.;
- *d->currentPage << gray << gray << gray;
- } else {
- *d->currentPage << rgba.redF()
- << rgba.greenF()
- << rgba.blueF();
- }
+ d->writeColor(QPdfEnginePrivate::ColorDomain::Stroking, b.color());
*d->currentPage << "SCN\n";
-
*d->currentPage << d->pen.widthF() << "w ";
int pdfCapStyle = 0;
@@ -1305,18 +1289,9 @@ void QPdfEngine::setBrush()
if (!patternObject && !specifyColor)
return;
- *d->currentPage << (patternObject ? "/PCSp cs " : "/CSp cs ");
- if (specifyColor) {
- QColor rgba = d->brush.color();
- if (d->grayscale) {
- qreal gray = qGray(rgba.rgba())/255.;
- *d->currentPage << gray << gray << gray;
- } else {
- *d->currentPage << rgba.redF()
- << rgba.greenF()
- << rgba.blueF();
- }
- }
+ const auto domain = patternObject ? QPdfEnginePrivate::ColorDomain::NonStrokingPattern
+ : QPdfEnginePrivate::ColorDomain::NonStroking;
+ d->writeColor(domain, specifyColor ? d->brush.color() : QColor());
if (patternObject)
*d->currentPage << "/Pat" << patternObject;
*d->currentPage << "scn\n";
@@ -1456,9 +1431,9 @@ int QPdfEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const
QPdfEnginePrivate::QPdfEnginePrivate()
: clipEnabled(false), allClipped(false), hasPen(true), hasBrush(false), simplePen(false),
needsTransform(false), pdfVersion(QPdfEngine::Version_1_4),
+ colorModel(QPdfEngine::ColorModel::Auto),
outDevice(nullptr), ownsDevice(false),
embedFonts(true),
- grayscale(false),
m_pageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(10, 10, 10, 10))
{
initResources();
@@ -1507,12 +1482,15 @@ bool QPdfEngine::begin(QPaintDevice *pdev)
d->xrefPositions.clear();
d->pageRoot = 0;
- d->embeddedfilesRoot = 0;
d->namesRoot = 0;
+ d->destsRoot = 0;
+ d->attachmentsRoot = 0;
d->catalog = 0;
d->info = 0;
d->graphicsState = 0;
- d->patternColorSpace = 0;
+ d->patternColorSpaceRGB = 0;
+ d->patternColorSpaceGrayscale = 0;
+ d->patternColorSpaceCMYK = 0;
d->simplePen = false;
d->needsTransform = false;
@@ -1545,6 +1523,7 @@ bool QPdfEngine::end()
d->outDevice = nullptr;
}
+ d->destCache.clear();
d->fileCache.clear();
setActive(false);
@@ -1593,10 +1572,7 @@ void QPdfEnginePrivate::writeHeader()
catalog = addXrefEntry(-1);
pageRoot = requestObject();
- if (!fileCache.isEmpty()) {
- namesRoot = requestObject();
- embeddedfilesRoot = requestObject();
- }
+ namesRoot = requestObject();
// catalog
{
@@ -1604,11 +1580,8 @@ void QPdfEnginePrivate::writeHeader()
QPdf::ByteStream s(&catalog);
s << "<<\n"
<< "/Type /Catalog\n"
- << "/Pages " << pageRoot << "0 R\n";
-
- // Embedded files, if any
- if (!fileCache.isEmpty())
- s << "/Names " << embeddedfilesRoot << "0 R\n";
+ << "/Pages " << pageRoot << "0 R\n"
+ << "/Names " << namesRoot << "0 R\n";
if (pdfVersion == QPdfEngine::Version_A1b || !xmpDocumentMetadata.isEmpty())
s << "/Metadata " << metaDataObj << "0 R\n";
@@ -1622,12 +1595,6 @@ void QPdfEnginePrivate::writeHeader()
write(catalog);
}
- if (!fileCache.isEmpty()) {
- addXrefEntry(embeddedfilesRoot);
- xprintf("<</EmbeddedFiles %d 0 R>>\n"
- "endobj\n", namesRoot);
- }
-
// graphics state
graphicsState = addXrefEntry(-1);
xprintf("<<\n"
@@ -1641,10 +1608,108 @@ void QPdfEnginePrivate::writeHeader()
">>\n"
"endobj\n");
- // color space for pattern
- patternColorSpace = addXrefEntry(-1);
+ // color spaces for pattern
+ patternColorSpaceRGB = addXrefEntry(-1);
xprintf("[/Pattern /DeviceRGB]\n"
"endobj\n");
+ patternColorSpaceGrayscale = addXrefEntry(-1);
+ xprintf("[/Pattern /DeviceGray]\n"
+ "endobj\n");
+ patternColorSpaceCMYK = addXrefEntry(-1);
+ xprintf("[/Pattern /DeviceCMYK]\n"
+ "endobj\n");
+}
+
+QPdfEngine::ColorModel QPdfEnginePrivate::colorModelForColor(const QColor &color) const
+{
+ switch (colorModel) {
+ case QPdfEngine::ColorModel::RGB:
+ case QPdfEngine::ColorModel::Grayscale:
+ case QPdfEngine::ColorModel::CMYK:
+ return colorModel;
+ case QPdfEngine::ColorModel::Auto:
+ switch (color.spec()) {
+ case QColor::Invalid:
+ case QColor::Rgb:
+ case QColor::Hsv:
+ case QColor::Hsl:
+ case QColor::ExtendedRgb:
+ return QPdfEngine::ColorModel::RGB;
+ case QColor::Cmyk:
+ return QPdfEngine::ColorModel::CMYK;
+ }
+
+ break;
+ }
+
+ Q_UNREACHABLE_RETURN(QPdfEngine::ColorModel::RGB);
+}
+
+void QPdfEnginePrivate::writeColor(ColorDomain domain, const QColor &color)
+{
+ // Switch to the right colorspace.
+ // For simplicity: do it even if it redundant (= already in that colorspace)
+ const QPdfEngine::ColorModel actualColorModel = colorModelForColor(color);
+
+ switch (actualColorModel) {
+ case QPdfEngine::ColorModel::RGB:
+ switch (domain) {
+ case ColorDomain::Stroking:
+ *currentPage << "/CSp CS\n"; break;
+ case ColorDomain::NonStroking:
+ *currentPage << "/CSp cs\n"; break;
+ case ColorDomain::NonStrokingPattern:
+ *currentPage << "/PCSp cs\n"; break;
+ }
+ break;
+ case QPdfEngine::ColorModel::Grayscale:
+ switch (domain) {
+ case ColorDomain::Stroking:
+ *currentPage << "/CSpg CS\n"; break;
+ case ColorDomain::NonStroking:
+ *currentPage << "/CSpg cs\n"; break;
+ case ColorDomain::NonStrokingPattern:
+ *currentPage << "/PCSpg cs\n"; break;
+ }
+ break;
+ case QPdfEngine::ColorModel::CMYK:
+ switch (domain) {
+ case ColorDomain::Stroking:
+ *currentPage << "/CSpcmyk CS\n"; break;
+ case ColorDomain::NonStroking:
+ *currentPage << "/CSpcmyk cs\n"; break;
+ case ColorDomain::NonStrokingPattern:
+ *currentPage << "/PCSpcmyk cs\n"; break;
+ }
+ break;
+ case QPdfEngine::ColorModel::Auto:
+ Q_UNREACHABLE_RETURN();
+ }
+
+ // If we also have a color specified, write it out.
+ if (!color.isValid())
+ return;
+
+ switch (actualColorModel) {
+ case QPdfEngine::ColorModel::RGB:
+ *currentPage << color.redF()
+ << color.greenF()
+ << color.blueF();
+ break;
+ case QPdfEngine::ColorModel::Grayscale: {
+ const qreal gray = qGray(color.rgba()) / 255.;
+ *currentPage << gray;
+ break;
+ }
+ case QPdfEngine::ColorModel::CMYK:
+ *currentPage << color.cyanF()
+ << color.magentaF()
+ << color.yellowF()
+ << color.blackF();
+ break;
+ case QPdfEngine::ColorModel::Auto:
+ Q_UNREACHABLE_RETURN();
+ }
}
void QPdfEnginePrivate::writeInfo()
@@ -1709,7 +1774,8 @@ int QPdfEnginePrivate::writeXmpDcumentMetaData()
const QString metaDataDate = timeStr + tzStr;
QFile metaDataFile(":/qpdf/qpdfa_metadata.xml"_L1);
- metaDataFile.open(QIODevice::ReadOnly);
+ bool ok = metaDataFile.open(QIODevice::ReadOnly);
+ Q_ASSERT(ok);
metaDataContent = QString::fromUtf8(metaDataFile.readAll()).arg(producer.toHtmlEscaped(),
title.toHtmlEscaped(),
creator.toHtmlEscaped(),
@@ -1735,7 +1801,8 @@ int QPdfEnginePrivate::writeOutputIntent()
const int colorProfile = addXrefEntry(-1);
{
QFile colorProfileFile(":/qpdf/sRGB2014.icc"_L1);
- colorProfileFile.open(QIODevice::ReadOnly);
+ bool ok = colorProfileFile.open(QIODevice::ReadOnly);
+ Q_ASSERT(ok);
const QByteArray colorProfileData = colorProfileFile.readAll();
QByteArray data;
@@ -1795,6 +1862,39 @@ void QPdfEnginePrivate::writePageRoot()
"endobj\n");
}
+void QPdfEnginePrivate::writeDestsRoot()
+{
+ if (destCache.isEmpty())
+ return;
+
+ QHash<QString, int> destObjects;
+ QByteArray xs, ys;
+ for (const DestInfo &destInfo : std::as_const(destCache)) {
+ int destObj = addXrefEntry(-1);
+ xs.setNum(static_cast<double>(destInfo.coords.x()), 'f');
+ ys.setNum(static_cast<double>(destInfo.coords.y()), 'f');
+ xprintf("[%d 0 R /XYZ %s %s 0]\n", destInfo.pageObj, xs.constData(), ys.constData());
+ xprintf("endobj\n");
+ destObjects.insert(destInfo.anchor, destObj);
+ }
+
+ // names
+ destsRoot = addXrefEntry(-1);
+ QStringList anchors = destObjects.keys();
+ anchors.sort();
+ xprintf("<<\n/Limits [");
+ printString(anchors.constFirst());
+ xprintf(" ");
+ printString(anchors.constLast());
+ xprintf("]\n/Names [\n");
+ for (const QString &anchor : std::as_const(anchors)) {
+ printString(anchor);
+ xprintf(" %d 0 R\n", destObjects[anchor]);
+ }
+ xprintf("]\n>>\n"
+ "endobj\n");
+}
+
void QPdfEnginePrivate::writeAttachmentRoot()
{
if (fileCache.isEmpty())
@@ -1834,7 +1934,7 @@ void QPdfEnginePrivate::writeAttachmentRoot()
}
// names
- addXrefEntry(namesRoot);
+ attachmentsRoot = addXrefEntry(-1);
xprintf("<</Names[");
for (int i = 0; i < size; ++i) {
auto attachment = fileCache.at(i);
@@ -1845,6 +1945,21 @@ void QPdfEnginePrivate::writeAttachmentRoot()
"endobj\n");
}
+void QPdfEnginePrivate::writeNamesRoot()
+{
+ addXrefEntry(namesRoot);
+ xprintf("<<\n");
+
+ if (attachmentsRoot)
+ xprintf("/EmbeddedFiles %d 0 R\n", attachmentsRoot);
+
+ if (destsRoot)
+ xprintf("/Dests %d 0 R\n", destsRoot);
+
+ xprintf(">>\n");
+ xprintf("endobj\n");
+}
+
void QPdfEnginePrivate::embedFont(QFontSubset *font)
{
//qDebug() << "embedFont" << font->object_id;
@@ -1938,7 +2053,7 @@ void QPdfEnginePrivate::embedFont(QFontSubset *font)
addXrefEntry(toUnicode);
QByteArray touc = font->createToUnicodeMap();
xprintf("<< /Length %d >>\n"
- "stream\n", touc.length());
+ "stream\n", touc.size());
write(touc);
write("\nendstream\n"
"endobj\n");
@@ -1961,7 +2076,7 @@ void QPdfEnginePrivate::embedFont(QFontSubset *font)
QByteArray cidSetStream(font->nGlyphs() / 8 + 1, 0);
int byteCounter = 0;
int bitCounter = 0;
- for (int i = 0; i < font->nGlyphs(); ++i) {
+ for (qsizetype i = 0; i < font->nGlyphs(); ++i) {
cidSetStream.data()[byteCounter] |= (1 << (7 - bitCounter));
bitCounter++;
@@ -2042,12 +2157,18 @@ void QPdfEnginePrivate::writePage()
xprintf("<<\n"
"/ColorSpace <<\n"
"/PCSp %d 0 R\n"
+ "/PCSpg %d 0 R\n"
+ "/PCSpcmyk %d 0 R\n"
"/CSp /DeviceRGB\n"
"/CSpg /DeviceGray\n"
+ "/CSpcmyk /DeviceCMYK\n"
">>\n"
"/ExtGState <<\n"
"/GSa %d 0 R\n",
- patternColorSpace, graphicsState);
+ patternColorSpaceRGB,
+ patternColorSpaceGrayscale,
+ patternColorSpaceCMYK,
+ graphicsState);
for (int i = 0; i < currentPage->graphicStates.size(); ++i)
xprintf("/GState%d %d 0 R\n", currentPage->graphicStates.at(i), currentPage->graphicStates.at(i));
@@ -2101,7 +2222,9 @@ void QPdfEnginePrivate::writeTail()
writePage();
writeFonts();
writePageRoot();
+ writeDestsRoot();
writeAttachmentRoot();
+ writeNamesRoot();
addXrefEntry(xrefPositions.size(),false);
xprintf("xref\n"
@@ -2280,16 +2403,15 @@ int QPdfEnginePrivate::writeCompressed(const char *src, int len)
{
#ifndef QT_NO_COMPRESS
if (do_compress) {
- uLongf destLen = len + len/100 + 13; // zlib requirement
- Bytef* dest = new Bytef[destLen];
- if (Z_OK == ::compress(dest, &destLen, (const Bytef*) src, (uLongf)len)) {
- stream->writeRawData((const char*)dest, destLen);
+ const QByteArray data = qCompress(reinterpret_cast<const uchar *>(src), len);
+ constexpr qsizetype HeaderSize = 4;
+ if (!data.isNull()) {
+ stream->writeRawData(data.data() + HeaderSize, data.size() - HeaderSize);
+ len = data.size() - HeaderSize;
} else {
qWarning("QPdfStream::writeCompressed: Error in compress()");
- destLen = 0;
+ len = 0;
}
- delete [] dest;
- len = destLen;
} else
#endif
{
@@ -2299,7 +2421,7 @@ int QPdfEnginePrivate::writeCompressed(const char *src, int len)
return len;
}
-int QPdfEnginePrivate::writeImage(const QByteArray &data, int width, int height, int depth,
+int QPdfEnginePrivate::writeImage(const QByteArray &data, int width, int height, WriteImageOption option,
int maskObject, int softMaskObject, bool dct, bool isMono)
{
int image = addXrefEntry(-1);
@@ -2309,7 +2431,8 @@ int QPdfEnginePrivate::writeImage(const QByteArray &data, int width, int height,
"/Width %d\n"
"/Height %d\n", width, height);
- if (depth == 1) {
+ switch (option) {
+ case WriteImageOption::Monochrome:
if (!isMono) {
xprintf("/ImageMask true\n"
"/Decode [1 0]\n");
@@ -2317,10 +2440,21 @@ int QPdfEnginePrivate::writeImage(const QByteArray &data, int width, int height,
xprintf("/BitsPerComponent 1\n"
"/ColorSpace /DeviceGray\n");
}
- } else {
+ break;
+ case WriteImageOption::Grayscale:
+ xprintf("/BitsPerComponent 8\n"
+ "/ColorSpace /DeviceGray\n");
+ break;
+ case WriteImageOption::RGB:
xprintf("/BitsPerComponent 8\n"
- "/ColorSpace %s\n", (depth == 32) ? "/DeviceRGB" : "/DeviceGray");
+ "/ColorSpace /DeviceRGB\n");
+ break;
+ case WriteImageOption::CMYK:
+ xprintf("/BitsPerComponent 8\n"
+ "/ColorSpace /DeviceCMYK\n");
+ break;
}
+
if (maskObject > 0)
xprintf("/Mask %d 0 R\n", maskObject);
if (softMaskObject > 0)
@@ -2335,7 +2469,7 @@ int QPdfEnginePrivate::writeImage(const QByteArray &data, int width, int height,
//qDebug("DCT");
xprintf("/Filter /DCTDecode\n>>\nstream\n");
write(data);
- len = data.length();
+ len = data.size();
} else {
if (do_compress)
xprintf("/Filter /FlateDecode\n>>\nstream\n");
@@ -2359,7 +2493,23 @@ struct QGradientBound {
};
Q_DECLARE_TYPEINFO(QGradientBound, Q_PRIMITIVE_TYPE);
-int QPdfEnginePrivate::createShadingFunction(const QGradient *gradient, int from, int to, bool reflect, bool alpha)
+void QPdfEnginePrivate::ShadingFunctionResult::writeColorSpace(QPdf::ByteStream *stream) const
+{
+ *stream << "/ColorSpace ";
+ switch (colorModel) {
+ case QPdfEngine::ColorModel::RGB:
+ *stream << "/DeviceRGB\n"; break;
+ case QPdfEngine::ColorModel::Grayscale:
+ *stream << "/DeviceGray\n"; break;
+ case QPdfEngine::ColorModel::CMYK:
+ *stream << "/DeviceCMYK\n"; break;
+ case QPdfEngine::ColorModel::Auto:
+ Q_UNREACHABLE(); break;
+ }
+}
+
+QPdfEnginePrivate::ShadingFunctionResult
+QPdfEnginePrivate::createShadingFunction(const QGradient *gradient, int from, int to, bool reflect, bool alpha)
{
QGradientStops stops = gradient->stops();
if (stops.isEmpty()) {
@@ -2371,6 +2521,35 @@ int QPdfEnginePrivate::createShadingFunction(const QGradient *gradient, int from
if (stops.at(stops.size() - 1).first < 1)
stops.append(QGradientStop(1, stops.at(stops.size() - 1).second));
+ // Color to use which colorspace to use
+ const QColor referenceColor = stops.constFirst().second;
+
+ switch (colorModel) {
+ case QPdfEngine::ColorModel::RGB:
+ case QPdfEngine::ColorModel::Grayscale:
+ case QPdfEngine::ColorModel::CMYK:
+ break;
+ case QPdfEngine::ColorModel::Auto: {
+ // Make sure that all the stops have the same color spec
+ // (we don't support anything else)
+ const QColor::Spec referenceSpec = referenceColor.spec();
+ bool warned = false;
+ for (QGradientStop &stop : stops) {
+ if (stop.second.spec() != referenceSpec) {
+ if (!warned) {
+ qWarning("QPdfEngine: unable to create a gradient between colors of different spec");
+ warned = true;
+ }
+ stop.second = stop.second.convertTo(referenceSpec);
+ }
+ }
+ break;
+ }
+ }
+
+ ShadingFunctionResult result;
+ result.colorModel = colorModelForColor(referenceColor);
+
QList<int> functions;
const int numStops = stops.size();
functions.reserve(numStops - 1);
@@ -2386,8 +2565,31 @@ int QPdfEnginePrivate::createShadingFunction(const QGradient *gradient, int from
s << "/C0 [" << stops.at(i).second.alphaF() << "]\n"
"/C1 [" << stops.at(i + 1).second.alphaF() << "]\n";
} else {
- s << "/C0 [" << stops.at(i).second.redF() << stops.at(i).second.greenF() << stops.at(i).second.blueF() << "]\n"
- "/C1 [" << stops.at(i + 1).second.redF() << stops.at(i + 1).second.greenF() << stops.at(i + 1).second.blueF() << "]\n";
+ switch (result.colorModel) {
+ case QPdfEngine::ColorModel::RGB:
+ s << "/C0 [" << stops.at(i).second.redF() << stops.at(i).second.greenF() << stops.at(i).second.blueF() << "]\n"
+ "/C1 [" << stops.at(i + 1).second.redF() << stops.at(i + 1).second.greenF() << stops.at(i + 1).second.blueF() << "]\n";
+ break;
+ case QPdfEngine::ColorModel::Grayscale:
+ s << "/C0 [" << (qGray(stops.at(i).second.rgba()) / 255.) << "]\n"
+ "/C1 [" << (qGray(stops.at(i + 1).second.rgba()) / 255.) << "]\n";
+ break;
+ case QPdfEngine::ColorModel::CMYK:
+ s << "/C0 [" << stops.at(i).second.cyanF()
+ << stops.at(i).second.magentaF()
+ << stops.at(i).second.yellowF()
+ << stops.at(i).second.blackF() << "]\n"
+ "/C1 [" << stops.at(i + 1).second.cyanF()
+ << stops.at(i + 1).second.magentaF()
+ << stops.at(i + 1).second.yellowF()
+ << stops.at(i + 1).second.blackF() << "]\n";
+ break;
+
+ case QPdfEngine::ColorModel::Auto:
+ Q_UNREACHABLE();
+ break;
+ }
+
}
s << ">>\n"
"endobj\n";
@@ -2455,7 +2657,8 @@ int QPdfEnginePrivate::createShadingFunction(const QGradient *gradient, int from
} else {
function = functions.at(0);
}
- return function;
+ result.function = function;
+ return result;
}
int QPdfEnginePrivate::generateLinearGradientShader(const QLinearGradient *gradient, const QTransform &matrix, bool alpha)
@@ -2501,17 +2704,22 @@ int QPdfEnginePrivate::generateLinearGradientShader(const QLinearGradient *gradi
}
}
- int function = createShadingFunction(gradient, from, to, reflect, alpha);
+ const auto shadingFunctionResult = createShadingFunction(gradient, from, to, reflect, alpha);
QByteArray shader;
QPdf::ByteStream s(&shader);
s << "<<\n"
- "/ShadingType 2\n"
- "/ColorSpace " << (alpha ? "/DeviceGray\n" : "/DeviceRGB\n") <<
- "/AntiAlias true\n"
+ "/ShadingType 2\n";
+
+ if (alpha)
+ s << "/ColorSpace /DeviceGray\n";
+ else
+ shadingFunctionResult.writeColorSpace(&s);
+
+ s << "/AntiAlias true\n"
"/Coords [" << start.x() << start.y() << stop.x() << stop.y() << "]\n"
"/Extend [true true]\n"
- "/Function " << function << "0 R\n"
+ "/Function " << shadingFunctionResult.function << "0 R\n"
">>\n"
"endobj\n";
int shaderObject = addXrefEntry(-1);
@@ -2569,18 +2777,23 @@ int QPdfEnginePrivate::generateRadialGradientShader(const QRadialGradient *gradi
}
}
- int function = createShadingFunction(gradient, from, to, reflect, alpha);
+ const auto shadingFunctionResult = createShadingFunction(gradient, from, to, reflect, alpha);
QByteArray shader;
QPdf::ByteStream s(&shader);
s << "<<\n"
- "/ShadingType 3\n"
- "/ColorSpace " << (alpha ? "/DeviceGray\n" : "/DeviceRGB\n") <<
- "/AntiAlias true\n"
+ "/ShadingType 3\n";
+
+ if (alpha)
+ s << "/ColorSpace /DeviceGray\n";
+ else
+ shadingFunctionResult.writeColorSpace(&s);
+
+ s << "/AntiAlias true\n"
"/Domain [0 1]\n"
"/Coords [" << p0.x() << p0.y() << r0 << p1.x() << p1.y() << r1 << "]\n"
"/Extend [true true]\n"
- "/Function " << function << "0 R\n"
+ "/Function " << shadingFunctionResult.function << "0 R\n"
">>\n"
"endobj\n";
int shaderObject = addXrefEntry(-1);
@@ -2666,7 +2879,7 @@ int QPdfEnginePrivate::gradientBrush(const QBrush &b, const QTransform &matrix,
"/Shading << /Shader" << alphaShaderObject << alphaShaderObject << "0 R >>\n"
">>\n";
- f << "/Length " << content.length() << "\n"
+ f << "/Length " << content.size() << "\n"
">>\n"
"stream\n"
<< content
@@ -2784,7 +2997,7 @@ int QPdfEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor,
s << "/XObject << /Im" << imageObject << ' ' << imageObject << "0 R >> ";
}
s << ">>\n"
- "/Length " << pattern.length() << "\n"
+ "/Length " << pattern.size() << "\n"
">>\n"
"stream\n"
<< pattern
@@ -2819,6 +3032,7 @@ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, bool lossless,
QImage image = img;
QImage::Format format = image.format();
+ const bool grayscale = (colorModel == QPdfEngine::ColorModel::Grayscale);
if (pdfVersion == QPdfEngine::Version_A1b) {
if (image.hasAlphaChannel()) {
@@ -2842,7 +3056,7 @@ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, bool lossless,
format = QImage::Format_Mono;
} else {
*bitmap = false;
- if (format != QImage::Format_RGB32 && format != QImage::Format_ARGB32) {
+ if (format != QImage::Format_RGB32 && format != QImage::Format_ARGB32 && format != QImage::Format_CMYK8888) {
image = image.convertToFormat(QImage::Format_ARGB32);
format = QImage::Format_ARGB32;
}
@@ -2850,7 +3064,6 @@ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, bool lossless,
int w = image.width();
int h = image.height();
- int d = image.depth();
if (format == QImage::Format_Mono) {
int bytesPerLine = (w + 7) >> 3;
@@ -2861,7 +3074,7 @@ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, bool lossless,
memcpy(rawdata, image.constScanLine(y), bytesPerLine);
rawdata += bytesPerLine;
}
- object = writeImage(data, w, h, d, 0, 0, false, is_monochrome(img.colorTable()));
+ object = writeImage(data, w, h, WriteImageOption::Monochrome, 0, 0, false, is_monochrome(img.colorTable()));
} else {
QByteArray softMaskData;
bool dct = false;
@@ -2873,10 +3086,14 @@ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, bool lossless,
QBuffer buffer(&imageData);
QImageWriter writer(&buffer, "jpeg");
writer.setQuality(94);
+ if (format == QImage::Format_CMYK8888) {
+ // PDFs require CMYK colors not to be inverted in the JPEG encoding
+ writer.setSubType("CMYK");
+ }
writer.write(image);
dct = true;
- if (format != QImage::Format_RGB32) {
+ if (format != QImage::Format_RGB32 && format != QImage::Format_CMYK8888) {
softMaskData.resize(w * h);
uchar *sdata = (uchar *)softMaskData.data();
for (int y = 0; y < h; ++y) {
@@ -2891,41 +3108,59 @@ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, bool lossless,
}
}
} else {
- imageData.resize(grayscale ? w * h : 3 * w * h);
- uchar *data = (uchar *)imageData.data();
- softMaskData.resize(w * h);
- uchar *sdata = (uchar *)softMaskData.data();
- for (int y = 0; y < h; ++y) {
- const QRgb *rgb = (const QRgb *)image.constScanLine(y);
+ if (format == QImage::Format_CMYK8888) {
+ imageData.resize(grayscale ? w * h : w * h * 4);
+ uchar *data = (uchar *)imageData.data();
+ const qsizetype bytesPerLine = image.bytesPerLine();
if (grayscale) {
- for (int x = 0; x < w; ++x) {
- *(data++) = qGray(*rgb);
- uchar alpha = qAlpha(*rgb);
- *sdata++ = alpha;
- hasMask |= (alpha < 255);
- hasAlpha |= (alpha != 0 && alpha != 255);
- ++rgb;
+ for (int y = 0; y < h; ++y) {
+ const uint *cmyk = (const uint *)image.constScanLine(y);
+ for (int x = 0; x < w; ++x)
+ *data++ = qGray(QCmyk32::fromCmyk32(*cmyk++).toColor().rgba());
}
} else {
- for (int x = 0; x < w; ++x) {
- *(data++) = qRed(*rgb);
- *(data++) = qGreen(*rgb);
- *(data++) = qBlue(*rgb);
- uchar alpha = qAlpha(*rgb);
- *sdata++ = alpha;
- hasMask |= (alpha < 255);
- hasAlpha |= (alpha != 0 && alpha != 255);
- ++rgb;
+ for (int y = 0; y < h; ++y) {
+ uchar *start = data + y * w * 4;
+ memcpy(start, image.constScanLine(y), bytesPerLine);
+ }
+ }
+ } else {
+ imageData.resize(grayscale ? w * h : 3 * w * h);
+ uchar *data = (uchar *)imageData.data();
+ softMaskData.resize(w * h);
+ uchar *sdata = (uchar *)softMaskData.data();
+ for (int y = 0; y < h; ++y) {
+ const QRgb *rgb = (const QRgb *)image.constScanLine(y);
+ if (grayscale) {
+ for (int x = 0; x < w; ++x) {
+ *(data++) = qGray(*rgb);
+ uchar alpha = qAlpha(*rgb);
+ *sdata++ = alpha;
+ hasMask |= (alpha < 255);
+ hasAlpha |= (alpha != 0 && alpha != 255);
+ ++rgb;
+ }
+ } else {
+ for (int x = 0; x < w; ++x) {
+ *(data++) = qRed(*rgb);
+ *(data++) = qGreen(*rgb);
+ *(data++) = qBlue(*rgb);
+ uchar alpha = qAlpha(*rgb);
+ *sdata++ = alpha;
+ hasMask |= (alpha < 255);
+ hasAlpha |= (alpha != 0 && alpha != 255);
+ ++rgb;
+ }
}
}
}
- if (format == QImage::Format_RGB32)
+ if (format == QImage::Format_RGB32 || format == QImage::Format_CMYK8888)
hasAlpha = hasMask = false;
}
int maskObject = 0;
int softMaskObject = 0;
if (hasAlpha) {
- softMaskObject = writeImage(softMaskData, w, h, 8, 0, 0);
+ softMaskObject = writeImage(softMaskData, w, h, WriteImageOption::Grayscale, 0, 0);
} else if (hasMask) {
// dither the soft mask to 1bit and add it. This also helps PDF viewers
// without transparency support
@@ -2941,9 +3176,18 @@ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, bool lossless,
}
mdata += bytesPerLine;
}
- maskObject = writeImage(mask, w, h, 1, 0, 0);
+ maskObject = writeImage(mask, w, h, WriteImageOption::Monochrome, 0, 0);
}
- object = writeImage(imageData, w, h, grayscale ? 8 : 32,
+
+ const WriteImageOption option = [&]() {
+ if (grayscale)
+ return WriteImageOption::Grayscale;
+ if (format == QImage::Format_CMYK8888)
+ return WriteImageOption::CMYK;
+ return WriteImageOption::RGB;
+ }();
+
+ object = writeImage(imageData, w, h, option,
maskObject, softMaskObject, dct);
}
imageCache.insert(serial_no, object);
@@ -2954,7 +3198,9 @@ void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
{
Q_Q(QPdfEngine);
- if (ti.charFormat.hasProperty(QTextFormat::AnchorHref)) {
+ const bool isLink = ti.charFormat.hasProperty(QTextFormat::AnchorHref);
+ const bool isAnchor = ti.charFormat.hasProperty(QTextFormat::AnchorName);
+ if (isLink || isAnchor) {
qreal size = ti.fontEngine->fontDef.pixelSize;
int synthesized = ti.fontEngine->synthesized();
qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
@@ -2974,32 +3220,47 @@ void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
trans.map(0, 0, &x1, &y1);
trans.map(ti.width.toReal()/size, (ti.ascent.toReal()-ti.descent.toReal())/size, &x2, &y2);
- uint annot = addXrefEntry(-1);
- QByteArray x1s, y1s, x2s, y2s;
- x1s.setNum(static_cast<double>(x1), 'f');
- y1s.setNum(static_cast<double>(y1), 'f');
- x2s.setNum(static_cast<double>(x2), 'f');
- y2s.setNum(static_cast<double>(y2), 'f');
- QByteArray rectData = x1s + ' ' + y1s + ' ' + x2s + ' ' + y2s;
- xprintf("<<\n/Type /Annot\n/Subtype /Link\n");
-
- if (pdfVersion == QPdfEngine::Version_A1b)
- xprintf("/F 4\n"); // enable print flag, disable all other
-
- xprintf("/Rect [");
- xprintf(rectData.constData());
+ if (isLink) {
+ uint annot = addXrefEntry(-1);
+ QByteArray x1s, y1s, x2s, y2s;
+ x1s.setNum(static_cast<double>(x1), 'f');
+ y1s.setNum(static_cast<double>(y1), 'f');
+ x2s.setNum(static_cast<double>(x2), 'f');
+ y2s.setNum(static_cast<double>(y2), 'f');
+ QByteArray rectData = x1s + ' ' + y1s + ' ' + x2s + ' ' + y2s;
+ xprintf("<<\n/Type /Annot\n/Subtype /Link\n");
+
+ if (pdfVersion == QPdfEngine::Version_A1b)
+ xprintf("/F 4\n"); // enable print flag, disable all other
+
+ xprintf("/Rect [");
+ xprintf(rectData.constData());
#ifdef Q_DEBUG_PDF_LINKS
- xprintf("]\n/Border [16 16 1]\n/A <<\n");
+ xprintf("]\n/Border [16 16 1]\n");
#else
- xprintf("]\n/Border [0 0 0]\n/A <<\n");
+ xprintf("]\n/Border [0 0 0]\n");
#endif
- xprintf("/Type /Action\n/S /URI\n/URI (%s)\n",
- ti.charFormat.anchorHref().toLatin1().constData());
- xprintf(">>\n>>\n");
- xprintf("endobj\n");
+ const QString link = ti.charFormat.anchorHref();
+ const bool isInternal = link.startsWith(QLatin1Char('#'));
+ if (!isInternal) {
+ xprintf("/A <<\n");
+ xprintf("/Type /Action\n/S /URI\n/URI (%s)\n", link.toLatin1().constData());
+ xprintf(">>\n");
+ } else {
+ xprintf("/Dest ");
+ printString(link.sliced(1));
+ xprintf("\n");
+ }
+ xprintf(">>\n");
+ xprintf("endobj\n");
- if (!currentPage->annotations.contains(annot)) {
- currentPage->annotations.append(annot);
+ if (!currentPage->annotations.contains(annot)) {
+ currentPage->annotations.append(annot);
+ }
+ } else {
+ const QString anchor = ti.charFormat.anchorNames().constFirst();
+ const uint curPage = pages.last();
+ destCache.append(DestInfo({ anchor, curPage, QPointF(x1, y2) }));
}
}
@@ -3081,7 +3342,7 @@ void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
x += .3*y;
x /= stretch;
char buf[5];
- int g = font->addGlyph(glyphs[i]);
+ qsizetype g = font->addGlyph(glyphs[i]);
*currentPage << x - last_x << last_y - y << "Td <"
<< QPdf::toHex((ushort)g, buf) << "> Tj\n";
last_x = x;
@@ -3101,7 +3362,7 @@ void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
x += .3*y;
x /= stretch;
char buf[5];
- int g = font->addGlyph(glyphs[i]);
+ qsizetype g = font->addGlyph(glyphs[i]);
*currentPage << x - last_x << last_y - y << "Td <"
<< QPdf::toHex((ushort)g, buf) << "> Tj\n";
last_x = x;
diff --git a/src/gui/painting/qpdf_p.h b/src/gui/painting/qpdf_p.h
index 92a848e0d1..3c34e0bd7a 100644
--- a/src/gui/painting/qpdf_p.h
+++ b/src/gui/painting/qpdf_p.h
@@ -60,10 +60,6 @@ namespace QPdf {
static inline int maxMemorySize() { return 100000000; }
static inline int chunkSize() { return 10000000; }
- protected:
- void constructor_helper(QIODevice *dev);
- void constructor_helper(QByteArray *ba);
-
private:
void prepareBuffer();
@@ -142,7 +138,7 @@ public:
};
QPdfEngine();
- QPdfEngine(QPdfEnginePrivate &d);
+ explicit QPdfEngine(QPdfEnginePrivate &d);
~QPdfEngine() {}
void setOutputFilename(const QString &filename);
@@ -157,6 +153,18 @@ public:
void addFileAttachment(const QString &fileName, const QByteArray &data, const QString &mimeType);
+ // keep in sync with QPdfWriter
+ enum class ColorModel
+ {
+ RGB,
+ Grayscale,
+ CMYK,
+ Auto,
+ };
+
+ ColorModel colorModel() const;
+ void setColorModel(ColorModel model);
+
// reimplementations QPaintEngine
bool begin(QPaintDevice *pdev) override;
bool end() override;
@@ -240,6 +248,7 @@ public:
bool needsTransform;
qreal opacity;
QPdfEngine::PdfVersion pdfVersion;
+ QPdfEngine::ColorModel colorModel;
QHash<QFontEngine::FaceId, QFontSubset *> fonts;
@@ -255,7 +264,6 @@ public:
QString creator;
bool embedFonts;
int resolution;
- bool grayscale;
// Page layout: size, orientation and margins
QPageLayout m_pageLayout;
@@ -265,13 +273,29 @@ private:
int generateGradientShader(const QGradient *gradient, const QTransform &matrix, bool alpha = false);
int generateLinearGradientShader(const QLinearGradient *lg, const QTransform &matrix, bool alpha);
int generateRadialGradientShader(const QRadialGradient *gradient, const QTransform &matrix, bool alpha);
- int createShadingFunction(const QGradient *gradient, int from, int to, bool reflect, bool alpha);
+ struct ShadingFunctionResult
+ {
+ int function;
+ QPdfEngine::ColorModel colorModel;
+ void writeColorSpace(QPdf::ByteStream *stream) const;
+ };
+ ShadingFunctionResult createShadingFunction(const QGradient *gradient, int from, int to, bool reflect, bool alpha);
+ enum class ColorDomain {
+ Stroking,
+ NonStroking,
+ NonStrokingPattern,
+ };
+
+ QPdfEngine::ColorModel colorModelForColor(const QColor &color) const;
+ void writeColor(ColorDomain domain, const QColor &color);
void writeInfo();
int writeXmpDcumentMetaData();
int writeOutputIntent();
void writePageRoot();
+ void writeDestsRoot();
void writeAttachmentRoot();
+ void writeNamesRoot();
void writeFonts();
void embedFont(QFontSubset *font);
qreal calcUserUnit() const;
@@ -280,7 +304,15 @@ private:
QDataStream* stream;
int streampos;
- int writeImage(const QByteArray &data, int width, int height, int depth,
+ enum class WriteImageOption
+ {
+ Monochrome,
+ Grayscale,
+ RGB,
+ CMYK,
+ };
+
+ int writeImage(const QByteArray &data, int width, int height, WriteImageOption option,
int maskObject, int softMaskObject, bool dct = false, bool isMono = false);
void writePage();
@@ -293,7 +325,7 @@ private:
}
int writeCompressed(const char *src, int len);
- inline int writeCompressed(const QByteArray &data) { return writeCompressed(data.constData(), data.length()); }
+ inline int writeCompressed(const QByteArray &data) { return writeCompressed(data.constData(), data.size()); }
int writeCompressed(QIODevice *dev);
struct AttachmentInfo
@@ -305,11 +337,23 @@ private:
QString mimeType;
};
+ struct DestInfo
+ {
+ QString anchor;
+ uint pageObj;
+ QPointF coords;
+ };
+
// various PDF objects
- int pageRoot, embeddedfilesRoot, namesRoot, catalog, info, graphicsState, patternColorSpace;
+ int pageRoot, namesRoot, destsRoot, attachmentsRoot, catalog, info;
+ int graphicsState;
+ int patternColorSpaceRGB;
+ int patternColorSpaceGrayscale;
+ int patternColorSpaceCMYK;
QList<uint> pages;
QHash<qint64, uint> imageCache;
QHash<QPair<uint, uint>, uint > alphaCache;
+ QList<DestInfo> destCache;
QList<AttachmentInfo> fileCache;
QByteArray xmpDocumentMetadata;
};
diff --git a/src/gui/painting/qpdfwriter.cpp b/src/gui/painting/qpdfwriter.cpp
index f28a460d7c..bce65927ab 100644
--- a/src/gui/painting/qpdfwriter.cpp
+++ b/src/gui/painting/qpdfwriter.cpp
@@ -295,6 +295,53 @@ bool QPdfWriter::newPage()
return d->engine->newPage();
}
+/*!
+ \enum QPdfWriter::ColorModel
+ \since 6.8
+
+ This enumeration describes the way in which the PDF engine interprets
+ stroking and filling colors, set as a QPainter's pen or brush (via
+ QPen and QBrush).
+
+ \value RGB All colors are converted to RGB and saved as such in the
+ PDF.
+
+ \value Grayscale All colors are converted to grayscale. For backwards
+ compatibility, they are emitted in the PDF output as RGB colors, with
+ identical quantities of red, green and blue.
+
+ \value CMYK All colors are converted to CMYK and saved as such.
+
+ \value Auto RGB colors are emitted as RGB; CMYK colors are emitted as
+ CMYK. Colors of any other color spec are converted to RGB.
+ This is the default since Qt 6.8.
+
+ \sa QColor, QGradient
+*/
+
+/*!
+ \since 6.8
+
+ Returns the color model used by this PDF writer.
+ The default is QPdfWriter::ColorModel::Auto.
+*/
+QPdfWriter::ColorModel QPdfWriter::colorModel() const
+{
+ Q_D(const QPdfWriter);
+ return static_cast<ColorModel>(d->engine->d_func()->colorModel);
+}
+
+/*!
+ \since 6.8
+
+ Sets the color model used by this PDF writer to \a model.
+*/
+void QPdfWriter::setColorModel(ColorModel model)
+{
+ Q_D(QPdfWriter);
+ d->engine->d_func()->colorModel = static_cast<QPdfEngine::ColorModel>(model);
+}
+
QT_END_NAMESPACE
#include "moc_qpdfwriter.cpp"
diff --git a/src/gui/painting/qpdfwriter.h b/src/gui/painting/qpdfwriter.h
index 5885c4ef1a..1a4b607b66 100644
--- a/src/gui/painting/qpdfwriter.h
+++ b/src/gui/painting/qpdfwriter.h
@@ -44,6 +44,18 @@ public:
void addFileAttachment(const QString &fileName, const QByteArray &data, const QString &mimeType = QString());
+ enum class ColorModel
+ {
+ RGB,
+ Grayscale,
+ CMYK,
+ Auto,
+ };
+ Q_ENUM(ColorModel)
+
+ ColorModel colorModel() const;
+ void setColorModel(ColorModel model);
+
protected:
QPaintEngine *paintEngine() const override;
int metric(PaintDeviceMetric id) const override;
diff --git a/src/gui/painting/qpen.cpp b/src/gui/painting/qpen.cpp
index 958539feda..d37beda6b6 100644
--- a/src/gui/painting/qpen.cpp
+++ b/src/gui/painting/qpen.cpp
@@ -10,8 +10,6 @@
QT_BEGIN_NAMESPACE
-typedef QPenPrivate QPenData;
-
/*!
\class QPen
\inmodule QtGui
@@ -193,7 +191,7 @@ typedef QPenPrivate QPenData;
*/
QPenPrivate::QPenPrivate(const QBrush &_brush, qreal _width, Qt::PenStyle penStyle,
Qt::PenCapStyle _capStyle, Qt::PenJoinStyle _joinStyle)
- : ref(1), dashOffset(0), miterLimit(2),
+ : dashOffset(0), miterLimit(2),
cosmetic(false)
{
width = _width;
@@ -209,17 +207,13 @@ static const Qt::PenJoinStyle qpen_default_join = Qt::BevelJoin;
class QPenDataHolder
{
public:
- QPenData *pen;
+ QPen::DataPtr pen;
QPenDataHolder(const QBrush &brush, qreal width, Qt::PenStyle penStyle,
Qt::PenCapStyle penCapStyle, Qt::PenJoinStyle _joinStyle)
- : pen(new QPenData(brush, width, penStyle, penCapStyle, _joinStyle))
+ : pen(new QPenPrivate(brush, width, penStyle, penCapStyle, _joinStyle))
{ }
- ~QPenDataHolder()
- {
- if (!pen->ref.deref())
- delete pen;
- pen = nullptr;
- }
+ ~QPenDataHolder() = default;
+ Q_DISABLE_COPY_MOVE(QPenDataHolder)
};
Q_GLOBAL_STATIC_WITH_ARGS(QPenDataHolder, defaultPenInstance,
@@ -234,7 +228,6 @@ Q_GLOBAL_STATIC_WITH_ARGS(QPenDataHolder, nullPenInstance,
QPen::QPen()
{
d = defaultPenInstance()->pen;
- d->ref.ref();
}
/*!
@@ -247,9 +240,8 @@ QPen::QPen(Qt::PenStyle style)
{
if (style == Qt::NoPen) {
d = nullPenInstance()->pen;
- d->ref.ref();
} else {
- d = new QPenData(Qt::black, 1, style, qpen_default_cap, qpen_default_join);
+ d = new QPenPrivate(Qt::black, 1, style, qpen_default_cap, qpen_default_join);
}
}
@@ -262,7 +254,7 @@ QPen::QPen(Qt::PenStyle style)
QPen::QPen(const QColor &color)
{
- d = new QPenData(color, 1, Qt::SolidLine, qpen_default_cap, qpen_default_join);
+ d = new QPenPrivate(color, 1, Qt::SolidLine, qpen_default_cap, qpen_default_join);
}
@@ -277,7 +269,7 @@ QPen::QPen(const QColor &color)
QPen::QPen(const QBrush &brush, qreal width, Qt::PenStyle s, Qt::PenCapStyle c, Qt::PenJoinStyle j)
{
- d = new QPenData(brush, width, s, c, j);
+ d = new QPenPrivate(brush, width, s, c, j);
}
/*!
@@ -287,10 +279,8 @@ QPen::QPen(const QBrush &brush, qreal width, Qt::PenStyle s, Qt::PenCapStyle c,
*/
QPen::QPen(const QPen &p) noexcept
+ : d(p.d)
{
- d = p.d;
- if (d)
- d->ref.ref();
}
@@ -309,11 +299,9 @@ QPen::QPen(const QPen &p) noexcept
Destroys the pen.
*/
-QPen::~QPen()
-{
- if (d && !d->ref.deref())
- delete d;
-}
+QPen::~QPen() = default;
+
+QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QPenPrivate)
/*!
\fn void QPen::detach()
@@ -327,14 +315,7 @@ QPen::~QPen()
void QPen::detach()
{
- if (d->ref.loadRelaxed() == 1)
- return;
-
- QPenData *x = new QPenData(*static_cast<QPenData *>(d));
- if (!d->ref.deref())
- delete d;
- x->ref.storeRelaxed(1);
- d = x;
+ d.detach();
}
@@ -407,9 +388,8 @@ void QPen::setStyle(Qt::PenStyle s)
return;
detach();
d->style = s;
- QPenData *dd = static_cast<QPenData *>(d);
- dd->dashPattern.clear();
- dd->dashOffset = 0;
+ d->dashPattern.clear();
+ d->dashOffset = 0;
}
/*!
@@ -419,36 +399,35 @@ void QPen::setStyle(Qt::PenStyle s)
*/
QList<qreal> QPen::dashPattern() const
{
- QPenData *dd = static_cast<QPenData *>(d);
if (d->style == Qt::SolidLine || d->style == Qt::NoPen) {
return QList<qreal>();
- } else if (dd->dashPattern.isEmpty()) {
+ } else if (d->dashPattern.isEmpty()) {
const qreal space = 2;
const qreal dot = 1;
const qreal dash = 4;
switch (d->style) {
case Qt::DashLine:
- dd->dashPattern.reserve(2);
- dd->dashPattern << dash << space;
+ d->dashPattern.reserve(2);
+ d->dashPattern << dash << space;
break;
case Qt::DotLine:
- dd->dashPattern.reserve(2);
- dd->dashPattern << dot << space;
+ d->dashPattern.reserve(2);
+ d->dashPattern << dot << space;
break;
case Qt::DashDotLine:
- dd->dashPattern.reserve(4);
- dd->dashPattern << dash << space << dot << space;
+ d->dashPattern.reserve(4);
+ d->dashPattern << dash << space << dot << space;
break;
case Qt::DashDotDotLine:
- dd->dashPattern.reserve(6);
- dd->dashPattern << dash << space << dot << space << dot << space;
+ d->dashPattern.reserve(6);
+ d->dashPattern << dash << space << dot << space << dot << space;
break;
default:
break;
}
}
- return dd->dashPattern;
+ return d->dashPattern;
}
/*!
@@ -487,13 +466,12 @@ void QPen::setDashPattern(const QList<qreal> &pattern)
return;
detach();
- QPenData *dd = static_cast<QPenData *>(d);
- dd->dashPattern = pattern;
+ d->dashPattern = pattern;
d->style = Qt::CustomDashLine;
- if ((dd->dashPattern.size() % 2) == 1) {
+ if ((d->dashPattern.size() % 2) == 1) {
qWarning("QPen::setDashPattern: Pattern not of even length");
- dd->dashPattern << 1;
+ d->dashPattern << 1;
}
}
@@ -505,8 +483,7 @@ void QPen::setDashPattern(const QList<qreal> &pattern)
*/
qreal QPen::dashOffset() const
{
- QPenData *dd = static_cast<QPenData *>(d);
- return dd->dashOffset;
+ return d->dashOffset;
}
/*!
Sets the dash offset (the starting point on the dash pattern) for this pen
@@ -528,13 +505,12 @@ qreal QPen::dashOffset() const
*/
void QPen::setDashOffset(qreal offset)
{
- if (qFuzzyCompare(offset, static_cast<QPenData *>(d)->dashOffset))
+ if (qFuzzyCompare(offset, d->dashOffset))
return;
detach();
- QPenData *dd = static_cast<QPenData *>(d);
- dd->dashOffset = offset;
+ d->dashOffset = offset;
if (d->style != Qt::CustomDashLine) {
- dd->dashPattern = dashPattern();
+ d->dashPattern = dashPattern();
d->style = Qt::CustomDashLine;
}
}
@@ -547,8 +523,7 @@ void QPen::setDashOffset(qreal offset)
*/
qreal QPen::miterLimit() const
{
- const QPenData *dd = static_cast<QPenData *>(d);
- return dd->miterLimit;
+ return d->miterLimit;
}
/*!
@@ -570,8 +545,7 @@ qreal QPen::miterLimit() const
void QPen::setMiterLimit(qreal limit)
{
detach();
- QPenData *dd = static_cast<QPenData *>(d);
- dd->miterLimit = limit;
+ d->miterLimit = limit;
}
@@ -782,8 +756,7 @@ bool QPen::isSolid() const
bool QPen::isCosmetic() const
{
- QPenData *dd = static_cast<QPenData *>(d);
- return (dd->cosmetic == true) || d->width == 0;
+ return (d->cosmetic == true) || d->width == 0;
}
@@ -797,8 +770,7 @@ bool QPen::isCosmetic() const
void QPen::setCosmetic(bool cosmetic)
{
detach();
- QPenData *dd = static_cast<QPenData *>(d);
- dd->cosmetic = cosmetic;
+ d->cosmetic = cosmetic;
}
@@ -825,19 +797,17 @@ void QPen::setCosmetic(bool cosmetic)
bool QPen::operator==(const QPen &p) const
{
- QPenData *dd = static_cast<QPenData *>(d);
- QPenData *pdd = static_cast<QPenData *>(p.d);
return (p.d == d)
|| (p.d->style == d->style
&& p.d->capStyle == d->capStyle
&& p.d->joinStyle == d->joinStyle
&& p.d->width == d->width
- && pdd->miterLimit == dd->miterLimit
+ && p.d->miterLimit == d->miterLimit
&& (d->style != Qt::CustomDashLine
- || (qFuzzyCompare(pdd->dashOffset, dd->dashOffset) &&
- pdd->dashPattern == dd->dashPattern))
+ || (qFuzzyCompare(p.d->dashOffset, d->dashOffset) &&
+ p.d->dashPattern == d->dashPattern))
&& p.d->brush == d->brush
- && pdd->cosmetic == dd->cosmetic);
+ && p.d->cosmetic == d->cosmetic);
}
@@ -869,14 +839,13 @@ bool QPen::isDetached()
QDataStream &operator<<(QDataStream &s, const QPen &p)
{
- QPenData *dd = static_cast<QPenData *>(p.d);
if (s.version() < 3) {
s << (quint8)p.style();
} else if (s.version() < QDataStream::Qt_4_3) {
s << (quint8)(uint(p.style()) | uint(p.capStyle()) | uint(p.joinStyle()));
} else {
s << (quint16)(uint(p.style()) | uint(p.capStyle()) | uint(p.joinStyle()));
- s << (bool)(dd->cosmetic);
+ s << (bool)(p.d->cosmetic);
}
if (s.version() < 7) {
@@ -965,16 +934,15 @@ QDataStream &operator>>(QDataStream &s, QPen &p)
}
p.detach();
- QPenData *dd = static_cast<QPenData *>(p.d);
- dd->width = width;
- dd->brush = brush;
- dd->style = Qt::PenStyle(style & Qt::MPenStyle);
- dd->capStyle = Qt::PenCapStyle(style & Qt::MPenCapStyle);
- dd->joinStyle = Qt::PenJoinStyle(style & Qt::MPenJoinStyle);
- dd->dashPattern = dashPattern;
- dd->miterLimit = miterLimit;
- dd->dashOffset = dashOffset;
- dd->cosmetic = cosmetic;
+ p.d->width = width;
+ p.d->brush = brush;
+ p.d->style = Qt::PenStyle(style & Qt::MPenStyle);
+ p.d->capStyle = Qt::PenCapStyle(style & Qt::MPenCapStyle);
+ p.d->joinStyle = Qt::PenJoinStyle(style & Qt::MPenJoinStyle);
+ p.d->dashPattern = dashPattern;
+ p.d->miterLimit = miterLimit;
+ p.d->dashOffset = dashOffset;
+ p.d->cosmetic = cosmetic;
return s;
}
diff --git a/src/gui/painting/qpen.h b/src/gui/painting/qpen.h
index d8f13df388..3367b96c35 100644
--- a/src/gui/painting/qpen.h
+++ b/src/gui/painting/qpen.h
@@ -4,6 +4,7 @@
#ifndef QPEN_H
#define QPEN_H
+#include <QtCore/qshareddata.h>
#include <QtGui/qtguiglobal.h>
#include <QtGui/qcolor.h>
#include <QtGui/qbrush.h>
@@ -21,6 +22,8 @@ Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPen &);
Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPen &);
#endif
+QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QPenPrivate, Q_GUI_EXPORT)
+
class Q_GUI_EXPORT QPen
{
public:
@@ -34,10 +37,9 @@ public:
~QPen();
QPen &operator=(const QPen &pen) noexcept;
- QPen(QPen &&other) noexcept
- : d(qExchange(other.d, nullptr)) {}
+ QPen(QPen &&other) noexcept = default;
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QPen)
- void swap(QPen &other) noexcept { qt_ptr_swap(d, other.d); }
+ void swap(QPen &other) noexcept { d.swap(other.d); }
Qt::PenStyle style() const;
void setStyle(Qt::PenStyle);
@@ -79,15 +81,19 @@ public:
operator QVariant() const;
bool isDetached();
+
private:
friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPen &);
friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPen &);
+public:
+ using DataPtr = QExplicitlySharedDataPointer<QPenPrivate>;
+
+private:
void detach();
- class QPenPrivate *d;
+ DataPtr d;
public:
- typedef QPenPrivate * DataPtr;
inline DataPtr &data_ptr() { return d; }
};
diff --git a/src/gui/painting/qpen_p.h b/src/gui/painting/qpen_p.h
index 39ed024d85..a939c5e4f6 100644
--- a/src/gui/painting/qpen_p.h
+++ b/src/gui/painting/qpen_p.h
@@ -16,14 +16,15 @@
//
#include <QtCore/private/qglobal_p.h>
+#include <QtCore/qshareddata.h>
QT_BEGIN_NAMESPACE
-class QPenPrivate {
+class QPenPrivate : public QSharedData
+{
public:
QPenPrivate(const QBrush &brush, qreal width, Qt::PenStyle, Qt::PenCapStyle,
Qt::PenJoinStyle _joinStyle);
- QAtomicInt ref;
qreal width;
QBrush brush;
Qt::PenStyle style;
diff --git a/src/gui/painting/qpixellayout.cpp b/src/gui/painting/qpixellayout.cpp
index 025bb168f5..4f2f0ae13a 100644
--- a/src/gui/painting/qpixellayout.cpp
+++ b/src/gui/painting/qpixellayout.cpp
@@ -7,6 +7,7 @@
#include "qpixellayout_p.h"
#include "qrgba64_p.h"
#include <QtCore/private/qsimd_p.h>
+#include <QtGui/private/qcmyk_p.h>
QT_BEGIN_NAMESPACE
@@ -212,8 +213,7 @@ inline void QT_FASTCALL storePixel<QPixelLayout::BPP24>(uchar *dest, int index,
template <QPixelLayout::BPP bpp> static
inline uint QT_FASTCALL fetchPixel(const uchar *, int)
{
- Q_UNREACHABLE();
- return 0;
+ Q_UNREACHABLE_RETURN(0);
}
template <>
@@ -1188,10 +1188,12 @@ static const QRgba64 *QT_FASTCALL fetchRGBA64ToRGBA64PM(QRgba64 *buffer, const u
const QRgba64 *s = reinterpret_cast<const QRgba64 *>(src) + index;
#ifdef __SSE2__
for (int i = 0; i < count; ++i) {
+ const auto a = s[i].alpha();
__m128i vs = _mm_loadl_epi64((const __m128i *)(s + i));
__m128i va = _mm_shufflelo_epi16(vs, _MM_SHUFFLE(3, 3, 3, 3));
vs = multiplyAlpha65535(vs, va);
_mm_storel_epi64((__m128i *)(buffer + i), vs);
+ buffer[i].setAlpha(a);
}
#else
for (int i = 0; i < count; ++i)
@@ -1656,11 +1658,71 @@ static const QRgba64 *QT_FASTCALL fetchRGBA32FPMToRGBA64PM(QRgba64 *buffer, cons
return buffer;
}
+inline const uint *qt_convertCMYK8888ToARGB32PM(uint *buffer, const uint *src, int count)
+{
+ UNALIASED_CONVERSION_LOOP(buffer, src, count, [](uint s) {
+ const QColor color = QCmyk32::fromCmyk32(s).toColor();
+ return color.rgba();
+ });
+ return buffer;
+}
+
+static void QT_FASTCALL convertCMYK8888ToARGB32PM(uint *buffer, int count, const QList<QRgb> *)
+{
+ qt_convertCMYK8888ToARGB32PM(buffer, buffer, count);
+}
+
+static const QRgba64 *QT_FASTCALL convertCMYK8888ToToRGBA64PM(QRgba64 *buffer, const uint *src, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = QCmyk32::fromCmyk32(src[i]).toColor().rgba64();
+ return buffer;
+}
+
+static const uint *QT_FASTCALL fetchCMYK8888ToARGB32PM(uint *buffer, const uchar *src, int index, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ const uint *s = reinterpret_cast<const uint *>(src) + index;
+ for (int i = 0; i < count; ++i)
+ buffer[i] = QCmyk32::fromCmyk32(s[i]).toColor().rgba();
+ return buffer;
+}
+
+static const QRgba64 *QT_FASTCALL fetchCMYK8888ToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ const uint *s = reinterpret_cast<const uint *>(src) + index;
+ for (int i = 0; i < count; ++i)
+ buffer[i] = QCmyk32::fromCmyk32(s[i]).toColor().rgba64();
+ return buffer;
+}
+
+static void QT_FASTCALL storeCMYK8888FromARGB32PM(uchar *dest, const uint *src, int index, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ for (int i = 0; i < count; ++i) {
+ QColor c = qUnpremultiply(src[i]);
+ d[i] = QCmyk32::fromColor(c).toUint();
+ }
+}
+
+static void QT_FASTCALL storeCMYK8888FromRGB32(uchar *dest, const uint *src, int index, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ for (int i = 0; i < count; ++i) {
+ QColor c = src[i];
+ d[i] = QCmyk32::fromColor(c).toUint();
+ }
+}
+
// Note:
// convertToArgb32() assumes that no color channel is less than 4 bits.
// storeRGBFromARGB32PM() assumes that no color channel is more than 8 bits.
// QImage::rgbSwapped() assumes that the red and blue color channels have the same number of bits.
-QPixelLayout qPixelLayouts[QImage::NImageFormats] = {
+QPixelLayout qPixelLayouts[] = {
{ false, false, QPixelLayout::BPPNone, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }, // Format_Invalid
{ false, false, QPixelLayout::BPP1MSB, nullptr,
convertIndexedToARGB32PM, convertIndexedTo<QRgba64>,
@@ -1778,9 +1840,13 @@ QPixelLayout qPixelLayouts[QImage::NImageFormats] = {
convertPassThrough, nullptr,
fetchRGB32FToRGB32, fetchRGBA32FPMToRGBA64PM,
storeRGB32FFromRGB32, storeRGB32FFromRGB32 }, // Format_RGBA32FPx4_Premultiplied
+ { false, false, QPixelLayout::BPP32, nullptr,
+ convertCMYK8888ToARGB32PM, convertCMYK8888ToToRGBA64PM,
+ fetchCMYK8888ToARGB32PM, fetchCMYK8888ToRGBA64PM,
+ storeCMYK8888FromARGB32PM, storeCMYK8888FromRGB32 }, // Format_CMYK8888
};
-static_assert(sizeof(qPixelLayouts) / sizeof(*qPixelLayouts) == QImage::NImageFormats);
+static_assert(std::size(qPixelLayouts) == QImage::NImageFormats);
static void QT_FASTCALL convertFromRgb64(uint *dest, const QRgba64 *src, int length)
{
@@ -1915,7 +1981,15 @@ static void QT_FASTCALL storeRGBA32FPMFromRGBA64PM(uchar *dest, const QRgba64 *s
d[i] = qConvertRgb64ToRgbaF32(src[i]);
}
-ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats] = {
+static void QT_FASTCALL storeCMYKFromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ for (int i = 0; i < count; ++i)
+ d[i] = QCmyk32::fromColor(QColor(src[i])).toUint();
+}
+
+ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[] = {
nullptr,
nullptr,
nullptr,
@@ -1952,8 +2026,11 @@ ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats] = {
storeRGBX32FFromRGBA64PM,
storeRGBA32FFromRGBA64PM,
storeRGBA32FPMFromRGBA64PM,
+ storeCMYKFromRGBA64PM,
};
+static_assert(std::size(qStoreFromRGBA64PM) == QImage::NImageFormats);
+
#if QT_CONFIG(raster_fp)
static void QT_FASTCALL convertToRgbaF32(QRgbaFloat32 *dest, const uint *src, int length)
{
@@ -1999,7 +2076,16 @@ static const QRgbaFloat32 * QT_FASTCALL convertRGB30ToRGBA32F(QRgbaFloat32 *buff
return buffer;
}
-ConvertToFPFunc qConvertToRGBA32F[QImage::NImageFormats] = {
+static const QRgbaFloat32 * QT_FASTCALL convertCMYKToRGBA32F(QRgbaFloat32 *buffer, const uint *src, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = QRgbaFloat32::fromArgb32(QCmyk32::fromCmyk32(src[i]).toColor().rgba());
+
+ return buffer;
+}
+
+ConvertToFPFunc qConvertToRGBA32F[] = {
nullptr,
convertIndexedTo<QRgbaFloat32>,
convertIndexedTo<QRgbaFloat32>,
@@ -2036,8 +2122,11 @@ ConvertToFPFunc qConvertToRGBA32F[QImage::NImageFormats] = {
nullptr,
nullptr,
nullptr,
+ convertCMYKToRGBA32F,
};
+static_assert(std::size(qConvertToRGBA32F) == QImage::NImageFormats);
+
static const QRgbaFloat32 *QT_FASTCALL fetchRGBX64ToRGBA32F(QRgbaFloat32 *buffer, const uchar *src, int index, int count,
const QList<QRgb> *, QDitherInfo *)
{
@@ -2102,7 +2191,17 @@ static const QRgbaFloat32 *QT_FASTCALL fetchRGBA32F(QRgbaFloat32 *, const uchar
return s;
}
-FetchAndConvertPixelsFuncFP qFetchToRGBA32F[QImage::NImageFormats] = {
+static const QRgbaFloat32 *QT_FASTCALL fetchCMYKToRGBA32F(QRgbaFloat32 *buffer, const uchar *src, int index, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ const uint *s = reinterpret_cast<const uint *>(src) + index;
+ for (int i = 0; i < count; ++i)
+ buffer[i] = QRgbaFloat32::fromArgb32(QCmyk32::fromCmyk32(s[i]).toColor().rgba());
+
+ return buffer;
+}
+
+FetchAndConvertPixelsFuncFP qFetchToRGBA32F[] = {
nullptr,
fetchIndexedToRGBA32F<QPixelLayout::BPP1MSB>,
fetchIndexedToRGBA32F<QPixelLayout::BPP1LSB>,
@@ -2139,8 +2238,11 @@ FetchAndConvertPixelsFuncFP qFetchToRGBA32F[QImage::NImageFormats] = {
fetchRGBA32F,
fetchRGBA32FToRGBA32F,
fetchRGBA32F,
+ fetchCMYKToRGBA32F,
};
+static_assert(std::size(qFetchToRGBA32F) == QImage::NImageFormats);
+
static void QT_FASTCALL convertFromRgba32f(uint *dest, const QRgbaFloat32 *src, int length)
{
for (int i = 0; i < length; ++i)
@@ -2227,7 +2329,7 @@ static void QT_FASTCALL storeRGBX16FFromRGBA32F(uchar *dest, const QRgbaFloat32
QRgbaFloat16 *d = reinterpret_cast<QRgbaFloat16 *>(dest) + index;
for (int i = 0; i < count; ++i) {
auto s = src[i].unpremultiplied();
- d[i] = QRgbaFloat16{ s.r, s.g, s.b, 1.0f };
+ d[i] = QRgbaFloat16{ qfloat16(s.r), qfloat16(s.g), qfloat16(s.b), qfloat16(1.0f) };
}
}
@@ -2237,7 +2339,7 @@ static void QT_FASTCALL storeRGBA16FFromRGBA32F(uchar *dest, const QRgbaFloat32
QRgbaFloat16 *d = reinterpret_cast<QRgbaFloat16 *>(dest) + index;
for (int i = 0; i < count; ++i) {
auto s = src[i].unpremultiplied();
- d[i] = QRgbaFloat16{ s.r, s.g, s.b, s.a };
+ d[i] = QRgbaFloat16{ qfloat16(s.r), qfloat16(s.g), qfloat16(s.b), qfloat16(s.a) };
}
}
@@ -2277,7 +2379,17 @@ static void QT_FASTCALL storeRGBA32FPMFromRGBA32F(uchar *dest, const QRgbaFloat3
}
}
-ConvertAndStorePixelsFuncFP qStoreFromRGBA32F[QImage::NImageFormats] = {
+static void QT_FASTCALL storeCMYKFromRGBA32F(uchar *dest, const QRgbaFloat32 *src, int index, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ for (int i = 0; i < count; ++i) {
+ // Yikes, this really needs enablers in QColor and friends
+ d[i] = QCmyk32::fromColor(QColor(src[i].toArgb32())).toUint();
+ }
+}
+
+ConvertAndStorePixelsFuncFP qStoreFromRGBA32F[] = {
nullptr,
nullptr,
nullptr,
@@ -2314,7 +2426,11 @@ ConvertAndStorePixelsFuncFP qStoreFromRGBA32F[QImage::NImageFormats] = {
storeRGBX32FFromRGBA32F,
storeRGBA32FFromRGBA32F,
storeRGBA32FPMFromRGBA32F,
+ storeCMYKFromRGBA32F,
};
+
+static_assert(std::size(qStoreFromRGBA32F) == QImage::NImageFormats);
+
#endif // QT_CONFIG(raster_fp)
QT_END_NAMESPACE
diff --git a/src/gui/painting/qpixellayout_p.h b/src/gui/painting/qpixellayout_p.h
index 45f6c15365..14f19f4e74 100644
--- a/src/gui/painting/qpixellayout_p.h
+++ b/src/gui/painting/qpixellayout_p.h
@@ -321,12 +321,12 @@ struct QPixelLayout
extern ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats];
#if QT_CONFIG(raster_fp)
-extern ConvertToFPFunc qConvertToRGBA32F[QImage::NImageFormats];
-extern FetchAndConvertPixelsFuncFP qFetchToRGBA32F[QImage::NImageFormats];
-extern ConvertAndStorePixelsFuncFP qStoreFromRGBA32F[QImage::NImageFormats];
+extern ConvertToFPFunc qConvertToRGBA32F[];
+extern FetchAndConvertPixelsFuncFP qFetchToRGBA32F[];
+extern ConvertAndStorePixelsFuncFP qStoreFromRGBA32F[];
#endif
-extern QPixelLayout qPixelLayouts[QImage::NImageFormats];
+extern QPixelLayout qPixelLayouts[];
extern MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3];
diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp
index befd3a4197..f7c4df40c8 100644
--- a/src/gui/painting/qplatformbackingstore.cpp
+++ b/src/gui/painting/qplatformbackingstore.cpp
@@ -37,6 +37,7 @@ struct QBackingstoreTextureInfo
{
void *source; // may be null
QRhiTexture *texture;
+ QRhiTexture *textureExtra;
QRect rect;
QRect clipRect;
QPlatformTextureList::Flags flags;
@@ -68,7 +69,7 @@ QPlatformTextureList::~QPlatformTextureList()
int QPlatformTextureList::count() const
{
Q_D(const QPlatformTextureList);
- return d->textures.count();
+ return d->textures.size();
}
QRhiTexture *QPlatformTextureList::texture(int index) const
@@ -77,6 +78,12 @@ QRhiTexture *QPlatformTextureList::texture(int index) const
return d->textures.at(index).texture;
}
+QRhiTexture *QPlatformTextureList::textureExtra(int index) const
+{
+ Q_D(const QPlatformTextureList);
+ return d->textures.at(index).textureExtra;
+}
+
void *QPlatformTextureList::source(int index)
{
Q_D(const QPlatformTextureList);
@@ -123,6 +130,22 @@ void QPlatformTextureList::appendTexture(void *source, QRhiTexture *texture, con
QBackingstoreTextureInfo bi;
bi.source = source;
bi.texture = texture;
+ bi.textureExtra = nullptr;
+ bi.rect = geometry;
+ bi.clipRect = clipRect;
+ bi.flags = flags;
+ d->textures.append(bi);
+}
+
+void QPlatformTextureList::appendTexture(void *source, QRhiTexture *textureLeft, QRhiTexture *textureRight, const QRect &geometry,
+ const QRect &clipRect, Flags flags)
+{
+ Q_D(QPlatformTextureList);
+
+ QBackingstoreTextureInfo bi;
+ bi.source = source;
+ bi.texture = textureLeft;
+ bi.textureExtra = textureRight;
bi.rect = geometry;
bi.clipRect = clipRect;
bi.flags = flags;
@@ -356,6 +379,7 @@ void QPlatformBackingStore::graphicsDeviceReportedLost()
return;
qWarning("Rhi backingstore: graphics device lost, attempting to reinitialize");
+ d_ptr->compositor.reset();
d_ptr->rhiSupport.reset();
d_ptr->rhiSupport.create();
if (!d_ptr->rhiSupport.rhi())
diff --git a/src/gui/painting/qplatformbackingstore.h b/src/gui/painting/qplatformbackingstore.h
index a1bae50555..1d55e9ab6a 100644
--- a/src/gui/painting/qplatformbackingstore.h
+++ b/src/gui/painting/qplatformbackingstore.h
@@ -23,7 +23,7 @@
QT_BEGIN_NAMESPACE
-Q_GUI_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcQpaBackingStore)
+Q_DECLARE_EXPORTED_LOGGING_CATEGORY(lcQpaBackingStore, Q_GUI_EXPORT)
class QRegion;
class QRect;
@@ -36,7 +36,6 @@ class QPlatformGraphicsBuffer;
class QRhi;
class QRhiTexture;
class QRhiResourceUpdateBatch;
-class QRhiSwapChain;
struct Q_GUI_EXPORT QPlatformBackingStoreRhiConfig
{
@@ -45,6 +44,7 @@ struct Q_GUI_EXPORT QPlatformBackingStoreRhiConfig
Metal,
Vulkan,
D3D11,
+ D3D12,
Null
};
@@ -66,22 +66,10 @@ struct Q_GUI_EXPORT QPlatformBackingStoreRhiConfig
bool isDebugLayerEnabled() const { return m_debugLayer; }
void setDebugLayer(bool enable) { m_debugLayer = enable; }
- QByteArrayList instanceExtensions() const { return m_instanceExtensions; }
- void setInstanceExtensions(const QByteArrayList &e) { m_instanceExtensions = e; }
-
- QByteArrayList instanceLayers() const { return m_instanceLayers; }
- void setInstanceLayers(const QByteArrayList &e) { m_instanceLayers = e; }
-
- QByteArrayList deviceExtensions() const { return m_deviceExtensions; }
- void setDeviceExtensions(const QByteArrayList &e) { m_deviceExtensions = e; }
-
private:
bool m_enable;
Api m_api = Null;
bool m_debugLayer = false;
- QByteArrayList m_instanceExtensions;
- QByteArrayList m_instanceLayers;
- QByteArrayList m_deviceExtensions;
friend bool operator==(const QPlatformBackingStoreRhiConfig &a, const QPlatformBackingStoreRhiConfig &b);
};
@@ -89,10 +77,7 @@ inline bool operator==(const QPlatformBackingStoreRhiConfig &a, const QPlatformB
{
return a.m_enable == b.m_enable
&& a.m_api == b.m_api
- && a.m_debugLayer == b.m_debugLayer
- && a.m_instanceExtensions == b.m_instanceExtensions
- && a.m_instanceLayers == b.m_instanceLayers
- && a.m_deviceExtensions == b.m_deviceExtensions;
+ && a.m_debugLayer == b.m_debugLayer;
}
inline bool operator!=(const QPlatformBackingStoreRhiConfig &a, const QPlatformBackingStoreRhiConfig &b)
@@ -108,7 +93,8 @@ public:
enum Flag {
StacksOnTop = 0x01,
TextureIsSrgb = 0x02,
- NeedsPremultipliedAlphaBlending = 0x04
+ NeedsPremultipliedAlphaBlending = 0x04,
+ MirrorVertically = 0x08
};
Q_DECLARE_FLAGS(Flags, Flag)
@@ -118,6 +104,7 @@ public:
int count() const;
bool isEmpty() const { return count() == 0; }
QRhiTexture *texture(int index) const;
+ QRhiTexture *textureExtra(int index) const;
QRect geometry(int index) const;
QRect clipRect(int index) const;
void *source(int index);
@@ -127,6 +114,9 @@ public:
void appendTexture(void *source, QRhiTexture *texture, const QRect &geometry,
const QRect &clipRect = QRect(), Flags flags = { });
+
+ void appendTexture(void *source, QRhiTexture *textureLeft, QRhiTexture *textureRight, const QRect &geometry,
+ const QRect &clipRect = QRect(), Flags flags = { });
void clear();
Q_SIGNALS:
@@ -183,7 +173,6 @@ public:
void setRhiConfig(const QPlatformBackingStoreRhiConfig &config);
QRhi *rhi() const;
- QRhiSwapChain *rhiSwapChain() const;
void surfaceAboutToBeDestroyed();
void graphicsDeviceReportedLost();
diff --git a/src/gui/painting/qpolygon.cpp b/src/gui/painting/qpolygon.cpp
index 99d67599ed..d615245eb4 100644
--- a/src/gui/painting/qpolygon.cpp
+++ b/src/gui/painting/qpolygon.cpp
@@ -419,12 +419,7 @@ QRect QPolygon::boundingRect() const
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QPolygon &a)
{
- QDebugStateSaver saver(dbg);
- dbg.nospace() << "QPolygon(";
- for (int i = 0; i < a.count(); ++i)
- dbg.nospace() << a.at(i);
- dbg.nospace() << ')';
- return dbg;
+ return QtPrivate::printSequentialContainer(dbg, "QPolygon", a);
}
#endif
@@ -702,13 +697,7 @@ QDataStream &operator>>(QDataStream &s, QPolygon &a)
QDataStream &operator<<(QDataStream &s, const QPolygonF &a)
{
- quint32 len = a.size();
- uint i;
-
- s << len;
- for (i = 0; i < len; ++i)
- s << a.at(i);
- return s;
+ return s << static_cast<const QList<QPointF> &>(a);
}
/*!
@@ -723,29 +712,14 @@ QDataStream &operator<<(QDataStream &s, const QPolygonF &a)
QDataStream &operator>>(QDataStream &s, QPolygonF &a)
{
- quint32 len;
- uint i;
-
- s >> len;
- a.reserve(a.size() + (int)len);
- QPointF p;
- for (i = 0; i < len; ++i) {
- s >> p;
- a.insert(i, p);
- }
- return s;
+ return s >> static_cast<QList<QPointF> &>(a);
}
#endif //QT_NO_DATASTREAM
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QPolygonF &a)
{
- QDebugStateSaver saver(dbg);
- dbg.nospace() << "QPolygonF(";
- for (int i = 0; i < a.count(); ++i)
- dbg.nospace() << a.at(i);
- dbg.nospace() << ')';
- return dbg;
+ return QtPrivate::printSequentialContainer(dbg, "QPolygonF", a);
}
#endif
diff --git a/src/gui/painting/qrasterbackingstore.cpp b/src/gui/painting/qrasterbackingstore.cpp
index c0df8088d4..3b3ef2fd2e 100644
--- a/src/gui/painting/qrasterbackingstore.cpp
+++ b/src/gui/painting/qrasterbackingstore.cpp
@@ -67,7 +67,7 @@ void QRasterBackingStore::beginPaint(const QRegion &region)
if (m_image.devicePixelRatio() != nativeWindowDevicePixelRatio || m_image.size() != effectiveBufferSize) {
m_image = QImage(effectiveBufferSize, format());
m_image.setDevicePixelRatio(nativeWindowDevicePixelRatio);
- if (m_image.format() == QImage::Format_ARGB32_Premultiplied)
+ if (m_image.hasAlphaChannel())
m_image.fill(Qt::transparent);
}
diff --git a/src/gui/painting/qrasterizer.cpp b/src/gui/painting/qrasterizer.cpp
index 8a3e46e58b..cd9d3ba4d0 100644
--- a/src/gui/painting/qrasterizer.cpp
+++ b/src/gui/painting/qrasterizer.cpp
@@ -1064,28 +1064,26 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width,
while (x <= leftMax) {
QScFixed excluded = 0;
- if (yFP <= iLeftFP)
+ if (yFP <= iLeftFP && rowBottomLeft > rowTop)
excluded += intersectPixelFP(x, rowTop, rowBottomLeft,
bottomLeftIntersectAf, topLeftIntersectAf,
topLeftSlopeFP, invTopLeftSlopeFP);
- if (yFP >= iLeftFP)
+ if (yFP >= iLeftFP && rowBottom > rowTopLeft)
excluded += intersectPixelFP(x, rowTopLeft, rowBottom,
topLeftIntersectBf, bottomLeftIntersectBf,
bottomLeftSlopeFP, invBottomLeftSlopeFP);
-
if (x >= rightMin) {
- if (yFP <= iRightFP)
+ if (yFP <= iRightFP && rowBottomRight > rowTop)
excluded += (rowBottomRight - rowTop) - intersectPixelFP(x, rowTop, rowBottomRight,
topRightIntersectAf, bottomRightIntersectAf,
topRightSlopeFP, invTopRightSlopeFP);
- if (yFP >= iRightFP)
+ if (yFP >= iRightFP && rowBottom > rowTopRight)
excluded += (rowBottom - rowTopRight) - intersectPixelFP(x, rowTopRight, rowBottom,
bottomRightIntersectBf, topRightIntersectBf,
bottomRightSlopeFP, invBottomRightSlopeFP);
}
- if (excluded > QScFixedFactor)
- excluded = excluded % QScFixedFactor;
+ Q_ASSERT(excluded >= 0 && excluded <= rowHeight);
QScFixed coverage = rowHeight - excluded;
buffer.addSpan(x, 1, QScFixedToInt(yFP),
QScFixedToInt(255 * coverage));
@@ -1098,17 +1096,16 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width,
}
while (x <= rightMax) {
QScFixed excluded = 0;
- if (yFP <= iRightFP)
+ if (yFP <= iRightFP && rowBottomRight > rowTop)
excluded += (rowBottomRight - rowTop) - intersectPixelFP(x, rowTop, rowBottomRight,
topRightIntersectAf, bottomRightIntersectAf,
topRightSlopeFP, invTopRightSlopeFP);
- if (yFP >= iRightFP)
+ if (yFP >= iRightFP && rowBottom > rowTopRight)
excluded += (rowBottom - rowTopRight) - intersectPixelFP(x, rowTopRight, rowBottom,
bottomRightIntersectBf, topRightIntersectBf,
bottomRightSlopeFP, invBottomRightSlopeFP);
- if (excluded > QScFixedFactor)
- excluded = excluded % QScFixedFactor;
+ Q_ASSERT(excluded >= 0 && excluded <= rowHeight);
QScFixed coverage = rowHeight - excluded;
buffer.addSpan(x, 1, QScFixedToInt(yFP),
QScFixedToInt(255 * coverage));
diff --git a/src/gui/painting/qregion.cpp b/src/gui/painting/qregion.cpp
index 7d912b29e5..8b712ee46d 100644
--- a/src/gui/painting/qregion.cpp
+++ b/src/gui/painting/qregion.cpp
@@ -51,7 +51,7 @@ QT_BEGIN_NAMESPACE
contains() a QPoint or QRect. The bounding rectangle can be found
with boundingRect().
- Iteration over the region (with begin(), end(), or C++11
+ Iteration over the region (with begin(), end(), or
ranged-for loops) gives a decomposition of the region into
rectangles.
@@ -636,7 +636,7 @@ bool QRegion::intersects(const QRegion &region) const
*/
-#if !defined (Q_OS_UNIX) && !defined (Q_OS_WIN) || defined(Q_CLANG_QDOC)
+#if !defined (Q_OS_UNIX) && !defined (Q_OS_WIN) || defined(Q_QDOC)
/*
\overload
\since 4.4
@@ -3191,8 +3191,7 @@ static void CreateETandAET(int count, const QPoint *pts,
int iSLLBlock = 0;
int dy;
- if (count < 2)
- return;
+ Q_ASSERT(count > 1);
/*
* initialize the Active Edge Table
@@ -3538,7 +3537,7 @@ static QRegionPrivate *PolygonRegion(const QPoint *Pts, int Count, int rule)
POINTBLOCK *tmpPtBlock;
int numFullPtBlocks = 0;
- Q_ASSUME(Count > 1);
+ Q_ASSERT(Count > 1);
region = new QRegionPrivate;
@@ -3818,7 +3817,7 @@ QRegion::QRegion(const QRect &r, RegionType t)
QRegion::QRegion(const QPolygon &a, Qt::FillRule fillRule)
{
- if (a.count() > 2) {
+ if (a.size() > 2) {
QRegionPrivate *qt_rgn = PolygonRegion(a.constData(), a.size(),
fillRule == Qt::WindingFill ? WindingRule : EvenOddRule);
if (qt_rgn) {
diff --git a/src/gui/painting/qregion.h b/src/gui/painting/qregion.h
index c946287411..b0051b6067 100644
--- a/src/gui/painting/qregion.h
+++ b/src/gui/painting/qregion.h
@@ -34,7 +34,7 @@ public:
QRegion(const QPolygon &pa, Qt::FillRule fillRule = Qt::OddEvenFill);
QRegion(const QRegion &region);
QRegion(QRegion &&other) noexcept
- : d(qExchange(other.d, const_cast<QRegionData*>(&shared_empty))) {}
+ : d(std::exchange(other.d, const_cast<QRegionData*>(&shared_empty))) {}
QRegion(const QBitmap &bitmap);
~QRegion();
QRegion &operator=(const QRegion &);
diff --git a/src/gui/painting/qrgba64_p.h b/src/gui/painting/qrgba64_p.h
index e848ae0148..ae8b6fd8cb 100644
--- a/src/gui/painting/qrgba64_p.h
+++ b/src/gui/painting/qrgba64_p.h
@@ -81,18 +81,22 @@ static inline QRgba64 multiplyAlpha65535(QRgba64 rgba64, uint alpha65535)
#endif
}
+#if defined(__SSE2__) || defined(__ARM_NEON__)
template<typename T>
static inline T Q_DECL_VECTORCALL multiplyAlpha255(T rgba64, uint alpha255)
{
-#if defined(__SSE2__) || defined(__ARM_NEON__)
return multiplyAlpha65535(rgba64, alpha255 * 257);
+}
#else
+template<typename T>
+static inline T multiplyAlpha255(T rgba64, uint alpha255)
+{
return QRgba64::fromRgba64(qt_div_255(rgba64.red() * alpha255),
qt_div_255(rgba64.green() * alpha255),
qt_div_255(rgba64.blue() * alpha255),
qt_div_255(rgba64.alpha() * alpha255));
-#endif
}
+#endif
#if defined __SSE2__
static inline __m128i Q_DECL_VECTORCALL interpolate255(__m128i x, uint alpha1, __m128i y, uint alpha2)
diff --git a/src/gui/painting/qrgbafloat.h b/src/gui/painting/qrgbafloat.h
index a7cd2d70d3..da74328f71 100644
--- a/src/gui/painting/qrgbafloat.h
+++ b/src/gui/painting/qrgbafloat.h
@@ -19,7 +19,13 @@ class alignas(sizeof(F) * 4) QRgbaFloat
static_assert(std::is_same<F, qfloat16>::value || std::is_same<F, float>::value);
public:
using Type = F;
+#if defined(__AVX512FP16__) && QFLOAT16_IS_NATIVE
+ // AVX512FP16 has multiplication instructions
+ using FastType = F;
+#else
+ // use FP32 for multiplications
using FastType = float;
+#endif
F r;
F g;
F b;
@@ -28,21 +34,23 @@ public:
static constexpr
QRgbaFloat fromRgba64(quint16 red, quint16 green, quint16 blue, quint16 alpha)
{
+ constexpr FastType scale = FastType(1.0f / 65535.0f);
return QRgbaFloat{
- red * (1.0f / 65535.0f),
- green * (1.0f / 65535.0f),
- blue * (1.0f / 65535.0f),
- alpha * (1.0f / 65535.0f) };
+ F(red * scale),
+ F(green * scale),
+ F(blue * scale),
+ F(alpha * scale) };
}
static constexpr
QRgbaFloat fromRgba(quint8 red, quint8 green, quint8 blue, quint8 alpha)
{
+ constexpr FastType scale = FastType(1.0f / 255.0f);
return QRgbaFloat{
- red * (1.0f / 255.0f),
- green * (1.0f / 255.0f),
- blue * (1.0f / 255.0f),
- alpha * (1.0f / 255.0f) };
+ F(red * scale),
+ F(green * scale),
+ F(blue * scale),
+ F(alpha * scale) };
}
static constexpr
QRgbaFloat fromArgb32(uint rgb)
@@ -50,36 +58,36 @@ public:
return fromRgba(quint8(rgb >> 16), quint8(rgb >> 8), quint8(rgb), quint8(rgb >> 24));
}
- constexpr bool isOpaque() const { return a >= 1.0f; }
- constexpr bool isTransparent() const { return a <= 0.0f; }
-
- constexpr FastType red() const { return r; }
- constexpr FastType green() const { return g; }
- constexpr FastType blue() const { return b; }
- constexpr FastType alpha() const { return a; }
- void setRed(FastType _red) { r = _red; }
- void setGreen(FastType _green) { g = _green; }
- void setBlue(FastType _blue) { b = _blue; }
- void setAlpha(FastType _alpha) { a = _alpha; }
-
- constexpr FastType redNormalized() const { return std::clamp(static_cast<FastType>(r), 0.0f, 1.0f); }
- constexpr FastType greenNormalized() const { return std::clamp(static_cast<FastType>(g), 0.0f, 1.0f); }
- constexpr FastType blueNormalized() const { return std::clamp(static_cast<FastType>(b), 0.0f, 1.0f); }
- constexpr FastType alphaNormalized() const { return std::clamp(static_cast<FastType>(a), 0.0f, 1.0f); }
-
- constexpr quint8 red8() const { return std::lround(redNormalized() * 255.0f); }
- constexpr quint8 green8() const { return std::lround(greenNormalized() * 255.0f); }
- constexpr quint8 blue8() const { return std::lround(blueNormalized() * 255.0f); }
- constexpr quint8 alpha8() const { return std::lround(alphaNormalized() * 255.0f); }
+ constexpr bool isOpaque() const { return a >= FastType(1.0f); }
+ constexpr bool isTransparent() const { return a <= FastType(0.0f); }
+
+ constexpr float red() const { return r; }
+ constexpr float green() const { return g; }
+ constexpr float blue() const { return b; }
+ constexpr float alpha() const { return a; }
+ void setRed(float _red) { r = F(_red); }
+ void setGreen(float _green) { g = F(_green); }
+ void setBlue(float _blue) { b = F(_blue); }
+ void setAlpha(float _alpha) { a = F(_alpha); }
+
+ constexpr float redNormalized() const { return clamp01(r); }
+ constexpr float greenNormalized() const { return clamp01(g); }
+ constexpr float blueNormalized() const { return clamp01(b); }
+ constexpr float alphaNormalized() const { return clamp01(a); }
+
+ constexpr quint8 red8() const { return qRound(redNormalized() * FastType(255.0f)); }
+ constexpr quint8 green8() const { return qRound(greenNormalized() * FastType(255.0f)); }
+ constexpr quint8 blue8() const { return qRound(blueNormalized() * FastType(255.0f)); }
+ constexpr quint8 alpha8() const { return qRound(alphaNormalized() * FastType(255.0f)); }
constexpr uint toArgb32() const
{
return uint((alpha8() << 24) | (red8() << 16) | (green8() << 8) | blue8());
}
- constexpr quint16 red16() const { return std::lround(redNormalized() * 65535.0f); }
- constexpr quint16 green16() const { return std::lround(greenNormalized() * 65535.0f); }
- constexpr quint16 blue16() const { return std::lround(blueNormalized() * 65535.0f); }
- constexpr quint16 alpha16() const { return std::lround(alphaNormalized() * 65535.0f); }
+ constexpr quint16 red16() const { return qRound(redNormalized() * FastType(65535.0f)); }
+ constexpr quint16 green16() const { return qRound(greenNormalized() * FastType(65535.0f)); }
+ constexpr quint16 blue16() const { return qRound(blueNormalized() * FastType(65535.0f)); }
+ constexpr quint16 alpha16() const { return qRound(alphaNormalized() * FastType(65535.0f)); }
constexpr Q_ALWAYS_INLINE QRgbaFloat premultiplied() const
{
@@ -87,12 +95,12 @@ public:
}
constexpr Q_ALWAYS_INLINE QRgbaFloat unpremultiplied() const
{
- if (a <= 0.0f)
- return QRgbaFloat{0.0f, 0.0f, 0.0f, 0.0f};
- if (a >= 1.0f)
+ if (a <= F{0.0f})
+ return QRgbaFloat{}; // default-initialization: zeroes
+ if (a >= F{1.0f})
return *this;
const FastType ia = 1.0f / a;
- return QRgbaFloat{r * ia, g * ia, b * ia, a};
+ return QRgbaFloat{F(r * ia), F(g * ia), F(b * ia), F(a)};
}
constexpr bool operator==(QRgbaFloat f) const
{
@@ -102,6 +110,12 @@ public:
{
return !(*this == f);
}
+
+private:
+ constexpr static FastType clamp01(Type f)
+ {
+ return std::clamp(FastType(f), FastType(0.0f), FastType(1.0f));
+ }
};
typedef QRgbaFloat<qfloat16> QRgbaFloat16;
diff --git a/src/gui/painting/qrgbafloat.qdoc b/src/gui/painting/qrgbafloat.qdoc
index f41064df38..3ec0d209f4 100644
--- a/src/gui/painting/qrgbafloat.qdoc
+++ b/src/gui/painting/qrgbafloat.qdoc
@@ -33,7 +33,7 @@
*/
/*!
- \fn template<typename F> QRgbaFloat<F>::fromRgba64(quint16 r, quint16 g, quint16 b, quint16 a)
+ \fn template<typename F> QRgbaFloat QRgbaFloat<F>::fromRgba64(quint16 red, quint16 green, quint16 blue, quint16 alpha)
Constructs a QRgbaFloat value from the four 16-bit integer color channels \a red, \a green, \a blue and \a alpha.
@@ -41,7 +41,7 @@
*/
/*!
- \fn template<typename F> QRgbaFloat<F>::fromRgba(quint8 red, quint8 green, quint8 blue, quint8 alpha)
+ \fn template<typename F> QRgbaFloat QRgbaFloat<F>::fromRgba(quint8 red, quint8 green, quint8 blue, quint8 alpha)
Constructs a QRgbaFloat value from the four 8-bit color channels \a red, \a green, \a blue and \a alpha.
@@ -49,7 +49,7 @@
*/
/*!
- \fn template<typename F> QRgbaFloat<F>::fromArgb32(uint rgb)
+ \fn template<typename F> QRgbaFloat QRgbaFloat<F>::fromArgb32(uint rgb)
Constructs a QRgbaFloat value from the 32bit ARGB value \a rgb.
@@ -81,7 +81,7 @@
*/
/*!
- \fn template<typename F> void QRgbaFloat<F>::setRed(QRgbaFloat::FastType red)
+ \fn template<typename F> void QRgbaFloat<F>::setRed(float red)
Sets the red color component of this color to \a red.
@@ -97,7 +97,7 @@
*/
/*!
- \fn template<typename F> void QRgbaFloat<F>::setGreen(QRgbaFloat::FastType green)
+ \fn template<typename F> void QRgbaFloat<F>::setGreen(float green)
Sets the green color component of this color to \a green.
@@ -113,7 +113,7 @@
*/
/*!
- \fn template<typename F> void QRgbaFloat<F>::setBlue(QRgbaFloat::FastType blue)
+ \fn template<typename F> void QRgbaFloat<F>::setBlue(float blue)
Sets the blue color component of this color to \a blue.
@@ -129,7 +129,7 @@
*/
/*!
- \fn template<typename F> void QRgbaFloat<F>::setAlpha(QRgbaFloat::FastType alpha)
+ \fn template<typename F> void QRgbaFloat<F>::setAlpha(float alpha)
Sets the alpha of this color to \a alpha.
diff --git a/src/gui/painting/qrhibackingstore.cpp b/src/gui/painting/qrhibackingstore.cpp
index fd7045e3f6..586dfb44a4 100644
--- a/src/gui/painting/qrhibackingstore.cpp
+++ b/src/gui/painting/qrhibackingstore.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qrhibackingstore_p.h"
+#include <private/qimage_p.h>
QT_BEGIN_NAMESPACE
@@ -14,17 +15,24 @@ QRhiBackingStore::~QRhiBackingStore()
{
}
-void QRhiBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
+void QRhiBackingStore::flush(QWindow *flushedWindow, const QRegion &region, const QPoint &offset)
{
Q_UNUSED(region);
Q_UNUSED(offset);
- if (window != this->window())
+ if (flushedWindow->surfaceType() != window()->surfaceType()) {
+ qWarning() << "Cannot flush child window" << flushedWindow
+ << "with surface type" << flushedWindow->surfaceType() << ";"
+ << "Must match" << window()->surfaceType() << "of" << window();
+
+ // FIXME: Support different surface types by not tying the
+ // RHI config to the backing store itself (per window config).
return;
+ }
if (!rhi()) {
QPlatformBackingStoreRhiConfig rhiConfig;
- switch (window->surfaceType()) {
+ switch (window()->surfaceType()) {
case QSurface::OpenGLSurface:
rhiConfig.setApi(QPlatformBackingStoreRhiConfig::OpenGL);
break;
@@ -40,7 +48,21 @@ void QRhiBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
static QPlatformTextureList emptyTextureList;
bool translucentBackground = m_image.hasAlphaChannel();
- rhiFlush(window, window->devicePixelRatio(), region, offset, &emptyTextureList, translucentBackground);
+ rhiFlush(flushedWindow, flushedWindow->devicePixelRatio(),
+ region, offset, &emptyTextureList, translucentBackground);
+}
+
+QImage::Format QRhiBackingStore::format() const
+{
+ QImage::Format fmt = QRasterBackingStore::format();
+
+ // With render-to-texture widgets and QRhi-based flushing the backingstore
+ // image must have an alpha channel. Hence upgrading the format. Matches
+ // what other platforms (Windows, xcb) do.
+ if (QImage::toPixelFormat(fmt).alphaUsage() != QPixelFormat::UsesAlpha)
+ fmt = qt_maybeDataCompatibleAlphaVersion(fmt);
+
+ return fmt;
}
QT_END_NAMESPACE
diff --git a/src/gui/painting/qrhibackingstore_p.h b/src/gui/painting/qrhibackingstore_p.h
index 95778fa74c..f222db860f 100644
--- a/src/gui/painting/qrhibackingstore_p.h
+++ b/src/gui/painting/qrhibackingstore_p.h
@@ -26,6 +26,7 @@ public:
~QRhiBackingStore();
void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;
+ QImage::Format format() const override;
};
QT_END_NAMESPACE
diff --git a/src/gui/painting/qt_attribution.json b/src/gui/painting/qt_attribution.json
index e2326a56c1..33ed2fd5c7 100644
--- a/src/gui/painting/qt_attribution.json
+++ b/src/gui/painting/qt_attribution.json
@@ -4,13 +4,13 @@
"Name": "Anti-aliasing rasterizer from FreeType 2",
"QDocModule": "qtgui",
"QtUsage": "Used in Qt GUI.",
- "Path": "qgrayraster.c",
+ "Files": "qgrayraster.c",
"Description": "FreeType is a freely available software library to render fonts.",
"Homepage": "http://www.freetype.org",
"License": "Freetype Project License or GNU General Public License v2.0 only",
- "LicenseId": "FTL or GPL-2.0",
- "LicenseFile": "../../3rdparty/freetype/docs/LICENSE.TXT",
+ "LicenseId": "FTL OR GPL-2.0-only",
+ "LicenseFile": "../../3rdparty/freetype/LICENSE.txt",
"Copyright": "Copyright 2000-2016 by David Turner, Robert Wilhelm, and Werner Lemberg."
},
{
@@ -24,9 +24,9 @@
"LicenseId": "BSD-2-Clause AND Imlib2",
"License": "BSD 2-clause \"Simplified\" License and Imlib2 License",
"LicenseFile": "QIMAGETRANSFORM_LICENSE.txt",
- "Copyright": "Copyright (C) 2004, 2005 Daniel M. Duley.
- (C) Carsten Haitzler and various contributors.
- (C) Willem Monsuwe <willem@stack.nl>"
+ "Copyright": ["Copyright (C) 2004, 2005 Daniel M. Duley.",
+ "(C) Carsten Haitzler and various contributors.",
+ "(C) Willem Monsuwe <willem@stack.nl>"]
},
{
"Id": "xserverhelper",
@@ -40,7 +40,7 @@
"License": "X11 License and Historical Permission Notice and Disclaimer",
"LicenseId": "X11 AND HPND",
"LicenseFile": "XCONSORTIUM_LICENSE.txt",
- "Copyright": "Copyright (c) 1987, 1988 X Consortium
-Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts."
+ "Copyright": ["Copyright (c) 1987, 1988 X Consortium",
+ "Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts."]
}
]
diff --git a/src/gui/painting/qt_mips_asm_dsp_p.h b/src/gui/painting/qt_mips_asm_dsp_p.h
index 0b54b88934..6b33285f32 100644
--- a/src/gui/painting/qt_mips_asm_dsp_p.h
+++ b/src/gui/painting/qt_mips_asm_dsp_p.h
@@ -19,7 +19,7 @@
#pragma qt_sync_stop_processing
#endif
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
#define zero $0
#define AT $1
#define v0 $2
diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp
index 215eb75e32..70d70e342a 100644
--- a/src/gui/painting/qtextureglyphcache.cpp
+++ b/src/gui/painting/qtextureglyphcache.cpp
@@ -57,13 +57,14 @@ int QTextureGlyphCache::calculateSubPixelPositionCount(glyph_t glyph) const
}
bool QTextureGlyphCache::populate(QFontEngine *fontEngine,
- int numGlyphs,
+ qsizetype numGlyphs,
const glyph_t *glyphs,
const QFixedPoint *positions,
- QPainter::RenderHints renderHints)
+ QPainter::RenderHints renderHints,
+ bool includeGlyphCacheScale)
{
#ifdef CACHE_DEBUG
- printf("Populating with %d glyphs\n", numGlyphs);
+ printf("Populating with %lld glyphs\n", static_cast<long long>(numGlyphs));
qDebug() << " -> current transformation: " << m_transform;
#endif
@@ -78,7 +79,7 @@ bool QTextureGlyphCache::populate(QFontEngine *fontEngine,
if (!supportsSubPixelPositions) {
fontEngine->m_subPixelPositionCount = 1;
} else {
- int i = 0;
+ qsizetype i = 0;
while (fontEngine->m_subPixelPositionCount == 0 && i < numGlyphs)
fontEngine->m_subPixelPositionCount = calculateSubPixelPositionCount(glyphs[i++]);
}
@@ -89,16 +90,23 @@ bool QTextureGlyphCache::populate(QFontEngine *fontEngine,
m_cy = padding;
}
+ qreal glyphCacheScaleX = transform().m11();
+ qreal glyphCacheScaleY = transform().m22();
+
QHash<GlyphAndSubPixelPosition, Coord> listItemCoordinates;
int rowHeight = 0;
// check each glyph for its metrics and get the required rowHeight.
- for (int i=0; i < numGlyphs; ++i) {
+ for (qsizetype i = 0; i < numGlyphs; ++i) {
const glyph_t glyph = glyphs[i];
QFixedPoint subPixelPosition;
if (supportsSubPixelPositions) {
QFixedPoint pos = positions != nullptr ? positions[i] : QFixedPoint();
+ if (includeGlyphCacheScale) {
+ pos = QFixedPoint(QFixed::fromReal(pos.x.toReal() * glyphCacheScaleX),
+ QFixed::fromReal(pos.y.toReal() * glyphCacheScaleY));
+ }
subPixelPosition = fontEngine->subPixelPositionFor(pos);
if (!verticalSubPixelPositions)
subPixelPosition.y = 0;
diff --git a/src/gui/painting/qtextureglyphcache_p.h b/src/gui/painting/qtextureglyphcache_p.h
index bf6b07f36f..c0218b3ace 100644
--- a/src/gui/painting/qtextureglyphcache_p.h
+++ b/src/gui/painting/qtextureglyphcache_p.h
@@ -75,10 +75,11 @@ public:
};
bool populate(QFontEngine *fontEngine,
- int numGlyphs,
+ qsizetype numGlyphs,
const glyph_t *glyphs,
const QFixedPoint *positions,
- QPainter::RenderHints renderHints = QPainter::RenderHints());
+ QPainter::RenderHints renderHints = QPainter::RenderHints(),
+ bool includeGlyphCacheScale = false);
bool hasPendingGlyphs() const { return !m_pendingGlyphs.isEmpty(); }
void fillInPendingGlyphs();
diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp
index 5fa5d04e54..df57d2c190 100644
--- a/src/gui/painting/qtransform.cpp
+++ b/src/gui/painting/qtransform.cpp
@@ -54,7 +54,7 @@ static void nanWarning(const char *func)
if (t == TxProject) { \
qreal w = (m_matrix[0][2] * FX_ + m_matrix[1][2] * FY_ + m_matrix[2][2]); \
if (w < qreal(Q_NEAR_CLIP)) w = qreal(Q_NEAR_CLIP); \
- w = 1./w; \
+ w = qreal(1.)/w; \
nx *= w; \
ny *= w; \
} \
@@ -358,7 +358,7 @@ QTransform &QTransform::translate(qreal dx, qreal dy)
if (dx == 0 && dy == 0)
return *this;
#ifndef QT_NO_DEBUG
- if (qIsNaN(dx) | qIsNaN(dy)) {
+ if (qIsNaN(dx) || qIsNaN(dy)) {
nanWarning("translate");
return *this;
}
@@ -401,7 +401,7 @@ QTransform &QTransform::translate(qreal dx, qreal dy)
QTransform QTransform::fromTranslate(qreal dx, qreal dy)
{
#ifndef QT_NO_DEBUG
- if (qIsNaN(dx) | qIsNaN(dy)) {
+ if (qIsNaN(dx) || qIsNaN(dy)) {
nanWarning("fromTranslate");
return QTransform();
}
@@ -426,7 +426,7 @@ QTransform & QTransform::scale(qreal sx, qreal sy)
if (sx == 1 && sy == 1)
return *this;
#ifndef QT_NO_DEBUG
- if (qIsNaN(sx) | qIsNaN(sy)) {
+ if (qIsNaN(sx) || qIsNaN(sy)) {
nanWarning("scale");
return *this;
}
@@ -467,7 +467,7 @@ QTransform & QTransform::scale(qreal sx, qreal sy)
QTransform QTransform::fromScale(qreal sx, qreal sy)
{
#ifndef QT_NO_DEBUG
- if (qIsNaN(sx) | qIsNaN(sy)) {
+ if (qIsNaN(sx) || qIsNaN(sy)) {
nanWarning("fromScale");
return QTransform();
}
@@ -492,7 +492,7 @@ QTransform & QTransform::shear(qreal sh, qreal sv)
if (sh == 0 && sv == 0)
return *this;
#ifndef QT_NO_DEBUG
- if (qIsNaN(sh) | qIsNaN(sv)) {
+ if (qIsNaN(sh) || qIsNaN(sv)) {
nanWarning("shear");
return *this;
}
@@ -533,28 +533,33 @@ QTransform & QTransform::shear(qreal sh, qreal sv)
return *this;
}
-const qreal inv_dist_to_plane = 1. / 1024.;
-
/*!
- \fn QTransform &QTransform::rotate(qreal angle, Qt::Axis axis)
+ \since 6.5
- Rotates the coordinate system counterclockwise by the given \a angle
- about the specified \a axis and returns a reference to the matrix.
+ Rotates the coordinate system counterclockwise by the given angle \a a
+ about the specified \a axis at distance \a distanceToPlane from the
+ screen and returns a reference to the matrix.
+//! [transform-rotate-note]
Note that if you apply a QTransform to a point defined in widget
coordinates, the direction of the rotation will be clockwise
because the y-axis points downwards.
The angle is specified in degrees.
+//! [transform-rotate-note]
+
+ If \a distanceToPlane is zero, it will be ignored. This is suitable
+ for implementing orthographic projections where the z coordinate should
+ be dropped rather than projected.
\sa setMatrix()
*/
-QTransform & QTransform::rotate(qreal a, Qt::Axis axis)
+QTransform & QTransform::rotate(qreal a, Qt::Axis axis, qreal distanceToPlane)
{
if (a == 0)
return *this;
#ifndef QT_NO_DEBUG
- if (qIsNaN(a)) {
+ if (qIsNaN(a) || qIsNaN(distanceToPlane)) {
nanWarning("rotate");
return *this;
}
@@ -617,13 +622,16 @@ QTransform & QTransform::rotate(qreal a, Qt::Axis axis)
if (m_dirty < TxRotate)
m_dirty = TxRotate;
} else {
+ if (!qIsNull(distanceToPlane))
+ sina /= distanceToPlane;
+
QTransform result;
if (axis == Qt::YAxis) {
result.m_matrix[0][0] = cosa;
- result.m_matrix[0][2] = -sina * inv_dist_to_plane;
+ result.m_matrix[0][2] = -sina;
} else {
result.m_matrix[1][1] = cosa;
- result.m_matrix[1][2] = -sina * inv_dist_to_plane;
+ result.m_matrix[1][2] = -sina;
}
result.m_type = TxProject;
*this = result * *this;
@@ -632,24 +640,49 @@ QTransform & QTransform::rotate(qreal a, Qt::Axis axis)
return *this;
}
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+/*!
+ \overload
+
+ Rotates the coordinate system counterclockwise by the given angle \a a
+ about the specified \a axis at distance 1024.0 from the screen and
+ returns a reference to the matrix.
+
+ \include qtransform.cpp transform-rotate-note
+
+ \sa setMatrix
+*/
+QTransform &QTransform::rotate(qreal a, Qt::Axis axis)
+{
+ return rotate(a, axis, 1024.0);
+}
+#endif
+
/*!
- \fn QTransform & QTransform::rotateRadians(qreal angle, Qt::Axis axis)
+ \since 6.5
- Rotates the coordinate system counterclockwise by the given \a angle
- about the specified \a axis and returns a reference to the matrix.
+ Rotates the coordinate system counterclockwise by the given angle \a a
+ about the specified \a axis at distance \a distanceToPlane from the
+ screen and returns a reference to the matrix.
+//! [transform-rotate-radians-note]
Note that if you apply a QTransform to a point defined in widget
coordinates, the direction of the rotation will be clockwise
because the y-axis points downwards.
The angle is specified in radians.
+//! [transform-rotate-radians-note]
+
+ If \a distanceToPlane is zero, it will be ignored. This is suitable
+ for implementing orthographic projections where the z coordinate should
+ be dropped rather than projected.
\sa setMatrix()
*/
-QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis)
+QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis, qreal distanceToPlane)
{
#ifndef QT_NO_DEBUG
- if (qIsNaN(a)) {
+ if (qIsNaN(a) || qIsNaN(distanceToPlane)) {
nanWarning("rotateRadians");
return *this;
}
@@ -700,13 +733,16 @@ QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis)
if (m_dirty < TxRotate)
m_dirty = TxRotate;
} else {
+ if (!qIsNull(distanceToPlane))
+ sina /= distanceToPlane;
+
QTransform result;
if (axis == Qt::YAxis) {
result.m_matrix[0][0] = cosa;
- result.m_matrix[0][2] = -sina * inv_dist_to_plane;
+ result.m_matrix[0][2] = -sina;
} else {
result.m_matrix[1][1] = cosa;
- result.m_matrix[1][2] = -sina * inv_dist_to_plane;
+ result.m_matrix[1][2] = -sina;
}
result.m_type = TxProject;
*this = result * *this;
@@ -714,6 +750,24 @@ QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis)
return *this;
}
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+/*!
+ \overload
+
+ Rotates the coordinate system counterclockwise by the given angle \a a
+ about the specified \a axis at distance 1024.0 from the screen and
+ returns a reference to the matrix.
+
+ \include qtransform.cpp transform-rotate-radians-note
+
+ \sa setMatrix()
+*/
+QTransform &QTransform::rotateRadians(qreal a, Qt::Axis axis)
+{
+ return rotateRadians(a, axis, 1024.0);
+}
+#endif
+
/*!
\fn bool QTransform::operator==(const QTransform &matrix) const
Returns \c true if this matrix is equal to the given \a matrix,
@@ -1091,30 +1145,8 @@ QPoint QTransform::map(const QPoint &p) const
qreal x = 0, y = 0;
TransformationType t = inline_type();
- switch(t) {
- case TxNone:
- x = fx;
- y = fy;
- break;
- case TxTranslate:
- x = fx + m_matrix[2][0];
- y = fy + m_matrix[2][1];
- break;
- case TxScale:
- x = m_matrix[0][0] * fx + m_matrix[2][0];
- y = m_matrix[1][1] * fy + m_matrix[2][1];
- break;
- case TxRotate:
- case TxShear:
- case TxProject:
- x = m_matrix[0][0] * fx + m_matrix[1][0] * fy + m_matrix[2][0];
- y = m_matrix[0][1] * fx + m_matrix[1][1] * fy + m_matrix[2][1];
- if (t == TxProject) {
- qreal w = 1./(m_matrix[0][2] * fx + m_matrix[1][2] * fy + m_matrix[2][2]);
- x *= w;
- y *= w;
- }
- }
+ MAP(fx, fy, x, y);
+
return QPoint(qRound(x), qRound(y));
}
@@ -1142,30 +1174,8 @@ QPointF QTransform::map(const QPointF &p) const
qreal x = 0, y = 0;
TransformationType t = inline_type();
- switch(t) {
- case TxNone:
- x = fx;
- y = fy;
- break;
- case TxTranslate:
- x = fx + m_matrix[2][0];
- y = fy + m_matrix[2][1];
- break;
- case TxScale:
- x = m_matrix[0][0] * fx + m_matrix[2][0];
- y = m_matrix[1][1] * fy + m_matrix[2][1];
- break;
- case TxRotate:
- case TxShear:
- case TxProject:
- x = m_matrix[0][0] * fx + m_matrix[1][0] * fy + m_matrix[2][0];
- y = m_matrix[0][1] * fx + m_matrix[1][1] * fy + m_matrix[2][1];
- if (t == TxProject) {
- qreal w = 1./(m_matrix[0][2] * fx + m_matrix[1][2] * fy + m_matrix[2][2]);
- x *= w;
- y *= w;
- }
- }
+ MAP(fx, fy, x, y);
+
return QPointF(x, y);
}
@@ -1213,41 +1223,9 @@ QLine QTransform::map(const QLine &l) const
qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0;
TransformationType t = inline_type();
- switch(t) {
- case TxNone:
- x1 = fx1;
- y1 = fy1;
- x2 = fx2;
- y2 = fy2;
- break;
- case TxTranslate:
- x1 = fx1 + m_matrix[2][0];
- y1 = fy1 + m_matrix[2][1];
- x2 = fx2 + m_matrix[2][0];
- y2 = fy2 + m_matrix[2][1];
- break;
- case TxScale:
- x1 = m_matrix[0][0] * fx1 + m_matrix[2][0];
- y1 = m_matrix[1][1] * fy1 + m_matrix[2][1];
- x2 = m_matrix[0][0] * fx2 + m_matrix[2][0];
- y2 = m_matrix[1][1] * fy2 + m_matrix[2][1];
- break;
- case TxRotate:
- case TxShear:
- case TxProject:
- x1 = m_matrix[0][0] * fx1 + m_matrix[1][0] * fy1 + m_matrix[2][0];
- y1 = m_matrix[0][1] * fx1 + m_matrix[1][1] * fy1 + m_matrix[2][1];
- x2 = m_matrix[0][0] * fx2 + m_matrix[1][0] * fy2 + m_matrix[2][0];
- y2 = m_matrix[0][1] * fx2 + m_matrix[1][1] * fy2 + m_matrix[2][1];
- if (t == TxProject) {
- qreal w = 1./(m_matrix[0][2] * fx1 + m_matrix[1][2] * fy1 + m_matrix[2][2]);
- x1 *= w;
- y1 *= w;
- w = 1./(m_matrix[0][2] * fx2 + m_matrix[1][2] * fy2 + m_matrix[2][2]);
- x2 *= w;
- y2 *= w;
- }
- }
+ MAP(fx1, fy1, x1, y1);
+ MAP(fx2, fy2, x2, y2);
+
return QLine(qRound(x1), qRound(y1), qRound(x2), qRound(y2));
}
@@ -1272,66 +1250,12 @@ QLineF QTransform::map(const QLineF &l) const
qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0;
TransformationType t = inline_type();
- switch(t) {
- case TxNone:
- x1 = fx1;
- y1 = fy1;
- x2 = fx2;
- y2 = fy2;
- break;
- case TxTranslate:
- x1 = fx1 + m_matrix[2][0];
- y1 = fy1 + m_matrix[2][1];
- x2 = fx2 + m_matrix[2][0];
- y2 = fy2 + m_matrix[2][1];
- break;
- case TxScale:
- x1 = m_matrix[0][0] * fx1 + m_matrix[2][0];
- y1 = m_matrix[1][1] * fy1 + m_matrix[2][1];
- x2 = m_matrix[0][0] * fx2 + m_matrix[2][0];
- y2 = m_matrix[1][1] * fy2 + m_matrix[2][1];
- break;
- case TxRotate:
- case TxShear:
- case TxProject:
- x1 = m_matrix[0][0] * fx1 + m_matrix[1][0] * fy1 + m_matrix[2][0];
- y1 = m_matrix[0][1] * fx1 + m_matrix[1][1] * fy1 + m_matrix[2][1];
- x2 = m_matrix[0][0] * fx2 + m_matrix[1][0] * fy2 + m_matrix[2][0];
- y2 = m_matrix[0][1] * fx2 + m_matrix[1][1] * fy2 + m_matrix[2][1];
- if (t == TxProject) {
- qreal w = 1./(m_matrix[0][2] * fx1 + m_matrix[1][2] * fy1 + m_matrix[2][2]);
- x1 *= w;
- y1 *= w;
- w = 1./(m_matrix[0][2] * fx2 + m_matrix[1][2] * fy2 + m_matrix[2][2]);
- x2 *= w;
- y2 *= w;
- }
- }
- return QLineF(x1, y1, x2, y2);
-}
+ MAP(fx1, fy1, x1, y1);
+ MAP(fx2, fy2, x2, y2);
-static QPolygonF mapProjective(const QTransform &transform, const QPolygonF &poly)
-{
- if (poly.size() == 0)
- return poly;
-
- if (poly.size() == 1)
- return QPolygonF() << transform.map(poly.at(0));
-
- QPainterPath path;
- path.addPolygon(poly);
-
- path = transform.map(path);
-
- QPolygonF result;
- const int elementCount = path.elementCount();
- result.reserve(elementCount);
- for (int i = 0; i < elementCount; ++i)
- result << path.elementAt(i);
- return result;
+ return QLineF(x1, y1, x2, y2);
}
-
/*!
\fn QPolygonF operator *(const QPolygonF &polygon, const QTransform &matrix)
\since 4.3
@@ -1365,9 +1289,6 @@ QPolygonF QTransform::map(const QPolygonF &a) const
if (t <= TxTranslate)
return a.translated(m_matrix[2][0], m_matrix[2][1]);
- if (t >= QTransform::TxProject)
- return mapProjective(*this, a);
-
int size = a.size();
int i;
QPolygonF p(size);
@@ -1395,9 +1316,6 @@ QPolygon QTransform::map(const QPolygon &a) const
if (t <= TxTranslate)
return a.translated(qRound(m_matrix[2][0]), qRound(m_matrix[2][1]));
- if (t >= QTransform::TxProject)
- return mapProjective(*this, QPolygonF(a)).toPolygon();
-
int size = a.size();
int i;
QPolygon p(size);
@@ -1459,7 +1377,7 @@ QRegion QTransform::map(const QRegion &r) const
if (!nr.isEmpty())
rects.append(nr);
}
- res.setRects(rects.constData(), rects.count());
+ res.setRects(rects.constData(), rects.size());
}
return res;
}
@@ -1706,7 +1624,7 @@ QPolygon QTransform::mapToPolygon(const QRect &rect) const
*/
bool QTransform::squareToQuad(const QPolygonF &quad, QTransform &trans)
{
- if (quad.count() != 4)
+ if (quad.size() != 4)
return false;
qreal dx0 = quad[0].x();
@@ -1828,14 +1746,6 @@ void QTransform::setMatrix(qreal m11, qreal m12, qreal m13,
m_dirty = TxProject;
}
-static inline bool needsPerspectiveClipping(const QRectF &rect, const QTransform &transform)
-{
- const qreal wx = qMin(transform.m13() * rect.left(), transform.m13() * rect.right());
- const qreal wy = qMin(transform.m23() * rect.top(), transform.m23() * rect.bottom());
-
- return wx + wy + transform.m33() < Q_NEAR_CLIP;
-}
-
QRect QTransform::mapRect(const QRect &rect) const
{
TransformationType t = inline_type();
@@ -1856,8 +1766,7 @@ QRect QTransform::mapRect(const QRect &rect) const
y -= h;
}
return QRect(x, y, w, h);
- } else if (t < TxProject || !needsPerspectiveClipping(rect, *this)) {
- // see mapToPolygon for explanations of the algorithm.
+ } else {
qreal x = 0, y = 0;
MAP(rect.left(), rect.top(), x, y);
qreal xmin = x;
@@ -1879,11 +1788,7 @@ QRect QTransform::mapRect(const QRect &rect) const
ymin = qMin(ymin, y);
xmax = qMax(xmax, x);
ymax = qMax(ymax, y);
- return QRect(qRound(xmin), qRound(ymin), qRound(xmax)-qRound(xmin), qRound(ymax)-qRound(ymin));
- } else {
- QPainterPath path;
- path.addRect(rect);
- return map(path).boundingRect().toRect();
+ return QRectF(xmin, ymin, xmax-xmin, ymax-ymin).toRect();
}
}
@@ -1926,7 +1831,7 @@ QRectF QTransform::mapRect(const QRectF &rect) const
y -= h;
}
return QRectF(x, y, w, h);
- } else if (t < TxProject || !needsPerspectiveClipping(rect, *this)) {
+ } else {
qreal x = 0, y = 0;
MAP(rect.x(), rect.y(), x, y);
qreal xmin = x;
@@ -1949,10 +1854,6 @@ QRectF QTransform::mapRect(const QRectF &rect) const
xmax = qMax(xmax, x);
ymax = qMax(ymax, y);
return QRectF(xmin, ymin, xmax-xmin, ymax - ymin);
- } else {
- QPainterPath path;
- path.addRect(rect);
- return map(path).boundingRect();
}
}
diff --git a/src/gui/painting/qtransform.h b/src/gui/painting/qtransform.h
index 71418935d1..e5b245d8c9 100644
--- a/src/gui/painting/qtransform.h
+++ b/src/gui/painting/qtransform.h
@@ -84,8 +84,17 @@ public:
QTransform &translate(qreal dx, qreal dy);
QTransform &scale(qreal sx, qreal sy);
QTransform &shear(qreal sh, qreal sv);
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ QTransform &rotate(qreal a, Qt::Axis axis, qreal distanceToPlane);
+ // ### Qt7: Remove
QTransform &rotate(qreal a, Qt::Axis axis = Qt::ZAxis);
+ QTransform &rotateRadians(qreal a, Qt::Axis axis, qreal distanceToPlane);
+ // ### Qt7: Remove
QTransform &rotateRadians(qreal a, Qt::Axis axis = Qt::ZAxis);
+#else
+ QTransform &rotate(qreal a, Qt::Axis axis = Qt::ZAxis, qreal distanceToPlane = 1024.0f);
+ QTransform &rotateRadians(qreal a, Qt::Axis axis = Qt::ZAxis, qreal distanceToPlane = 1024.0f);
+#endif
static bool squareToQuad(const QPolygonF &square, QTransform &result);
static bool quadToSquare(const QPolygonF &quad, QTransform &result);
diff --git a/src/gui/painting/qtriangulator.cpp b/src/gui/painting/qtriangulator.cpp
index 86f3a2b20c..029566f1b9 100644
--- a/src/gui/painting/qtriangulator.cpp
+++ b/src/gui/painting/qtriangulator.cpp
@@ -2321,3 +2321,5 @@ QPolylineSet qPolyline(const QPainterPath &path,
}
QT_END_NAMESPACE
+
+#undef Q_FIXED_POINT_SCALE
diff --git a/src/gui/painting/shaders/backingstorecompose.frag b/src/gui/painting/shaders/backingstorecompose.frag
index e0c419840e..3b08ade035 100644
--- a/src/gui/painting/shaders/backingstorecompose.frag
+++ b/src/gui/painting/shaders/backingstorecompose.frag
@@ -7,7 +7,7 @@ layout(std140, binding = 0) uniform buf {
mat4 vertexTransform;
mat3 textureTransform;
float opacity;
- int swapRedBlue;
+ int textureSwizzle;
};
layout(binding = 1) uniform sampler2D textureSampler;
@@ -16,8 +16,10 @@ void main()
{
vec4 tmpFragColor = texture(textureSampler, v_texcoord);
tmpFragColor.a *= opacity;
- if (swapRedBlue == 0)
+ if (textureSwizzle == 0)
fragColor = tmpFragColor;
+ else if(textureSwizzle == 2)
+ fragColor.argb = tmpFragColor;
else
- fragColor = tmpFragColor.bgra;
+ fragColor.bgra = tmpFragColor;
}
diff --git a/src/gui/painting/shaders/backingstorecompose.frag.qsb b/src/gui/painting/shaders/backingstorecompose.frag.qsb
index a7c2dfebce..63ba55eed8 100644
--- a/src/gui/painting/shaders/backingstorecompose.frag.qsb
+++ b/src/gui/painting/shaders/backingstorecompose.frag.qsb
Binary files differ
diff --git a/src/gui/painting/shaders/backingstorecompose.vert b/src/gui/painting/shaders/backingstorecompose.vert
index bb8444ade5..0c72c97419 100644
--- a/src/gui/painting/shaders/backingstorecompose.vert
+++ b/src/gui/painting/shaders/backingstorecompose.vert
@@ -9,7 +9,7 @@ layout(std140, binding = 0) uniform buf {
mat4 vertexTransform;
mat3 textureTransform;
float opacity;
- int swapRedBlue;
+ int textureSwizzle;
};
void main()
diff --git a/src/gui/painting/shaders/backingstorecompose.vert.qsb b/src/gui/painting/shaders/backingstorecompose.vert.qsb
index 1f464516fe..1aa6e661f8 100644
--- a/src/gui/painting/shaders/backingstorecompose.vert.qsb
+++ b/src/gui/painting/shaders/backingstorecompose.vert.qsb
Binary files differ
diff --git a/src/gui/painting/webgradients.cpp b/src/gui/painting/webgradients.cpp
index 5924fe6e60..fd5031349e 100644
--- a/src/gui/painting/webgradients.cpp
+++ b/src/gui/painting/webgradients.cpp
@@ -346,8 +346,7 @@ static QList<QGradientStop> qt_preset_gradient_stops(QGradient::Preset preset)
case QGradient::NumPresets:
Q_UNREACHABLE();
}
- Q_UNREACHABLE();
- return {};
+ Q_UNREACHABLE_RETURN({});
}
static constexpr QGradient::QGradientData qt_preset_gradient_data[] = {
diff --git a/src/gui/platform/android/qandroidnativeinterface.cpp b/src/gui/platform/android/qandroidnativeinterface.cpp
index 3062b5255e..c1c4b7149f 100644
--- a/src/gui/platform/android/qandroidnativeinterface.cpp
+++ b/src/gui/platform/android/qandroidnativeinterface.cpp
@@ -7,6 +7,8 @@
#include <QtGui/qoffscreensurface_platform.h>
#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformscreen_p.h>
+
QT_BEGIN_NAMESPACE
using namespace QNativeInterface::Private;
@@ -33,4 +35,20 @@ QOffscreenSurface *QNativeInterface::QAndroidOffscreenSurface::fromNative(ANati
&QAndroidOffScreenIntegration::createOffscreenSurface>(nativeSurface);
}
+/*!
+ \class QNativeInterface::QAndroidScreen
+ \since 6.7
+ \brief Native interface to a screen.
+
+ Accessed through QScreen::nativeInterface().
+ \inmodule QtGui
+ \ingroup native-interfaces
+ \ingroup native-interfaces-qscreen
+*/
+/*!
+ \fn int QNativeInterface::QAndroidScreen::displayId() const;
+ \return the id of the underlying Android display.
+*/
+QT_DEFINE_NATIVE_INTERFACE(QAndroidScreen);
+
QT_END_NAMESPACE
diff --git a/src/gui/platform/darwin/qappleiconengine.mm b/src/gui/platform/darwin/qappleiconengine.mm
new file mode 100644
index 0000000000..7e0ed184dc
--- /dev/null
+++ b/src/gui/platform/darwin/qappleiconengine.mm
@@ -0,0 +1,464 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qappleiconengine_p.h"
+
+#if defined(Q_OS_MACOS)
+# include <AppKit/AppKit.h>
+#elif defined(QT_PLATFORM_UIKIT)
+# include <UIKit/UIKit.h>
+#endif
+
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpalette.h>
+#include <QtGui/qstylehints.h>
+
+#include <QtGui/private/qcoregraphics_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace {
+auto *loadImage(const QString &iconName)
+{
+ static constexpr std::pair<QLatin1StringView, NSString *> iconMap[] = {
+ {"address-book-new"_L1, @"book.closed"},
+ {"application-exit"_L1, @"xmark.circle"},
+ {"appointment-new"_L1, @"calendar.badge.plus"},
+ {"call-start"_L1, @"phone.arrow.up.right"},
+ {"call-stop"_L1, @"phone.down"},
+ {"contact-new"_L1, @"person.crop.circle.badge.plus"},
+ {"document-new"_L1, @"doc.badge.plus"},
+ {"document-open"_L1, @"folder"},
+ {"document-open-recent"_L1, @"doc.badge.clock"},
+ {"document-page-setup"_L1, @"doc.badge.gearshape"},
+ {"document-print"_L1, @"printer"},
+ //{"document-print-preview"_L1, @""},
+ {"document-properties"_L1, @"doc.badge.ellipsis"},
+ //{"document-revert"_L1, @""},
+ {"document-save"_L1, @"square.and.arrow.down"},
+ //{"document-save-as"_L1, @""},
+ {"document-send"_L1, @"paperplane"},
+ {"edit-clear"_L1, @"xmark.circle"},
+ {"edit-copy"_L1, @"doc.on.doc"},
+ {"edit-cut"_L1, @"scissors"},
+ {"edit-delete"_L1, @"delete.left"},
+ {"edit-find"_L1, @"magnifyingglass"},
+ //{"edit-find-replace"_L1, @"arrow.up.left.and.down.right.magnifyingglass"},
+ {"edit-paste"_L1, @"clipboard"},
+ {"edit-redo"_L1, @"arrowshape.turn.up.right"},
+ //{"edit-select-all"_L1, @""},
+ {"edit-undo"_L1, @"arrowshape.turn.up.left"},
+ {"folder-new"_L1, @"folder.badge.plus"},
+ {"format-indent-less"_L1, @"decrease.indent"},
+ {"format-indent-more"_L1, @"increase.indent"},
+ {"format-justify-center"_L1, @"text.aligncenter"},
+ {"format-justify-fill"_L1, @"text.justify"},
+ {"format-justify-left"_L1, @"text.justify.left"},
+ {"format-justify-right"_L1, @"text.justify.right"},
+ {"format-text-direction-ltr"_L1, @"text.justify.leading"},
+ {"format-text-direction-rtl"_L1, @"text.justify.trailing"},
+ {"format-text-bold"_L1, @"bold"},
+ {"format-text-italic"_L1, @"italic"},
+ {"format-text-underline"_L1, @"underline"},
+ {"format-text-strikethrough"_L1, @"strikethrough"},
+ //{"go-bottom"_L1, @""},
+ {"go-down"_L1, @"arrowshape.down"},
+ {"go-first"_L1, @"increase.indent"},
+ {"go-home"_L1, @"house"},
+ //{"go-jump"_L1, @""},
+ //{"go-last"_L1, @""},
+ {"go-next"_L1, @"arrowshape.right"},
+ {"go-previous"_L1, @"arrowshape.left"},
+ //{"go-top"_L1, @""},
+ {"go-up"_L1, @"arrowshape.up"},
+ {"help-about"_L1, @"info.circle"},
+ //{"help-contents"_L1, @""},
+ {"help-faq"_L1, @"questionmark.app"},
+ {"insert-image"_L1, @"photo.badge.plus"},
+ {"insert-link"_L1, @"link.badge.plus"},
+ //{"insert-object"_L1, @""},
+ {"insert-text"_L1, @"textformat"},
+ {"list-add"_L1, @"plus.circle"},
+ {"list-remove"_L1, @"minus.circle"},
+ {"mail-forward"_L1, @"arrowshape.turn.up.right"},
+ {"mail-mark-important"_L1, @"star"},
+ {"mail-mark-junk"_L1, @"xmark.bin"},
+ {"mail-mark-notjunk"_L1, @"trash.slash"},
+ {"mail-mark-read"_L1, @"envelope.open"},
+ {"mail-mark-unread"_L1, @"envelope.fill"},
+ {"mail-message-new"_L1, @"square.and.pencil"},
+ {"mail-reply-all"_L1, @"arrowshape.turn.up.left.2"},
+ {"mail-reply-sender"_L1, @"arrowshape.turn.up.left"},
+ {"mail-send"_L1, @"paperplane"},
+ {"mail-send-receive"_L1, @"envelope.arrow.triangle.branch"},
+ {"media-eject"_L1, @"eject"},
+ {"media-playback-pause"_L1, @"pause"},
+ {"media-playback-start"_L1, @"play"},
+ {"media-playback-stop"_L1, @"stop"},
+ {"media-record"_L1, @"record.circle"},
+ {"media-seek-backward"_L1, @"backward"},
+ {"media-seek-forward"_L1, @"forward"},
+ {"media-skip-backward"_L1, @"backward.end.alt"},
+ {"media-skip-forward"_L1, @"forward.end.alt"},
+ {"object-flip-horizontal"_L1, @"rectangle.landscape.rotate"},
+ {"object-flip-vertical"_L1, @"rectangle.portrait.rotate"},
+ {"object-rotate-left"_L1, @"rotate.left"},
+ {"object-rotate-right"_L1, @"rotate.right"},
+ {"process-stop"_L1, @"stop.circle"},
+ {"system-lock-screen"_L1, @"lock.display"},
+ {"system-log-out"_L1, @"door.left.hand.open"},
+ //{"system-run"_L1, @""},
+ {"system-search"_L1, @"magnifyingglass"},
+ //{"system-reboot"_L1, @""},
+ {"system-shutdown"_L1, @"power"},
+ //{"tools-check-spelling"_L1, @""},
+ {"view-fullscreen"_L1, @"arrow.up.left.and.arrow.down.right"},
+ {"view-refresh"_L1, @"arrow.clockwise"},
+ {"view-restore"_L1, @"arrow.down.right.and.arrow.up.left"},
+ //{"view-sort-ascending"_L1, @""},
+ //{"view-sort-descending"_L1, @""},
+ {"window-close"_L1, @"xmark.circle"},
+ {"window-new"_L1, @"macwindow.badge.plus"},
+ {"zoom-fit-best"_L1, @"square.arrowtriangle.4.outward"},
+ {"zoom-in"_L1, @"plus.magnifyingglass"},
+ //{"zoom-original"_L1, @""},
+ {"zoom-out"_L1, @"minus.magnifyingglass"},
+ {"process-working"_L1, @"circle.dotted"},
+ //{"accessories-calculator"_L1, @""},
+ //{"accessories-character-map"_L1, @""},
+ {"accessories-dictionary"_L1, @"character.book.closed"},
+ {"accessories-text-editor"_L1, @"textformat"},
+ {"help-browser"_L1, @"folder.badge.questionmark"},
+ {"multimedia-volume-control"_L1, @"speaker.wave.3"},
+ {"preferences-desktop-accessibility"_L1, @"accessibility"},
+ //{"preferences-desktop-font"_L1, @""},
+ {"preferences-desktop-keyboard"_L1, @"keyboard.badge.ellipsis"},
+ //{"preferences-desktop-locale"_L1, @""},
+ //{"preferences-desktop-multimedia"_L1, @""},
+ //{"preferences-desktop-screensaver"_L1, @""},
+ //{"preferences-desktop-theme"_L1, @""},
+ //{"preferences-desktop-wallpaper"_L1, @""},
+ {"system-file-manager"_L1, @"folder.badge.gearshape"},
+ //{"system-software-install"_L1, @""},
+ //{"system-software-update"_L1, @""}, d
+ //{"utilities-system-monitor"_L1, @""},
+ {"utilities-terminal"_L1, @"apple.terminal"},
+ //{"applications-accessories"_L1, @""},
+ //{"applications-development"_L1, @""},
+ //{"applications-engineering"_L1, @""},
+ {"applications-games"_L1, @"gamecontroller"},
+ //{"applications-graphics"_L1, @""},
+ {"applications-internet"_L1, @"network"},
+ {"applications-multimedia"_L1, @"tv.and.mediabox"},
+ //{"applications-office"_L1, @""},
+ //{"applications-other"_L1, @""},
+ {"applications-science"_L1, @"atom"},
+ //{"applications-system"_L1, @""},
+ //{"applications-utilities"_L1, @""},
+ {"preferences-desktop"_L1, @"menubar.dock.rectangle"},
+ //{"preferences-desktop-peripherals"_L1, @""},
+ //{"preferences-desktop-personal"_L1, @""},
+ //{"preferences-other"_L1, @""},
+ //{"preferences-system"_L1, @""},
+ {"preferences-system-network"_L1, @"network"},
+ {"system-help"_L1, @"questionmark.diamond"},
+ {"audio-card"_L1, @"waveform.circle"},
+ {"audio-input-microphone"_L1, @"mic"},
+ {"battery"_L1, @"battery.100percent"},
+ {"camera-photo"_L1, @"camera"},
+ {"camera-video"_L1, @"video"},
+ {"camera-web"_L1, @"web.camera"},
+ {"computer"_L1, @"desktopcomputer"},
+ {"drive-harddisk"_L1, @"internaldrive"},
+ {"drive-optical"_L1, @"opticaldiscdrive"},
+ {"drive-removable-media"_L1, @"externaldrive"},
+ {"input-gaming"_L1, @"gamecontroller"}, // "games" also using this one
+ {"input-keyboard"_L1, @"keyboard"},
+ {"input-mouse"_L1, @"computermouse"},
+ {"input-tablet"_L1, @"ipad"},
+ {"media-flash"_L1, @"mediastick"},
+ //{"media-floppy"_L1, @""},
+ //{"media-optical"_L1, @""},
+ {"media-tape"_L1, @"recordingtape"},
+ //{"modem"_L1, @""},
+ {"multimedia-player"_L1, @"play.rectangle"},
+ {"network-wired"_L1, @"app.connected.to.app.below.fill"},
+ {"network-wireless"_L1, @"wifi"},
+ //{"pda"_L1, @""},
+ {"phone"_L1, @"iphone"},
+ {"printer"_L1, @"printer"},
+ {"scanner"_L1, @"scanner"},
+ {"video-display"_L1, @"play.display"},
+ //{"emblem-default"_L1, @""},
+ {"emblem-documents"_L1, @"doc.circle"},
+ {"emblem-downloads"_L1, @"arrow.down.circle"},
+ {"emblem-favorite"_L1, @"star"},
+ {"emblem-important"_L1, @"exclamationmark.bubble.circle"},
+ {"emblem-mail"_L1, @"envelope"},
+ {"emblem-photos"_L1, @"photo.stack"},
+ //{"emblem-readonly"_L1, @""},
+ {"emblem-shared"_L1, @"folder.badge.person.crop"},
+ {"emblem-symbolic-link"_L1, @"link.circle"},
+ {"emblem-synchronized"_L1, @"arrow.triangle.2.circlepath.circle"},
+ {"emblem-system"_L1, @"gear"},
+ //{"emblem-unreadable"_L1, @""},
+ {"folder"_L1, @"folder"},
+ //{"folder-remote"_L1, @""},
+ {"network-server"_L1, @"server.rack"},
+ //{"network-workgroup"_L1, @""},
+ //{"start-here"_L1, @""},
+ {"user-bookmarks"_L1, @"bookmark.circle"},
+ {"user-desktop"_L1, @"desktopcomputer"}, //"computer" also using this one
+ {"user-home"_L1, @"house"}, //"go-home" also using this one
+ {"user-trash"_L1, @"trash"},
+ {"appointment-missed"_L1, @"calendar.badge.exclamationmark"},
+ {"appointment-soon"_L1, @"calendar.badge.clock"},
+ {"audio-volume-high"_L1, @"speaker.wave.3"},
+ {"audio-volume-low"_L1, @"speaker.wave.1"},
+ {"audio-volume-medium"_L1, @"speaker.wave.2"},
+ {"audio-volume-muted"_L1, @"speaker.slash"},
+ {"battery-caution"_L1, @"minus.plus.batteryblock.exclamationmark"},
+ {"battery-low"_L1, @"battery.25percent"}, // there are different levels that can be low battery
+ {"dialog-error"_L1, @"exclamationmark.bubble"},
+ {"dialog-information"_L1, @"info.circle"},
+ {"dialog-password"_L1, @"lock"},
+ {"dialog-question"_L1, @"questionmark.circle"},
+ {"dialog-warning"_L1, @"exclamationmark.octagon"},
+ {"folder-drag-accept"_L1, @"plus.rectangle.on.folder"},
+ //{"folder-open"_L1, @""},
+ {"folder-visiting"_L1, @"folder.circle"},
+ {"image-loading"_L1, @"photo.circle"},
+ {"image-missing"_L1, @"photo"},
+ {"mail-attachment"_L1, @"paperclip"},
+ {"mail-unread"_L1, @"envelope.badge"},
+ {"mail-read"_L1, @"envelope.open"},
+ {"mail-replied"_L1, @"arrowshape.turn.up.left"},
+ //{"mail-signed"_L1, @""},
+ //{"mail-signed-verified"_L1, @""},
+ {"media-playlist-repeat"_L1, @"repet"},
+ {"media-playlist-shuffle"_L1, @"shuffle"},
+ //{"network-error"_L1, @""},
+ //{"network-idle"_L1, @""},
+ {"network-offline"_L1, @"network.slash"},
+ //{"network-receive"_L1, @""},
+ //{"network-transmit"_L1, @""},
+ //{"network-transmit-receive"_L1, @""},
+ //{"printer-error"_L1, @""},
+ {"printer-printing"_L1, @"printer.dotmatrix.filled.and.paper"}, // not sure
+ {"security-high"_L1, @"lock.shield"},
+ //{"security-medium"_L1, @""},
+ {"security-low"_L1, @"lock.trianglebadge.exclamationmark"},
+ {"software-update-available"_L1, @"arrowshape.up.circle"},
+ {"software-update-urgent"_L1, @"exclamationmark.transmission"},
+ {"sync-error"_L1, @"exclamationmark.arrow.triangle.2.circlepath"},
+ {"sync-synchronizing"_L1, @"arrow.triangle.2.circlepath"},
+ {"task-due"_L1, @"clock.badge.exclamationmark"},
+ {"task-past-due"_L1, @"clock.badge.xmark"},
+ {"user-available"_L1, @"person.crop.circle.badge.checkmark"},
+ {"user-away"_L1, @"person.crop.circle.badge.clock"},
+ //{"user-idle"_L1, @""},
+ {"user-offline"_L1, @"person.crop.circle.badge.xmark"},
+ //{"user-trash-full"_L1, @""},
+ {"weather-clear"_L1, @"sun.max"},
+ {"weather-clear-night"_L1, @"moon"},
+ {"weather-few-clouds"_L1, @"cloud.sun"},
+ {"weather-few-clouds-night"_L1, @"cloud.moon"},
+ {"weather-fog"_L1, @"cloud.fog"},
+ {"weather-overcast"_L1, @"cloud"},
+ //{"weather-severe-alert"_L1, @""},
+ {"weather-showers"_L1, @"cloud.rain"},
+ //{"weather-showers-scattered"_L1, @""},
+ {"weather-snow"_L1, @"cloud.snow"},
+ {"weather-storm"_L1, @"tropicalstorm"},
+ };
+ const auto it = std::find_if(std::begin(iconMap), std::end(iconMap), [iconName](const auto &c){
+ return c.first == iconName;
+ });
+ NSString *systemIconName = it != std::end(iconMap) ? it->second : iconName.toNSString();
+#if defined(Q_OS_MACOS)
+ return [NSImage imageWithSystemSymbolName:systemIconName accessibilityDescription:nil];
+#elif defined(QT_PLATFORM_UIKIT)
+ return [UIImage systemImageNamed:systemIconName];
+#endif
+}
+}
+
+QAppleIconEngine::QAppleIconEngine(const QString &iconName)
+ : m_iconName(iconName), m_image(loadImage(iconName))
+{
+ if (m_image)
+ [m_image retain];
+}
+
+QAppleIconEngine::~QAppleIconEngine()
+{
+ if (m_image)
+ [m_image release];
+}
+
+QIconEngine *QAppleIconEngine::clone() const
+{
+ return new QAppleIconEngine(m_iconName);
+}
+
+QString QAppleIconEngine::key() const
+{
+ return u"QAppleIconEngine"_s;
+}
+
+QString QAppleIconEngine::iconName()
+{
+ return m_iconName;
+}
+
+bool QAppleIconEngine::isNull()
+{
+ return m_image == nullptr;
+}
+
+QList<QSize> QAppleIconEngine::availableIconSizes(double aspectRatio)
+{
+ const qreal devicePixelRatio = qGuiApp->devicePixelRatio();
+ const QList<QSize> sizes = {
+ {qRound(16 * devicePixelRatio), qRound(16. * devicePixelRatio / aspectRatio)},
+ {qRound(32 * devicePixelRatio), qRound(32. * devicePixelRatio / aspectRatio)},
+ {qRound(64 * devicePixelRatio), qRound(64. * devicePixelRatio / aspectRatio)},
+ {qRound(128 * devicePixelRatio), qRound(128. * devicePixelRatio / aspectRatio)},
+ {qRound(256 * devicePixelRatio), qRound(256. * devicePixelRatio / aspectRatio)},
+ };
+ return sizes;
+}
+
+QList<QSize> QAppleIconEngine::availableSizes(QIcon::Mode, QIcon::State)
+{
+ const double aspectRatio = isNull() ? 1.0 : m_image.size.width / m_image.size.height;
+ return availableIconSizes(aspectRatio);
+}
+
+QSize QAppleIconEngine::actualSize(const QSize &size, QIcon::Mode /*mode*/, QIcon::State /*state*/)
+{
+ const double inputAspectRatio = isNull() ? 1.0 : m_image.size.width / m_image.size.height;
+ const double outputAspectRatio = size.width() / size.height();
+ QSize result = size;
+ if (outputAspectRatio > inputAspectRatio)
+ result.rwidth() = result.height() * inputAspectRatio;
+ else
+ result.rheight() = result.width() / inputAspectRatio;
+ return result;
+}
+
+QPixmap QAppleIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ return scaledPixmap(size, mode, state, 1.0);
+}
+
+namespace {
+#if defined(Q_OS_MACOS)
+auto *configuredImage(const NSImage *image, const QColor &color)
+{
+ auto *config = [NSImageSymbolConfiguration configurationWithPointSize:48
+ weight:NSFontWeightRegular
+ scale:NSImageSymbolScaleLarge];
+ if (@available(macOS 12, *)) {
+ auto *primaryColor = [NSColor colorWithSRGBRed:color.redF()
+ green:color.greenF()
+ blue:color.blueF()
+ alpha:color.alphaF()];
+
+ auto *colorConfig = [NSImageSymbolConfiguration configurationWithHierarchicalColor:primaryColor];
+ config = [config configurationByApplyingConfiguration:colorConfig];
+ }
+
+ return [image imageWithSymbolConfiguration:config];
+}
+#elif defined(QT_PLATFORM_UIKIT)
+auto *configuredImage(const UIImage *image, const QColor &color)
+{
+ auto *config = [UIImageSymbolConfiguration configurationWithPointSize:48
+ weight:UIImageSymbolWeightRegular
+ scale:UIImageSymbolScaleLarge];
+
+ if (@available(iOS 15, *)) {
+ auto *primaryColor = [UIColor colorWithRed:color.redF()
+ green:color.greenF()
+ blue:color.blueF()
+ alpha:color.alphaF()];
+
+ auto *colorConfig = [UIImageSymbolConfiguration configurationWithHierarchicalColor:primaryColor];
+ config = [config configurationByApplyingConfiguration:colorConfig];
+ }
+ return [image imageByApplyingSymbolConfiguration:config];
+}
+#endif
+}
+
+QPixmap QAppleIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
+{
+ const quint64 cacheKey = calculateCacheKey(mode, state);
+ if (cacheKey != m_cacheKey || m_pixmap.size() != size || m_pixmap.devicePixelRatio() != scale) {
+ const QSize paintSize = actualSize(size, mode, state);
+ const QSize paintOffset = paintSize != size
+ ? (QSizeF(size - paintSize) * 0.5).toSize()
+ : QSize();
+
+ m_pixmap = QPixmap(size * scale);
+ m_pixmap.setDevicePixelRatio(scale);
+ m_pixmap.fill(Qt::transparent);
+
+ QPainter painter(&m_pixmap);
+ paint(&painter, QRect(paintOffset.width(), paintOffset.height(),
+ paintSize.width(), paintSize.height()), mode, state);
+
+ m_cacheKey = cacheKey;
+ }
+ return m_pixmap;
+}
+
+void QAppleIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
+{
+ Q_UNUSED(state);
+
+ QColor color;
+ const QPalette palette;
+ switch (mode) {
+ case QIcon::Normal:
+ color = palette.color(QPalette::Inactive, QPalette::Text);
+ break;
+ case QIcon::Disabled:
+ color = palette.color(QPalette::Disabled, QPalette::Text);
+ break;
+ case QIcon::Active:
+ color = palette.color(QPalette::Active, QPalette::Text);
+ break;
+ case QIcon::Selected:
+ color = palette.color(QPalette::Active, QPalette::HighlightedText);
+ break;
+ }
+ const auto *image = configuredImage(m_image, color);
+
+ QMacCGContext ctx(painter);
+
+#if defined(Q_OS_MACOS)
+ NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithCGContext:ctx flipped:YES];
+ [NSGraphicsContext saveGraphicsState];
+ [NSGraphicsContext setCurrentContext:gc];
+
+ const NSSize pixmapSize = NSMakeSize(rect.width(), rect.height());
+ [image setSize:pixmapSize];
+ const NSRect sourceRect = NSMakeRect(0, 0, pixmapSize.width, pixmapSize.height);
+ const NSRect iconRect = NSMakeRect(rect.x(), rect.y(), pixmapSize.width, pixmapSize.height);
+
+ [image drawInRect:iconRect fromRect:sourceRect operation:NSCompositingOperationSourceOver fraction:1.0 respectFlipped:YES hints:nil];
+ [NSGraphicsContext restoreGraphicsState];
+#elif defined(QT_PLATFORM_UIKIT)
+ UIGraphicsPushContext(ctx);
+ const CGRect cgrect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
+ [image drawInRect:cgrect];
+ UIGraphicsPopContext();
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/platform/darwin/qappleiconengine_p.h b/src/gui/platform/darwin/qappleiconengine_p.h
new file mode 100644
index 0000000000..2a4ff7fc64
--- /dev/null
+++ b/src/gui/platform/darwin/qappleiconengine_p.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QAPPLEICONENGINE_P_H
+#define QAPPLEICONENGINE_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 <QtGui/qiconengine.h>
+
+#include <QtCore/private/qcore_mac_p.h>
+
+Q_FORWARD_DECLARE_OBJC_CLASS(UIImage);
+Q_FORWARD_DECLARE_OBJC_CLASS(NSImage);
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QAppleIconEngine : public QIconEngine
+{
+public:
+ QAppleIconEngine(const QString &iconName);
+ ~QAppleIconEngine();
+ QIconEngine *clone() const override;
+ QString key() const override;
+ QString iconName() override;
+ bool isNull() override;
+
+ QList<QSize> availableSizes(QIcon::Mode, QIcon::State) override;
+ QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
+ QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
+ QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) override;
+ void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override;
+
+ static QList<QSize> availableIconSizes(double aspectRatio = 1.0);
+
+private:
+ static constexpr quint64 calculateCacheKey(QIcon::Mode mode, QIcon::State state)
+ {
+ return (quint64(mode) << 32) | state;
+ }
+
+ const QString m_iconName;
+#if defined(Q_OS_MACOS)
+ const NSImage *m_image;
+#elif defined(QT_PLATFORM_UIKIT)
+ const UIImage *m_image;
+#endif
+ mutable QPixmap m_pixmap;
+ mutable quint64 m_cacheKey = {};
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QAPPLEICONENGINE_P_H
diff --git a/src/gui/platform/darwin/qapplekeymapper.mm b/src/gui/platform/darwin/qapplekeymapper.mm
index dcc51bfca6..b8ff5c9d6d 100644
--- a/src/gui/platform/darwin/qapplekeymapper.mm
+++ b/src/gui/platform/darwin/qapplekeymapper.mm
@@ -18,7 +18,6 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcQpaKeyMapper, "qt.qpa.keymapper");
Q_LOGGING_CATEGORY(lcQpaKeyMapperKeys, "qt.qpa.keymapper.keys");
static Qt::KeyboardModifiers swapModifiersIfNeeded(const Qt::KeyboardModifiers modifiers)
@@ -37,36 +36,6 @@ static Qt::KeyboardModifiers swapModifiersIfNeeded(const Qt::KeyboardModifiers m
return swappedModifiers;
}
-Qt::Key QAppleKeyMapper::fromNSString(Qt::KeyboardModifiers qtModifiers, NSString *characters,
- NSString *charactersIgnoringModifiers, QString &text)
-{
- if ([characters isEqualToString:@"\t"]) {
- if (qtModifiers & Qt::ShiftModifier)
- return Qt::Key_Backtab;
- return Qt::Key_Tab;
- } else if ([characters isEqualToString:@"\r"]) {
- if (qtModifiers & Qt::KeypadModifier)
- return Qt::Key_Enter;
- return Qt::Key_Return;
- }
- if ([characters length] != 0 || [charactersIgnoringModifiers length] != 0) {
- QChar ch;
- if (((qtModifiers & Qt::MetaModifier) || (qtModifiers & Qt::AltModifier)) &&
- ([charactersIgnoringModifiers length] != 0)) {
- ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
- } else if ([characters length] != 0) {
- ch = QChar([characters characterAtIndex:0]);
- }
- if (!(qtModifiers & (Qt::ControlModifier | Qt::MetaModifier)) &&
- (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff)) {
- text = QString::fromNSString(characters);
- }
- if (!ch.isNull())
- return Qt::Key(ch.toUpper().unicode());
- }
- return Qt::Key_unknown;
-}
-
#ifdef Q_OS_MACOS
static constexpr std::tuple<NSEventModifierFlags, Qt::KeyboardModifier> cocoaModifierMap[] = {
{ NSEventModifierFlagShift, Qt::ShiftModifier },
@@ -360,11 +329,11 @@ QChar QAppleKeyMapper::toCocoaKey(Qt::Key key)
{
// Prioritize overloaded keys
if (key == Qt::Key_Return)
- return QChar(NSNewlineCharacter);
+ return QChar(NSCarriageReturnCharacter);
if (key == Qt::Key_Backspace)
return QChar(NSBackspaceCharacter);
- static QHash<Qt::Key, char16_t> reverseCocoaKeys;
+ Q_CONSTINIT static QHash<Qt::Key, char16_t> reverseCocoaKeys;
if (reverseCocoaKeys.isEmpty()) {
reverseCocoaKeys.reserve(cocoaKeys.size());
for (auto it = cocoaKeys.begin(); it != cocoaKeys.end(); ++it)
@@ -384,7 +353,7 @@ Qt::Key QAppleKeyMapper::fromCocoaKey(QChar keyCode)
// ------------------------------------------------
-Qt::KeyboardModifiers QAppleKeyMapper::queryKeyboardModifiers()
+Qt::KeyboardModifiers QAppleKeyMapper::queryKeyboardModifiers() const
{
return fromCocoaModifiers(NSEvent.modifierFlags);
}
@@ -538,11 +507,9 @@ const QAppleKeyMapper::KeyMap &QAppleKeyMapper::keyMapForKey(VirtualKeyCode virt
where each modifier-key combination has been mapped to the
key it will produce.
*/
-QList<int> QAppleKeyMapper::possibleKeys(const QKeyEvent *event) const
+QList<QKeyCombination> QAppleKeyMapper::possibleKeyCombinations(const QKeyEvent *event) const
{
- QList<int> ret;
-
- qCDebug(lcQpaKeyMapper) << "Computing possible keys for" << event;
+ QList<QKeyCombination> ret;
const auto nativeVirtualKey = event->nativeVirtualKey();
if (!nativeVirtualKey)
@@ -555,16 +522,49 @@ QList<int> QAppleKeyMapper::possibleKeys(const QKeyEvent *event) const
auto eventModifiers = event->modifiers();
- // The complete set of event modifiers, along with the
- // unmodified key, is always a valid key combination,
- // and the first priority.
- ret << int(eventModifiers) + int(unmodifiedKey);
+ int startingModifierLayer = 0;
+ if (toCocoaModifiers(eventModifiers) & NSEventModifierFlagCommand) {
+ // When the Command key is pressed AppKit seems to do key equivalent
+ // matching using a Latin/Roman interpretation of the current keyboard
+ // layout. For example, for a Greek layout, pressing Option+Command+C
+ // produces a key event with chars="ç" and unmodchars="ψ", but AppKit
+ // still treats this as a match for a key equivalent of Option+Command+C.
+ // We can't do the same by just applying the modifiers to our key map,
+ // as that too contains "ψ" for the Option+Command combination. What we
+ // can do instead is take advantage of the fact that the Command
+ // modifier layer in all/most keyboard layouts contains a Latin
+ // layer. We then combine that with the modifiers of the event
+ // to produce the resulting "Latin" key combination.
+ static constexpr int kCommandLayer = 2;
+ ret << QKeyCombination::fromCombined(
+ int(eventModifiers) + int(keyMap[kCommandLayer]));
+
+ // If the unmodified key is outside of Latin1, we also treat
+ // that as a valid key combination, even if AppKit natively
+ // does not. For example, for a Greek layout, we still want
+ // to support Option+Command+ψ as a key combination, as it's
+ // unlikely to clash with the Latin key combination we added
+ // above.
+
+ // However, if the unmodified key is within Latin1, we skip
+ // it, to avoid these types of conflicts. For example, in
+ // the same Greek layout, pressing the key next to Tab will
+ // produce a Latin ';' symbol, but we've already treated that
+ // as 'q' above, thanks to the Command modifier, so we skip
+ // the potential Command+; key combination. This is also in
+ // line with what AppKit natively does.
+
+ // Skipping Latin1 unmodified keys also handles the case of
+ // a Latin layout, where the unmodified and modified keys
+ // are the same.
+
+ if (unmodifiedKey <= 0xff)
+ startingModifierLayer = 1;
+ }
// FIXME: We only compute the first 8 combinations. Why?
- for (int i = 1; i < 8; ++i) {
+ for (int i = startingModifierLayer; i < 15; ++i) {
auto keyAfterApplyingModifiers = keyMap[i];
- if (keyAfterApplyingModifiers == unmodifiedKey)
- continue;
if (!keyAfterApplyingModifiers)
continue;
@@ -575,18 +575,39 @@ QList<int> QAppleKeyMapper::possibleKeys(const QKeyEvent *event) const
// If the event includes more modifiers than the candidate they
// will need to be included in the resulting key combination.
auto additionalModifiers = eventModifiers & ~candidateModifiers;
- ret << int(additionalModifiers) + int(keyAfterApplyingModifiers);
- }
- }
- if (lcQpaKeyMapper().isDebugEnabled()) {
- qCDebug(lcQpaKeyMapper) << "Possible keys:";
- for (int keyAndModifiers : ret) {
- auto keyCombination = QKeyCombination::fromCombined(keyAndModifiers);
- auto keySequence = QKeySequence(keyCombination);
- qCDebug(lcQpaKeyMapper).verbosity(0) << "\t-"
- << keyCombination << "/" << keySequence << "/"
- << qUtf8Printable(keySequence.toString(QKeySequence::NativeText));
+ auto keyCombination = QKeyCombination::fromCombined(
+ int(additionalModifiers) + int(keyAfterApplyingModifiers));
+
+ // If there's an existing key combination with the same key,
+ // but a different set of modifiers, we want to choose only
+ // one of them, by priority (see below).
+ const auto existingCombination = std::find_if(
+ ret.begin(), ret.end(), [&](auto existingCombination) {
+ return existingCombination.key() == keyAfterApplyingModifiers;
+ });
+
+ if (existingCombination != ret.end()) {
+ // We prioritize the combination with the more specific
+ // modifiers. In the case where the number of modifiers
+ // are the same, we want to prioritize Command over Option
+ // over Control over Shift. Unfortunately the order (and
+ // hence value) of the modifiers in Qt::KeyboardModifier
+ // does not match our preferred order when Control and
+ // Meta is switched, but we can work around that by
+ // explicitly swapping the modifiers and using that
+ // for the comparison. This also works when the
+ // Qt::AA_MacDontSwapCtrlAndMeta application attribute
+ // is set, as the incoming modifiers are then left
+ // as is, and we can still trust the order.
+ auto existingModifiers = swapModifiersIfNeeded(existingCombination->keyboardModifiers());
+ auto replacementModifiers = swapModifiersIfNeeded(additionalModifiers);
+ if (replacementModifiers > existingModifiers)
+ *existingCombination = keyCombination;
+ } else {
+ // All is good, no existing combination has this key
+ ret << keyCombination;
+ }
}
}
@@ -597,11 +618,40 @@ QList<int> QAppleKeyMapper::possibleKeys(const QKeyEvent *event) const
#else // iOS
+Qt::Key QAppleKeyMapper::fromNSString(Qt::KeyboardModifiers qtModifiers, NSString *characters,
+ NSString *charactersIgnoringModifiers, QString &text)
+{
+ if ([characters isEqualToString:@"\t"]) {
+ if (qtModifiers & Qt::ShiftModifier)
+ return Qt::Key_Backtab;
+ return Qt::Key_Tab;
+ } else if ([characters isEqualToString:@"\r"]) {
+ if (qtModifiers & Qt::KeypadModifier)
+ return Qt::Key_Enter;
+ return Qt::Key_Return;
+ }
+ if ([characters length] != 0 || [charactersIgnoringModifiers length] != 0) {
+ QChar ch;
+ if (((qtModifiers & Qt::MetaModifier) || (qtModifiers & Qt::AltModifier)) &&
+ ([charactersIgnoringModifiers length] != 0)) {
+ ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
+ } else if ([characters length] != 0) {
+ ch = QChar([characters characterAtIndex:0]);
+ }
+ if (!(qtModifiers & (Qt::ControlModifier | Qt::MetaModifier)) &&
+ (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff)) {
+ text = QString::fromNSString(characters);
+ }
+ if (!ch.isNull())
+ return Qt::Key(ch.toUpper().unicode());
+ }
+ return Qt::Key_unknown;
+}
+
// Keyboard keys (non-modifiers)
API_AVAILABLE(ios(13.4)) Qt::Key QAppleKeyMapper::fromUIKitKey(NSString *keyCode)
{
static QHash<NSString *, Qt::Key> uiKitKeys = {
-#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_13_4)
{ UIKeyInputF1, Qt::Key_F1 },
{ UIKeyInputF2, Qt::Key_F2 },
{ UIKeyInputF3, Qt::Key_F3 },
@@ -618,7 +668,6 @@ API_AVAILABLE(ios(13.4)) Qt::Key QAppleKeyMapper::fromUIKitKey(NSString *keyCode
{ UIKeyInputEnd, Qt::Key_End },
{ UIKeyInputPageUp, Qt::Key_PageUp },
{ UIKeyInputPageDown, Qt::Key_PageDown },
-#endif
{ UIKeyInputEscape, Qt::Key_Escape },
{ UIKeyInputUpArrow, Qt::Key_Up },
{ UIKeyInputDownArrow, Qt::Key_Down },
diff --git a/src/gui/platform/darwin/qapplekeymapper_p.h b/src/gui/platform/darwin/qapplekeymapper_p.h
index 34557c8ede..1f3494d16f 100644
--- a/src/gui/platform/darwin/qapplekeymapper_p.h
+++ b/src/gui/platform/darwin/qapplekeymapper_p.h
@@ -19,6 +19,8 @@
#include <Carbon/Carbon.h>
#endif
+#include <qpa/qplatformkeymapper.h>
+
#include <QtCore/QList>
#include <QtCore/QHash>
#include <QtGui/QKeyEvent>
@@ -27,13 +29,12 @@
QT_BEGIN_NAMESPACE
-class Q_GUI_EXPORT QAppleKeyMapper
+class Q_GUI_EXPORT QAppleKeyMapper : public QPlatformKeyMapper
{
public:
- static Qt::KeyboardModifiers queryKeyboardModifiers();
- QList<int> possibleKeys(const QKeyEvent *event) const;
- static Qt::Key fromNSString(Qt::KeyboardModifiers qtMods, NSString *characters,
- NSString *charactersIgnoringModifiers, QString &text);
+ Qt::KeyboardModifiers queryKeyboardModifiers() const override;
+ QList<QKeyCombination> possibleKeyCombinations(const QKeyEvent *event) const override;
+
#ifdef Q_OS_MACOS
static Qt::KeyboardModifiers fromCocoaModifiers(NSEventModifierFlags cocoaModifiers);
static NSEventModifierFlags toCocoaModifiers(Qt::KeyboardModifiers);
@@ -41,6 +42,9 @@ public:
static QChar toCocoaKey(Qt::Key key);
static Qt::Key fromCocoaKey(QChar keyCode);
#else
+ static Qt::Key fromNSString(Qt::KeyboardModifiers qtMods, NSString *characters,
+ NSString *charactersIgnoringModifiers, QString &text);
+
static Qt::Key fromUIKitKey(NSString *keyCode);
static Qt::KeyboardModifiers fromUIKitModifiers(ulong uikitModifiers);
static ulong toUIKitModifiers(Qt::KeyboardModifiers);
diff --git a/src/gui/platform/darwin/qmacmime.mm b/src/gui/platform/darwin/qmacmime.mm
deleted file mode 100644
index fe323bf60d..0000000000
--- a/src/gui/platform/darwin/qmacmime.mm
+++ /dev/null
@@ -1,1013 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include <ImageIO/ImageIO.h>
-
-#include <QtCore/qsystemdetection.h>
-#include <QtCore/qurl.h>
-#include <QtGui/qimage.h>
-#include <QtCore/qmimedata.h>
-#include <QtCore/qstringconverter.h>
-
-#if defined(Q_OS_MACOS)
-#import <AppKit/AppKit.h>
-#else
-#include <MobileCoreServices/MobileCoreServices.h>
-#endif
-
-#if defined(QT_PLATFORM_UIKIT)
-#import <UIKit/UIKit.h>
-#endif
-
-#include "qmacmime_p.h"
-#include "qguiapplication.h"
-#include "private/qcore_mac_p.h"
-
-QT_BEGIN_NAMESPACE
-
-using namespace Qt::StringLiterals;
-
-typedef QList<QMacInternalPasteboardMime*> MimeList;
-Q_GLOBAL_STATIC(MimeList, globalMimeList)
-Q_GLOBAL_STATIC(QStringList, globalDraggedTypesList)
-
-void qt_mac_addToGlobalMimeList(QMacInternalPasteboardMime *macMime)
-{
- // globalMimeList is in decreasing priority order. Recently added
- // converters take prioity over previously added converters: prepend
- // to the list.
- globalMimeList()->prepend(macMime);
-}
-
-void qt_mac_removeFromGlobalMimeList(QMacInternalPasteboardMime *macMime)
-{
- if (!QGuiApplication::closingDown())
- globalMimeList()->removeAll(macMime);
-}
-
-/*!
- \fn void qRegisterDraggedTypes(const QStringList &types)
- \relates QMacPasteboardMime
-
- Registers the given \a types as custom pasteboard types.
-
- This function should be called to enable the Drag and Drop events
- for custom pasteboard types on Cocoa implementations. This is required
- in addition to a QMacPasteboardMime subclass implementation. By default
- drag and drop is enabled for all standard pasteboard types.
-
- \sa QMacPasteboardMime
-*/
-void qt_mac_registerDraggedTypes(const QStringList &types)
-{
- (*globalDraggedTypesList()) += types;
-}
-
-const QStringList& qt_mac_enabledDraggedTypes()
-{
- return (*globalDraggedTypesList());
-}
-
-/*****************************************************************************
- QDnD debug facilities
- *****************************************************************************/
-//#define DEBUG_MIME_MAPS
-
-/*!
- \class QMacInternalPasteboardMime
- \internal
- \brief The QMacPasteboardMime class converts between a MIME type and a
- \l{http://developer.apple.com/macosx/uniformtypeidentifiers.html}{Uniform
- Type Identifier (UTI)} format.
- \since 4.2
-
- \ingroup draganddrop
- \inmodule QtWidgets
-
- Qt's drag and drop and clipboard facilities use the MIME
- standard. On X11, this maps trivially to the Xdnd protocol. On
- Mac, although some applications use MIME to describe clipboard
- contents, it is more common to use Apple's UTI format.
-
- QMacPasteboardMime's role is to bridge the gap between MIME and UTI;
- By subclasses this class, one can extend Qt's drag and drop
- and clipboard handling to convert to and from unsupported, or proprietary, UTI formats.
-
- A subclass of QMacPasteboardMime will automatically be registered, and active, upon instantiation.
-
- Qt has predefined support for the following UTIs:
- \list
- \li public.utf8-plain-text - converts to "text/plain"
- \li public.utf16-plain-text - converts to "text/plain"
- \li public.text - converts to "text/plain"
- \li public.html - converts to "text/html"
- \li public.url - converts to "text/uri-list"
- \li public.file-url - converts to "text/uri-list"
- \li public.tiff - converts to "application/x-qt-image"
- \li public.vcard - converts to "text/plain"
- \li com.apple.traditional-mac-plain-text - converts to "text/plain"
- \li com.apple.pict - converts to "application/x-qt-image"
- \endlist
-
- When working with MIME data, Qt will iterate through all instances of QMacPasteboardMime to
- find an instance that can convert to, or from, a specific MIME type. It will do this by calling
- canConvert() on each instance, starting with (and choosing) the last created instance first.
- The actual conversions will be done by using convertToMime() and convertFromMime().
-
- \note The API uses the term "flavor" in some cases. This is for backwards
- compatibility reasons, and should now be understood as UTIs.
-*/
-
-/*
- \enum QMacPasteboardMime::QMacPasteboardMimeType
- \internal
-*/
-
-/*
- Constructs a new conversion object of type \a t, adding it to the
- globally accessed list of available converters.
-*/
-QMacInternalPasteboardMime::QMacInternalPasteboardMime(char t) : type(t)
-{
- qt_mac_addToGlobalMimeList(this);
-}
-
-/*
- Destroys a conversion object, removing it from the global
- list of available converters.
-*/
-QMacInternalPasteboardMime::~QMacInternalPasteboardMime()
-{
- qt_mac_removeFromGlobalMimeList(this);
-}
-
-/*
- Returns the item count for the given \a mimeData
-*/
-int QMacInternalPasteboardMime::count(QMimeData *mimeData)
-{
- Q_UNUSED(mimeData);
- return 1;
-}
-
-class QMacPasteboardMimeAny : public QMacInternalPasteboardMime {
-private:
-
-public:
- QMacPasteboardMimeAny() : QMacInternalPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) {
- }
- ~QMacPasteboardMimeAny() {
- }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeAny::convertorName()
-{
- return "Any-Mime"_L1;
-}
-
-QString QMacPasteboardMimeAny::flavorFor(const QString &mime)
-{
- // do not handle the mime type name in the drag pasteboard
- if (mime == "application/x-qt-mime-type-name"_L1)
- return QString();
- QString ret = "com.trolltech.anymime."_L1 + mime;
- return ret.replace(u'/', "--"_L1);
-}
-
-QString QMacPasteboardMimeAny::mimeFor(QString flav)
-{
- const QString any_prefix = "com.trolltech.anymime."_L1;
- if (flav.size() > any_prefix.length() && flav.startsWith(any_prefix))
- return flav.mid(any_prefix.length()).replace("--"_L1, "/"_L1);
- return QString();
-}
-
-bool QMacPasteboardMimeAny::canConvert(const QString &mime, QString flav)
-{
- return mimeFor(flav) == mime;
-}
-
-QVariant QMacPasteboardMimeAny::convertToMime(const QString &mime, QList<QByteArray> data, QString)
-{
- if (data.count() > 1)
- qWarning("QMacPasteboardMimeAny: Cannot handle multiple member data");
- QVariant ret;
- if (mime == "text/plain"_L1)
- ret = QString::fromUtf8(data.first());
- else
- ret = data.first();
- return ret;
-}
-
-QList<QByteArray> QMacPasteboardMimeAny::convertFromMime(const QString &mime, QVariant data, QString)
-{
- QList<QByteArray> ret;
- if (mime == "text/plain"_L1)
- ret.append(data.toString().toUtf8());
- else
- ret.append(data.toByteArray());
- return ret;
-}
-
-class QMacPasteboardMimeTypeName : public QMacInternalPasteboardMime {
-private:
-
-public:
- QMacPasteboardMimeTypeName() : QMacInternalPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) {
- }
- ~QMacPasteboardMimeTypeName() {
- }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeTypeName::convertorName()
-{
- return "Qt-Mime-Type"_L1;
-}
-
-QString QMacPasteboardMimeTypeName::flavorFor(const QString &mime)
-{
- if (mime == "application/x-qt-mime-type-name"_L1)
- return "com.trolltech.qt.MimeTypeName"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimeTypeName::mimeFor(QString)
-{
- return QString();
-}
-
-bool QMacPasteboardMimeTypeName::canConvert(const QString &, QString)
-{
- return false;
-}
-
-QVariant QMacPasteboardMimeTypeName::convertToMime(const QString &, QList<QByteArray>, QString)
-{
- QVariant ret;
- return ret;
-}
-
-QList<QByteArray> QMacPasteboardMimeTypeName::convertFromMime(const QString &, QVariant, QString)
-{
- QList<QByteArray> ret;
- ret.append(QString("x-qt-mime-type-name"_L1).toUtf8());
- return ret;
-}
-
-class QMacPasteboardMimePlainTextFallback : public QMacInternalPasteboardMime {
-public:
- QMacPasteboardMimePlainTextFallback() : QMacInternalPasteboardMime(MIME_ALL) { }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimePlainTextFallback::convertorName()
-{
- return "PlainText (public.text)"_L1;
-}
-
-QString QMacPasteboardMimePlainTextFallback::flavorFor(const QString &mime)
-{
- if (mime == "text/plain"_L1)
- return "public.text"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimePlainTextFallback::mimeFor(QString flav)
-{
- if (flav == "public.text"_L1)
- return "text/plain"_L1;
- return QString();
-}
-
-bool QMacPasteboardMimePlainTextFallback::canConvert(const QString &mime, QString flav)
-{
- return mime == mimeFor(flav);
-}
-
-QVariant QMacPasteboardMimePlainTextFallback::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor)
-{
- if (data.count() > 1)
- qWarning("QMacPasteboardMimePlainTextFallback: Cannot handle multiple member data");
-
- if (flavor == "public.text"_L1) {
- // Note that public.text is documented by Apple to have an undefined encoding. From
- // testing it seems that utf8 is normally used, at least by Safari on iOS.
- const QByteArray &firstData = data.first();
- return QString(QCFString(CFStringCreateWithBytes(kCFAllocatorDefault,
- reinterpret_cast<const UInt8 *>(firstData.constData()),
- firstData.size(), kCFStringEncodingUTF8, false)));
- } else {
- qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype));
- }
- return QVariant();
-}
-
-QList<QByteArray> QMacPasteboardMimePlainTextFallback::convertFromMime(const QString &, QVariant data, QString flavor)
-{
- QList<QByteArray> ret;
- QString string = data.toString();
- if (flavor == "public.text"_L1)
- ret.append(string.toUtf8());
- return ret;
-}
-
-class QMacPasteboardMimeUnicodeText : public QMacInternalPasteboardMime {
-public:
- QMacPasteboardMimeUnicodeText() : QMacInternalPasteboardMime(MIME_ALL) { }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeUnicodeText::convertorName()
-{
- return "UnicodeText"_L1;
-}
-
-QString QMacPasteboardMimeUnicodeText::flavorFor(const QString &mime)
-{
- if (mime == "text/plain"_L1)
- return "public.utf16-plain-text"_L1;
- int i = mime.indexOf("charset="_L1);
- if (i >= 0) {
- QString cs(mime.mid(i+8).toLower());
- i = cs.indexOf(u';');
- if (i>=0)
- cs = cs.left(i);
- if (cs == "system"_L1)
- return "public.utf8-plain-text"_L1;
- else if (cs == "iso-10646-ucs-2"_L1 || cs == "utf16"_L1)
- return "public.utf16-plain-text"_L1;
- }
- return QString();
-}
-
-QString QMacPasteboardMimeUnicodeText::mimeFor(QString flav)
-{
- if (flav == "public.utf16-plain-text"_L1 || flav == "public.utf8-plain-text"_L1)
- return "text/plain"_L1;
- return QString();
-}
-
-bool QMacPasteboardMimeUnicodeText::canConvert(const QString &mime, QString flav)
-{
- return (mime == "text/plain"_L1
- && (flav == "public.utf8-plain-text"_L1 || (flav == "public.utf16-plain-text"_L1)));
-}
-
-QVariant QMacPasteboardMimeUnicodeText::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor)
-{
- if (data.count() > 1)
- qWarning("QMacPasteboardMimeUnicodeText: Cannot handle multiple member data");
- const QByteArray &firstData = data.first();
- // I can only handle two types (system and unicode) so deal with them that way
- QVariant ret;
- if (flavor == "public.utf8-plain-text"_L1) {
- ret = QString::fromUtf8(firstData);
- } else if (flavor == "public.utf16-plain-text"_L1) {
- QString str = QStringDecoder(QStringDecoder::Utf16)(firstData);
- ret = str;
- } else {
- qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype));
- }
- return ret;
-}
-
-QList<QByteArray> QMacPasteboardMimeUnicodeText::convertFromMime(const QString &, QVariant data, QString flavor)
-{
- QList<QByteArray> ret;
- QString string = data.toString();
- if (flavor == "public.utf8-plain-text"_L1)
- ret.append(string.toUtf8());
- else if (flavor == "public.utf16-plain-text"_L1) {
- QStringEncoder::Flags f;
-#if defined(Q_OS_MACOS)
- // Some applications such as Microsoft Excel, don't deal well with
- // a BOM present, so we follow the traditional approach of Qt on
- // macOS to not generate public.utf16-plain-text with a BOM.
- f = QStringEncoder::Flag::Default;
-#else
- // Whereas iOS applications will fail to paste if we do _not_
- // include a BOM in the public.utf16-plain-text content, most
- // likely due to converting the data using NSUTF16StringEncoding
- // which assumes big-endian byte order if there is no BOM.
- f = QStringEncoder::Flag::WriteBom;
-#endif
- QStringEncoder encoder(QStringEncoder::Utf16, f);
- ret.append(encoder(string));
- }
- return ret;
-}
-
-class QMacPasteboardMimeHTMLText : public QMacInternalPasteboardMime {
-public:
- QMacPasteboardMimeHTMLText() : QMacInternalPasteboardMime(MIME_ALL) { }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeHTMLText::convertorName()
-{
- return "HTML"_L1;
-}
-
-QString QMacPasteboardMimeHTMLText::flavorFor(const QString &mime)
-{
- if (mime == "text/html"_L1)
- return "public.html"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimeHTMLText::mimeFor(QString flav)
-{
- if (flav == "public.html"_L1)
- return "text/html"_L1;
- return QString();
-}
-
-bool QMacPasteboardMimeHTMLText::canConvert(const QString &mime, QString flav)
-{
- return flavorFor(mime) == flav;
-}
-
-QVariant QMacPasteboardMimeHTMLText::convertToMime(const QString &mimeType, QList<QByteArray> data, QString flavor)
-{
- if (!canConvert(mimeType, flavor))
- return QVariant();
- if (data.count() > 1)
- qWarning("QMacPasteboardMimeHTMLText: Cannot handle multiple member data");
- return data.first();
-}
-
-QList<QByteArray> QMacPasteboardMimeHTMLText::convertFromMime(const QString &mime, QVariant data, QString flavor)
-{
- QList<QByteArray> ret;
- if (!canConvert(mime, flavor))
- return ret;
- ret.append(data.toByteArray());
- return ret;
-}
-
-class QMacPasteboardMimeRtfText : public QMacInternalPasteboardMime {
-public:
- QMacPasteboardMimeRtfText() : QMacInternalPasteboardMime(MIME_ALL) { }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeRtfText::convertorName()
-{
- return "Rtf"_L1;
-}
-
-QString QMacPasteboardMimeRtfText::flavorFor(const QString &mime)
-{
- if (mime == "text/html"_L1)
- return "public.rtf"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimeRtfText::mimeFor(QString flav)
-{
- if (flav == "public.rtf"_L1)
- return "text/html"_L1;
- return QString();
-}
-
-bool QMacPasteboardMimeRtfText::canConvert(const QString &mime, QString flav)
-{
- return mime == mimeFor(flav);
-}
-
-QVariant QMacPasteboardMimeRtfText::convertToMime(const QString &mimeType, QList<QByteArray> data, QString flavor)
-{
- if (!canConvert(mimeType, flavor))
- return QVariant();
- if (data.count() > 1)
- qWarning("QMacPasteboardMimeHTMLText: Cannot handle multiple member data");
-
- // Read RTF into to NSAttributedString, then convert the string to HTML
- NSAttributedString *string = [[NSAttributedString alloc] initWithData:data.at(0).toNSData()
- options:@{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType}
- documentAttributes:nil
- error:nil];
-
- NSError *error;
- NSRange range = NSMakeRange(0, [string length]);
- NSDictionary *dict = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType};
- NSData *htmlData = [string dataFromRange:range documentAttributes:dict error:&error];
- return QByteArray::fromNSData(htmlData);
-}
-
-QList<QByteArray> QMacPasteboardMimeRtfText::convertFromMime(const QString &mime, QVariant data, QString flavor)
-{
- QList<QByteArray> ret;
- if (!canConvert(mime, flavor))
- return ret;
-
- NSAttributedString *string = [[NSAttributedString alloc] initWithData:data.toByteArray().toNSData()
- options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType}
- documentAttributes:nil
- error:nil];
-
- NSError *error;
- NSRange range = NSMakeRange(0, [string length]);
- NSDictionary *dict = @{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType};
- NSData *rtfData = [string dataFromRange:range documentAttributes:dict error:&error];
- ret << QByteArray::fromNSData(rtfData);
- return ret;
-}
-
-class QMacPasteboardMimeFileUri : public QMacInternalPasteboardMime {
-public:
- QMacPasteboardMimeFileUri() : QMacInternalPasteboardMime(MIME_ALL) { }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
- int count(QMimeData *mimeData);
-};
-
-QString QMacPasteboardMimeFileUri::convertorName()
-{
- return "FileURL"_L1;
-}
-
-QString QMacPasteboardMimeFileUri::flavorFor(const QString &mime)
-{
- if (mime == "text/uri-list"_L1)
- return "public.file-url"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimeFileUri::mimeFor(QString flav)
-{
- if (flav == "public.file-url"_L1)
- return "text/uri-list"_L1;
- return QString();
-}
-
-bool QMacPasteboardMimeFileUri::canConvert(const QString &mime, QString flav)
-{
- return mime == "text/uri-list"_L1 && flav == "public.file-url"_L1;
-}
-
-QVariant QMacPasteboardMimeFileUri::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
-{
- if (!canConvert(mime, flav))
- return QVariant();
- QList<QVariant> ret;
- for (int i = 0; i < data.size(); ++i) {
- const QByteArray &a = data.at(i);
- NSString *urlString = [[[NSString alloc] initWithBytesNoCopy:(void *)a.data() length:a.size()
- encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease];
- NSURL *nsurl = [NSURL URLWithString:urlString];
- QUrl url;
- // OS X 10.10 sends file references instead of file paths
- if ([nsurl isFileReferenceURL]) {
- url = QUrl::fromNSURL([nsurl filePathURL]);
- } else {
- url = QUrl::fromNSURL(nsurl);
- }
-
- if (url.host().toLower() == "localhost"_L1)
- url.setHost(QString());
-
- url.setPath(url.path().normalized(QString::NormalizationForm_C));
- ret.append(url);
- }
- return QVariant(ret);
-}
-
-QList<QByteArray> QMacPasteboardMimeFileUri::convertFromMime(const QString &mime, QVariant data, QString flav)
-{
- QList<QByteArray> ret;
- if (!canConvert(mime, flav))
- return ret;
- QList<QVariant> urls = data.toList();
- for (int i = 0; i < urls.size(); ++i) {
- QUrl url = urls.at(i).toUrl();
- if (url.scheme().isEmpty())
- url.setScheme("file"_L1);
- if (url.scheme() == "file"_L1) {
- if (url.host().isEmpty())
- url.setHost("localhost"_L1);
- url.setPath(url.path().normalized(QString::NormalizationForm_D));
- }
- if (url.isLocalFile())
- ret.append(url.toEncoded());
- }
- return ret;
-}
-
-int QMacPasteboardMimeFileUri::count(QMimeData *mimeData)
-{
- return mimeData->urls().count();
-}
-
-class QMacPasteboardMimeUrl : public QMacInternalPasteboardMime {
-public:
- QMacPasteboardMimeUrl() : QMacInternalPasteboardMime(MIME_ALL) { }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeUrl::convertorName()
-{
- return "URL"_L1;
-}
-
-QString QMacPasteboardMimeUrl::flavorFor(const QString &mime)
-{
- if (mime.startsWith("text/uri-list"_L1))
- return "public.url"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimeUrl::mimeFor(QString flav)
-{
- if (flav == "public.url"_L1)
- return "text/uri-list"_L1;
- return QString();
-}
-
-bool QMacPasteboardMimeUrl::canConvert(const QString &mime, QString flav)
-{
- return flav == "public.url"_L1
- && mime == "text/uri-list"_L1;
-}
-
-QVariant QMacPasteboardMimeUrl::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
-{
- if (!canConvert(mime, flav))
- return QVariant();
-
- QList<QVariant> ret;
- for (int i=0; i<data.size(); ++i) {
- QUrl url = QUrl::fromEncoded(data.at(i));
- if (url.host().toLower() == "localhost"_L1)
- url.setHost(QString());
- url.setPath(url.path().normalized(QString::NormalizationForm_C));
- ret.append(url);
- }
- return QVariant(ret);
-}
-
-QList<QByteArray> QMacPasteboardMimeUrl::convertFromMime(const QString &mime, QVariant data, QString flav)
-{
- QList<QByteArray> ret;
- if (!canConvert(mime, flav))
- return ret;
-
- QList<QVariant> urls = data.toList();
- for (int i=0; i<urls.size(); ++i) {
- QUrl url = urls.at(i).toUrl();
- if (url.scheme().isEmpty())
- url.setScheme("file"_L1);
- if (url.scheme() == "file"_L1) {
- if (url.host().isEmpty())
- url.setHost("localhost"_L1);
- url.setPath(url.path().normalized(QString::NormalizationForm_D));
- }
- ret.append(url.toEncoded());
- }
- return ret;
-}
-
-class QMacPasteboardMimeVCard : public QMacInternalPasteboardMime
-{
-public:
- QMacPasteboardMimeVCard() : QMacInternalPasteboardMime(MIME_ALL){ }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeVCard::convertorName()
-{
- return "VCard"_L1;
-}
-
-bool QMacPasteboardMimeVCard::canConvert(const QString &mime, QString flav)
-{
- return mimeFor(flav) == mime;
-}
-
-QString QMacPasteboardMimeVCard::flavorFor(const QString &mime)
-{
- if (mime.startsWith("text/vcard"_L1))
- return "public.vcard"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimeVCard::mimeFor(QString flav)
-{
- if (flav == "public.vcard"_L1)
- return "text/vcard"_L1;
- return QString();
-}
-
-QVariant QMacPasteboardMimeVCard::convertToMime(const QString &mime, QList<QByteArray> data, QString)
-{
- QByteArray cards;
- if (mime == "text/vcard"_L1) {
- for (int i=0; i<data.size(); ++i)
- cards += data[i];
- }
- return QVariant(cards);
-}
-
-QList<QByteArray> QMacPasteboardMimeVCard::convertFromMime(const QString &mime, QVariant data, QString)
-{
- QList<QByteArray> ret;
- if (mime == "text/vcard"_L1)
- ret.append(data.toString().toUtf8());
- return ret;
-}
-
-extern QImage qt_mac_toQImage(CGImageRef image);
-extern CGImageRef qt_mac_toCGImage(const QImage &qImage);
-
-class QMacPasteboardMimeTiff : public QMacInternalPasteboardMime {
-public:
- QMacPasteboardMimeTiff() : QMacInternalPasteboardMime(MIME_ALL) { }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeTiff::convertorName()
-{
- return "Tiff"_L1;
-}
-
-QString QMacPasteboardMimeTiff::flavorFor(const QString &mime)
-{
- if (mime.startsWith("application/x-qt-image"_L1))
- return "public.tiff"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimeTiff::mimeFor(QString flav)
-{
- if (flav == "public.tiff"_L1)
- return "application/x-qt-image"_L1;
- return QString();
-}
-
-bool QMacPasteboardMimeTiff::canConvert(const QString &mime, QString flav)
-{
- return flav == "public.tiff"_L1 && mime == "application/x-qt-image"_L1;
-}
-
-QVariant QMacPasteboardMimeTiff::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
-{
- if (data.count() > 1)
- qWarning("QMacPasteboardMimeTiff: Cannot handle multiple member data");
-
- if (!canConvert(mime, flav))
- return QVariant();
-
- QCFType<CFDataRef> tiffData = data.first().toRawCFData();
- QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithData(tiffData, 0);
-
- if (QCFType<CGImageRef> image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0))
- return QVariant(qt_mac_toQImage(image));
-
- return QVariant();
-}
-
-QList<QByteArray> QMacPasteboardMimeTiff::convertFromMime(const QString &mime, QVariant variant, QString flav)
-{
- if (!canConvert(mime, flav))
- return QList<QByteArray>();
-
- QCFType<CFMutableDataRef> data = CFDataCreateMutable(0, 0);
- QCFType<CGImageDestinationRef> imageDestination = CGImageDestinationCreateWithData(data, kUTTypeTIFF, 1, 0);
-
- if (!imageDestination)
- return QList<QByteArray>();
-
- QImage img = qvariant_cast<QImage>(variant);
- NSDictionary *props = @{
- static_cast<NSString *>(kCGImagePropertyPixelWidth): @(img.width()),
- static_cast<NSString *>(kCGImagePropertyPixelHeight): @(img.height())
- };
-
- CGImageDestinationAddImage(imageDestination, qt_mac_toCGImage(img), static_cast<CFDictionaryRef>(props));
- CGImageDestinationFinalize(imageDestination);
-
- return QList<QByteArray>() << QByteArray::fromCFData(data);
-}
-
-/*!
- \internal
-
- This is an internal function.
-*/
-void QMacInternalPasteboardMime::initializeMimeTypes()
-{
- if (globalMimeList()->isEmpty()) {
- // Create QMacPasteboardMimeAny first to put it at the end of globalMimeList
- // with lowest priority. (the constructor prepends to the list)
- new QMacPasteboardMimeAny;
-
- //standard types that we wrap
- new QMacPasteboardMimeTiff;
- new QMacPasteboardMimePlainTextFallback;
- new QMacPasteboardMimeUnicodeText;
- new QMacPasteboardMimeRtfText;
- new QMacPasteboardMimeHTMLText;
- new QMacPasteboardMimeFileUri;
- new QMacPasteboardMimeUrl;
- new QMacPasteboardMimeTypeName;
- new QMacPasteboardMimeVCard;
- }
-}
-
-/*!
- \internal
-*/
-void QMacInternalPasteboardMime::destroyMimeTypes()
-{
- MimeList *mimes = globalMimeList();
- while (!mimes->isEmpty())
- delete mimes->takeFirst();
-}
-
-/*
- Returns the most-recently created QMacPasteboardMime of type \a t that can convert
- between the \a mime and \a flav formats. Returns 0 if no such convertor
- exists.
-*/
-QMacInternalPasteboardMime*
-QMacInternalPasteboardMime::convertor(uchar t, const QString &mime, QString flav)
-{
- MimeList *mimes = globalMimeList();
- for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) {
-#ifdef DEBUG_MIME_MAPS
- qDebug("QMacPasteboardMime::convertor: seeing if %s (%d) can convert %s to %d[%c%c%c%c] [%d]",
- (*it)->convertorName().toLatin1().constData(),
- (*it)->type & t, mime.toLatin1().constData(),
- flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF,
- (*it)->canConvert(mime,flav));
- for (int i = 0; i < (*it)->countFlavors(); ++i) {
- int f = (*it)->flavor(i);
- qDebug(" %d) %d[%c%c%c%c] [%s]", i, f,
- (f >> 24) & 0xFF, (f >> 16) & 0xFF, (f >> 8) & 0xFF, (f) & 0xFF,
- (*it)->convertorName().toLatin1().constData());
- }
-#endif
- if (((*it)->type & t) && (*it)->canConvert(mime, flav))
- return (*it);
- }
- return 0;
-}
-/*
- Returns a MIME type of type \a t for \a flav, or 0 if none exists.
-*/
-QString QMacInternalPasteboardMime::flavorToMime(uchar t, QString flav)
-{
- MimeList *mimes = globalMimeList();
- for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) {
-#ifdef DEBUG_MIME_MAPS
- qDebug("QMacMIme::flavorToMime: attempting %s (%d) for flavor %d[%c%c%c%c] [%s]",
- (*it)->convertorName().toLatin1().constData(),
- (*it)->type & t, flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF,
- (*it)->mimeFor(flav).toLatin1().constData());
-
-#endif
- if ((*it)->type & t) {
- QString mimeType = (*it)->mimeFor(flav);
- if (!mimeType.isNull())
- return mimeType;
- }
- }
- return QString();
-}
-
-/*
- Returns a list of all currently defined QMacPasteboardMime objects of type \a t.
-*/
-QList<QMacInternalPasteboardMime*> QMacInternalPasteboardMime::all(uchar t)
-{
- MimeList ret;
- MimeList *mimes = globalMimeList();
- for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) {
- if ((*it)->type & t)
- ret.append((*it));
- }
- return ret;
-}
-
-
-/*
- \fn QString QMacPasteboardMime::convertorName()
-
- Returns a name for the convertor.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-/*
- \fn bool QMacPasteboardMime::canConvert(const QString &mime, QString flav)
-
- Returns \c true if the convertor can convert (both ways) between
- \a mime and \a flav; otherwise returns \c false.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-/*
- \fn QString QMacPasteboardMime::mimeFor(QString flav)
-
- Returns the MIME UTI used for Mac flavor \a flav, or 0 if this
- convertor does not support \a flav.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-/*
- \fn QString QMacPasteboardMime::flavorFor(const QString &mime)
-
- Returns the Mac UTI used for MIME type \a mime, or 0 if this
- convertor does not support \a mime.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-/*
- \fn QVariant QMacPasteboardMime::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
-
- Returns \a data converted from Mac UTI \a flav to MIME type \a
- mime.
-
- Note that Mac flavors must all be self-terminating. The input \a
- data may contain trailing data.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-/*
- \fn QList<QByteArray> QMacPasteboardMime::convertFromMime(const QString &mime, QVariant data, QString flav)
-
- Returns \a data converted from MIME type \a mime
- to Mac UTI \a flav.
-
- Note that Mac flavors must all be self-terminating. The return
- value may contain trailing data.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-QT_END_NAMESPACE
diff --git a/src/gui/platform/darwin/qmacmime_p.h b/src/gui/platform/darwin/qmacmime_p.h
deleted file mode 100644
index bd926f59fe..0000000000
--- a/src/gui/platform/darwin/qmacmime_p.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef QMACMIME_H
-#define QMACMIME_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/private/qtguiglobal_p.h>
-
-#include <CoreFoundation/CoreFoundation.h>
-
-QT_BEGIN_NAMESPACE
-
-// Duplicate of QMacPasteboardMime in QtMacExtras. Keep in sync!
-class Q_GUI_EXPORT QMacInternalPasteboardMime {
- char type;
-public:
- enum QMacPasteboardMimeType { MIME_DND=0x01,
- MIME_CLIP=0x02,
- MIME_QT_CONVERTOR=0x04,
- MIME_QT3_CONVERTOR=0x08,
- MIME_ALL=MIME_DND|MIME_CLIP
- };
- explicit QMacInternalPasteboardMime(char);
- virtual ~QMacInternalPasteboardMime();
-
- static void initializeMimeTypes();
- static void destroyMimeTypes();
-
- static QList<QMacInternalPasteboardMime*> all(uchar);
- static QMacInternalPasteboardMime *convertor(uchar, const QString &mime, QString flav);
- static QString flavorToMime(uchar, QString flav);
-
- virtual QString convertorName() = 0;
-
- virtual bool canConvert(const QString &mime, QString flav) = 0;
- virtual QString mimeFor(QString flav) = 0;
- virtual QString flavorFor(const QString &mime) = 0;
- virtual QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav) = 0;
- virtual QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav) = 0;
- virtual int count(QMimeData *mimeData);
-};
-
-Q_GUI_EXPORT void qt_mac_addToGlobalMimeList(QMacInternalPasteboardMime *macMime);
-Q_GUI_EXPORT void qt_mac_removeFromGlobalMimeList(QMacInternalPasteboardMime *macMime);
-Q_GUI_EXPORT void qt_mac_registerDraggedTypes(const QStringList &types);
-Q_GUI_EXPORT const QStringList& qt_mac_enabledDraggedTypes();
-
-QT_END_NAMESPACE
-
-#endif
-
diff --git a/src/gui/platform/darwin/qmacmimeregistry.mm b/src/gui/platform/darwin/qmacmimeregistry.mm
new file mode 100644
index 0000000000..6710a0656f
--- /dev/null
+++ b/src/gui/platform/darwin/qmacmimeregistry.mm
@@ -0,0 +1,118 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtCore/qmimedata.h>
+
+#include "qutimimeconverter.h"
+#include "qmacmimeregistry_p.h"
+#include "qguiapplication.h"
+#include "private/qcore_mac_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace QMacMimeRegistry {
+
+typedef QList<QUtiMimeConverter*> MimeList;
+Q_GLOBAL_STATIC(MimeList, globalMimeList)
+Q_GLOBAL_STATIC(QStringList, globalDraggedTypesList)
+
+// implemented in qutimimeconverter.mm
+void registerBuiltInTypes();
+
+void registerDraggedTypes(const QStringList &types)
+{
+ (*globalDraggedTypesList()) += types;
+}
+
+const QStringList& enabledDraggedTypes()
+{
+ return (*globalDraggedTypesList());
+}
+
+/*****************************************************************************
+ QDnD debug facilities
+ *****************************************************************************/
+//#define DEBUG_MIME_MAPS
+
+/*!
+ \class QMacMimeRegistry
+ \internal
+ \ingroup draganddrop
+*/
+
+/*!
+ \internal
+
+ This is an internal function.
+*/
+void initializeMimeTypes()
+{
+ if (globalMimeList()->isEmpty())
+ registerBuiltInTypes();
+}
+
+/*!
+ \internal
+*/
+void destroyMimeTypes()
+{
+ MimeList *mimes = globalMimeList();
+ while (!mimes->isEmpty())
+ delete mimes->takeFirst();
+}
+
+/*
+ Returns a MIME type of for scope \a scope for \a uti, or \nullptr if none exists.
+*/
+QString flavorToMime(QUtiMimeConverter::HandlerScope scope, const QString &uti)
+{
+ const MimeList &mimes = *globalMimeList();
+ for (const auto &mime : mimes) {
+ const bool relevantScope = mime->scope() & scope;
+#ifdef DEBUG_MIME_MAPS
+ qDebug("QMacMimeRegistry::flavorToMime: attempting (%d) for uti %s [%s]",
+ relevantScope, qPrintable(uti), qPrintable((*it)->mimeForUti(uti)));
+#endif
+ if (relevantScope) {
+ const QString mimeType = mime->mimeForUti(uti);
+ if (!mimeType.isNull())
+ return mimeType;
+ }
+ }
+ return QString();
+}
+
+void registerMimeConverter(QUtiMimeConverter *macMime)
+{
+ // globalMimeList is in decreasing priority order. Recently added
+ // converters take prioity over previously added converters: prepend
+ // to the list.
+ globalMimeList()->prepend(macMime);
+}
+
+void unregisterMimeConverter(QUtiMimeConverter *macMime)
+{
+ if (!QGuiApplication::closingDown())
+ globalMimeList()->removeAll(macMime);
+}
+
+
+/*
+ Returns a list of all currently defined QUtiMimeConverter objects for scope \a scope.
+*/
+QList<QUtiMimeConverter *> all(QUtiMimeConverter::HandlerScope scope)
+{
+ MimeList ret;
+ const MimeList &mimes = *globalMimeList();
+ for (const auto &mime : mimes) {
+ if (mime->scope() & scope)
+ ret.append(mime);
+ }
+ return ret;
+}
+
+} // namespace QMacMimeRegistry
+
+QT_END_NAMESPACE
diff --git a/src/gui/platform/darwin/qmacmimeregistry_p.h b/src/gui/platform/darwin/qmacmimeregistry_p.h
new file mode 100644
index 0000000000..5928b81959
--- /dev/null
+++ b/src/gui/platform/darwin/qmacmimeregistry_p.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QMACMIMEREGISTRY_H
+#define QMACMIMEREGISTRY_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/private/qtguiglobal_p.h>
+#include <QtGui/qutimimeconverter.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QMacMimeRegistry {
+ Q_GUI_EXPORT void initializeMimeTypes();
+ Q_GUI_EXPORT void destroyMimeTypes();
+
+ Q_GUI_EXPORT void registerMimeConverter(QUtiMimeConverter *);
+ Q_GUI_EXPORT void unregisterMimeConverter(QUtiMimeConverter *);
+
+ Q_GUI_EXPORT QList<QUtiMimeConverter *> all(QUtiMimeConverter::HandlerScope scope);
+ Q_GUI_EXPORT QString flavorToMime(QUtiMimeConverter::HandlerScope scope, const QString &flav);
+
+ Q_GUI_EXPORT void registerDraggedTypes(const QStringList &types);
+ Q_GUI_EXPORT const QStringList& enabledDraggedTypes();
+};
+
+QT_END_NAMESPACE
+
+#endif // QMACMIMEREGISTRY_H
diff --git a/src/gui/platform/darwin/qutimimeconverter.h b/src/gui/platform/darwin/qutimimeconverter.h
new file mode 100644
index 0000000000..e9297b5fa0
--- /dev/null
+++ b/src/gui/platform/darwin/qutimimeconverter.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QUTIMIMECONVERTER_H
+#define QUTIMIMECONVERTER_H
+
+#include <QtGui/qtguiglobal.h>
+
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class QByteArray;
+class QString;
+class QVariant;
+class QMimeData;
+
+class Q_GUI_EXPORT QUtiMimeConverter
+{
+ Q_DISABLE_COPY(QUtiMimeConverter)
+public:
+ enum class HandlerScopeFlag : uint8_t
+ {
+ DnD = 0x01,
+ Clipboard = 0x02,
+ Qt_compatible = 0x04,
+ Qt3_compatible = 0x08,
+ All = DnD|Clipboard,
+ AllCompatible = All|Qt_compatible
+ };
+ Q_DECLARE_FLAGS(HandlerScope, HandlerScopeFlag)
+
+ QUtiMimeConverter();
+ virtual ~QUtiMimeConverter();
+
+ HandlerScope scope() const { return m_scope; }
+ bool canConvert(const QString &mime, const QString &uti) const { return mimeForUti(uti) == mime; }
+
+ // for converting from Qt
+ virtual QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data, const QString &uti) const = 0;
+ virtual QString utiForMime(const QString &mime) const = 0;
+
+ // for converting to Qt
+ virtual QString mimeForUti(const QString &uti) const = 0;
+ virtual QVariant convertToMime(const QString &mime, const QList<QByteArray> &data, const QString &uti) const = 0;
+ virtual int count(const QMimeData *mimeData) const;
+
+private:
+ friend class QMacMimeTypeName;
+ friend class QMacMimeAny;
+
+ explicit QUtiMimeConverter(HandlerScope scope);
+
+ const HandlerScope m_scope;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QUtiMimeConverter::HandlerScope)
+
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/gui/platform/darwin/qutimimeconverter.mm b/src/gui/platform/darwin/qutimimeconverter.mm
new file mode 100644
index 0000000000..ee643fd0c6
--- /dev/null
+++ b/src/gui/platform/darwin/qutimimeconverter.mm
@@ -0,0 +1,823 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <ImageIO/ImageIO.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <UniformTypeIdentifiers/UTCoreTypes.h>
+
+#include <QtCore/qsystemdetection.h>
+#include <QtCore/qurl.h>
+#include <QtGui/qimage.h>
+#include <QtCore/qmimedata.h>
+#include <QtCore/qstringconverter.h>
+
+#if defined(Q_OS_MACOS)
+#import <AppKit/AppKit.h>
+#else
+#include <MobileCoreServices/MobileCoreServices.h>
+#endif
+
+#if defined(QT_PLATFORM_UIKIT)
+#import <UIKit/UIKit.h>
+#endif
+
+#include "qutimimeconverter.h"
+#include "qmacmimeregistry_p.h"
+#include "qguiapplication.h"
+#include "private/qcore_mac_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+/*****************************************************************************
+ QDnD debug facilities
+ *****************************************************************************/
+//#define DEBUG_MIME_MAPS
+
+/*!
+ \class QUtiMimeConverter
+ \brief The QUtiMimeConverter class converts between a MIME type and a
+ \l{https://developer.apple.com/documentation/uniformtypeidentifiers}
+ {Uniform Type Identifier (UTI)} format.
+ \since 6.5
+
+ \ingroup draganddrop
+ \inmodule QtGui
+
+ Qt's drag and drop and clipboard facilities use the MIME
+ standard. On X11, this maps trivially to the Xdnd protocol. On
+ Mac, although some applications use MIME to describe clipboard
+ contents, it is more common to use Apple's UTI format.
+
+ QUtiMimeConverter's role is to bridge the gap between MIME and UTI;
+ By subclasses this class, one can extend Qt's drag and drop
+ and clipboard handling to convert to and from unsupported, or proprietary, UTI formats.
+
+ Construct an instance of your converter implementation after instantiating
+ QGuiApplication:
+
+ \code
+ int main(int argc, char **argv)
+ {
+ QGuiApplication app(argc, argv);
+ JsonMimeConverter jsonConverter;
+ }
+ \endcode
+
+ Destroying the instance will unregister the converter and remove support
+ for the conversion. It is also valid to heap-allocate the converter
+ instance; Qt takes ownership and will delete the converter object during
+ QGuiApplication shut-down.
+
+ Qt has predefined support for the following UTIs:
+ \list
+ \li public.utf8-plain-text - converts to "text/plain"
+ \li public.utf16-plain-text - converts to "text/plain"
+ \li public.text - converts to "text/plain"
+ \li public.html - converts to "text/html"
+ \li public.url - converts to "text/uri-list"
+ \li public.file-url - converts to "text/uri-list"
+ \li public.tiff - converts to "application/x-qt-image"
+ \li public.vcard - converts to "text/plain"
+ \li com.apple.traditional-mac-plain-text - converts to "text/plain"
+ \li com.apple.pict - converts to "application/x-qt-image"
+ \endlist
+
+ When working with MIME data, Qt will iterate through all instances of QUtiMimeConverter to find
+ find an instance that can convert to, or from, a specific MIME type. It will do this by calling
+ mimeForUti() or utiForMime() on each instance, starting with (and choosing) the last created
+ instance first. The actual conversions will be done by using convertToMime() and convertFromMime().
+*/
+
+/*!
+ \enum QUtiMimeConverter::HandlerScope
+ \internal
+*/
+
+/*!
+ \internal
+ Constructs a new conversion object of type \a scope, adding it to the
+ globally accessed list of available converters.
+*/
+QUtiMimeConverter::QUtiMimeConverter(HandlerScope scope)
+ : m_scope(scope)
+{
+ QMacMimeRegistry::registerMimeConverter(this);
+}
+
+/*!
+ Constructs a new conversion object and adds it to the
+ globally accessed list of available converters.
+
+ Call this constructor after QGuiApplication has been created.
+*/
+QUtiMimeConverter::QUtiMimeConverter()
+ : QUtiMimeConverter(HandlerScopeFlag::All)
+{
+}
+
+/*!
+ Destroys a conversion object, removing it from the global
+ list of available converters.
+*/
+QUtiMimeConverter::~QUtiMimeConverter()
+{
+ QMacMimeRegistry::unregisterMimeConverter(this);
+}
+
+/*!
+ Returns the item count for the given \a mimeData
+*/
+int QUtiMimeConverter::count(const QMimeData *mimeData) const
+{
+ Q_UNUSED(mimeData);
+ return 1;
+}
+
+/*!
+ \fn bool QUtiMimeConverter::canConvert(const QString &mime, const QString &uti) const
+
+ Returns \c true if the converter can convert (both ways) between
+ \a mime and \a uti; otherwise returns \c false.
+*/
+
+/*!
+ \fn QString QUtiMimeConverter::mimeForUti(const QString &uti) const
+
+ Returns the MIME type used for Mac UTI \a uti, or an empty string if
+ this converter does not support converting from \a uti.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QString QUtiMimeConverter::utiForMime(const QString &mime) const
+
+ Returns the Mac UTI used for MIME type \a mime, or an empty string if
+ this converter does not support converting from \a mime.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QVariant QUtiMimeConverter::convertToMime(const QString &mime,
+ const QList<QByteArray> &data, const QString &uti) const
+
+ Returns \a data converted from Mac UTI \a uti to MIME type \a mime.
+
+ Note that Mac UTIs must all be self-terminating. The input \a data
+ may contain trailing data.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QList<QByteArray> QUtiMimeConverter::convertFromMime(const QString &mime,
+ const QVariant &data, const QString & uti) const
+
+ Returns \a data converted from MIME type \a mime to Mac UTI \a uti.
+
+ Note that Mac UTIs must all be self-terminating. The return
+ value may contain trailing data.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+
+class QMacMimeAny : public QUtiMimeConverter {
+public:
+ QMacMimeAny() : QUtiMimeConverter(HandlerScopeFlag::AllCompatible) {}
+
+ QString utiForMime(const QString &mime) const override;
+ QString mimeForUti(const QString &uti) const override;
+ QVariant convertToMime(const QString &mime, const QList<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data,
+ const QString &uti) const override;
+};
+
+QString QMacMimeAny::utiForMime(const QString &mime) const
+{
+ // do not handle the mime type name in the drag pasteboard
+ if (mime == "application/x-qt-mime-type-name"_L1)
+ return QString();
+ QString ret = "com.trolltech.anymime."_L1 + mime;
+ return ret.replace(u'/', "--"_L1);
+}
+
+QString QMacMimeAny::mimeForUti(const QString &uti) const
+{
+ const QString any_prefix = "com.trolltech.anymime."_L1;
+ if (uti.size() > any_prefix.length() && uti.startsWith(any_prefix))
+ return uti.mid(any_prefix.length()).replace("--"_L1, "/"_L1);
+ return QString();
+}
+
+QVariant QMacMimeAny::convertToMime(const QString &mime, const QList<QByteArray> &data,
+ const QString &) const
+{
+ if (data.count() > 1)
+ qWarning("QMacMimeAny: Cannot handle multiple member data");
+ QVariant ret;
+ if (mime == "text/plain"_L1)
+ ret = QString::fromUtf8(data.first());
+ else
+ ret = data.first();
+ return ret;
+}
+
+QList<QByteArray> QMacMimeAny::convertFromMime(const QString &mime, const QVariant &data,
+ const QString &) const
+{
+ QList<QByteArray> ret;
+ if (mime == "text/plain"_L1)
+ ret.append(data.toString().toUtf8());
+ else
+ ret.append(data.toByteArray());
+ return ret;
+}
+
+class QMacMimeTypeName : public QUtiMimeConverter {
+private:
+
+public:
+ QMacMimeTypeName(): QUtiMimeConverter(HandlerScopeFlag::AllCompatible) {}
+
+ QString utiForMime(const QString &mime) const override;
+ QString mimeForUti(const QString &uti) const override;
+ QVariant convertToMime(const QString &mime, const QList<QByteArray> &data, const QString &uti) const override;
+ QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data, const QString &uti) const override;
+};
+
+QString QMacMimeTypeName::utiForMime(const QString &mime) const
+{
+ if (mime == "application/x-qt-mime-type-name"_L1)
+ return u"com.trolltech.qt.MimeTypeName"_s;
+ return QString();
+}
+
+QString QMacMimeTypeName::mimeForUti(const QString &) const
+{
+ return QString();
+}
+
+QVariant QMacMimeTypeName::convertToMime(const QString &, const QList<QByteArray> &, const QString &) const
+{
+ QVariant ret;
+ return ret;
+}
+
+QList<QByteArray> QMacMimeTypeName::convertFromMime(const QString &, const QVariant &, const QString &) const
+{
+ QList<QByteArray> ret;
+ ret.append(QString("x-qt-mime-type-name"_L1).toUtf8());
+ return ret;
+}
+
+class QMacMimePlainTextFallback : public QUtiMimeConverter
+{
+public:
+ QString utiForMime(const QString &mime) const override;
+ QString mimeForUti(const QString &uti) const override;
+ QVariant convertToMime(const QString &mime, const QList<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data,
+ const QString &uti) const override;
+};
+
+QString QMacMimePlainTextFallback::utiForMime(const QString &mime) const
+{
+ if (mime == "text/plain"_L1)
+ return "public.text"_L1;
+ return QString();
+}
+
+QString QMacMimePlainTextFallback::mimeForUti(const QString &uti) const
+{
+ if (uti == "public.text"_L1)
+ return "text/plain"_L1;
+ return QString();
+}
+
+QVariant
+QMacMimePlainTextFallback::convertToMime(const QString &mimetype,
+ const QList<QByteArray> &data, const QString &uti) const
+{
+ if (data.count() > 1)
+ qWarning("QMacMimePlainTextFallback: Cannot handle multiple member data");
+
+ if (uti == "public.text"_L1) {
+ // Note that public.text is documented by Apple to have an undefined encoding. From
+ // testing it seems that utf8 is normally used, at least by Safari on iOS.
+ const QByteArray &firstData = data.first();
+ return QString(QCFString(CFStringCreateWithBytes(kCFAllocatorDefault,
+ reinterpret_cast<const UInt8 *>(firstData.constData()),
+ firstData.size(), kCFStringEncodingUTF8, false)));
+ } else {
+ qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype));
+ }
+ return QVariant();
+}
+
+QList<QByteArray>
+QMacMimePlainTextFallback::convertFromMime(const QString &, const QVariant &data,
+ const QString &uti) const
+{
+ QList<QByteArray> ret;
+ QString string = data.toString();
+ if (uti == "public.text"_L1)
+ ret.append(string.toUtf8());
+ return ret;
+}
+
+class QMacMimeUnicodeText : public QUtiMimeConverter
+{
+public:
+ QString utiForMime(const QString &mime) const override;
+ QString mimeForUti(const QString &uti) const override;
+ QVariant convertToMime(const QString &mime, const QList<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data,
+ const QString &uti) const override;
+};
+
+QString QMacMimeUnicodeText::utiForMime(const QString &mime) const
+{
+ if (mime == "text/plain"_L1)
+ return "public.utf16-plain-text"_L1;
+ if (qsizetype i = mime.indexOf("charset="_L1); i >= 0) {
+ QString cs(mime.mid(i + 8).toLower());
+ i = cs.indexOf(u';');
+ if (i >= 0)
+ cs = cs.left(i);
+ if (cs == "system"_L1)
+ return "public.utf8-plain-text"_L1;
+ else if (cs == "iso-10646-ucs-2"_L1 || cs == "utf16"_L1)
+ return "public.utf16-plain-text"_L1;
+ }
+ return QString();
+}
+
+QString QMacMimeUnicodeText::mimeForUti(const QString &uti) const
+{
+ if (uti == "public.utf16-plain-text"_L1 || uti == "public.utf8-plain-text"_L1)
+ return "text/plain"_L1;
+ return QString();
+}
+
+QVariant
+QMacMimeUnicodeText::convertToMime(const QString &mimetype,
+ const QList<QByteArray> &data, const QString &uti) const
+{
+ if (data.count() > 1)
+ qWarning("QMacMimeUnicodeText: Cannot handle multiple member data");
+ const QByteArray &firstData = data.first();
+ // I can only handle two types (system and unicode) so deal with them that way
+ QVariant ret;
+ if (uti == "public.utf8-plain-text"_L1) {
+ ret = QString::fromUtf8(firstData);
+ } else if (uti == "public.utf16-plain-text"_L1) {
+ QString str = QStringDecoder(QStringDecoder::Utf16)(firstData);
+ ret = str;
+ } else {
+ qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype));
+ }
+ return ret;
+}
+
+QList<QByteArray>
+QMacMimeUnicodeText::convertFromMime(const QString &, const QVariant &data,
+ const QString &uti) const
+{
+ QList<QByteArray> ret;
+ QString string = data.toString();
+ if (uti == "public.utf8-plain-text"_L1)
+ ret.append(string.toUtf8());
+ else if (uti == "public.utf16-plain-text"_L1) {
+ QStringEncoder::Flags f;
+#if defined(Q_OS_MACOS)
+ // Some applications such as Microsoft Excel, don't deal well with
+ // a BOM present, so we follow the traditional approach of Qt on
+ // macOS to not generate public.utf16-plain-text with a BOM.
+ f = QStringEncoder::Flag::Default;
+#else
+ // Whereas iOS applications will fail to paste if we do _not_
+ // include a BOM in the public.utf16-plain-text content, most
+ // likely due to converting the data using NSUTF16StringEncoding
+ // which assumes big-endian byte order if there is no BOM.
+ f = QStringEncoder::Flag::WriteBom;
+#endif
+ QStringEncoder encoder(QStringEncoder::Utf16, f);
+ ret.append(encoder(string));
+ }
+ return ret;
+}
+
+class QMacMimeHTMLText : public QUtiMimeConverter
+{
+public:
+ QString utiForMime(const QString &mime) const override;
+ QString mimeForUti(const QString &uti) const override;
+ QVariant convertToMime(const QString &mime, const QList<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data,
+ const QString &uti) const override;
+};
+
+QString QMacMimeHTMLText::utiForMime(const QString &mime) const
+{
+ if (mime == "text/html"_L1)
+ return "public.html"_L1;
+ return QString();
+}
+
+QString QMacMimeHTMLText::mimeForUti(const QString &uti) const
+{
+ if (uti == "public.html"_L1)
+ return "text/html"_L1;
+ return QString();
+}
+
+QVariant
+QMacMimeHTMLText::convertToMime(const QString &mimeType,
+ const QList<QByteArray> &data, const QString &uti) const
+{
+ if (!canConvert(mimeType, uti))
+ return QVariant();
+ if (data.count() > 1)
+ qWarning("QMacMimeHTMLText: Cannot handle multiple member data");
+ return data.first();
+}
+
+QList<QByteArray>
+QMacMimeHTMLText::convertFromMime(const QString &mime,
+ const QVariant &data, const QString &uti) const
+{
+ QList<QByteArray> ret;
+ if (!canConvert(mime, uti))
+ return ret;
+ ret.append(data.toByteArray());
+ return ret;
+}
+
+class QMacMimeRtfText : public QUtiMimeConverter
+{
+public:
+ QString utiForMime(const QString &mime) const override;
+ QString mimeForUti(const QString &uti) const override;
+ QVariant convertToMime(const QString &mime, const QList<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data,
+ const QString &uti) const override;
+};
+
+QString QMacMimeRtfText::utiForMime(const QString &mime) const
+{
+ if (mime == "text/html"_L1)
+ return "public.rtf"_L1;
+ return QString();
+}
+
+QString QMacMimeRtfText::mimeForUti(const QString &uti) const
+{
+ if (uti == "public.rtf"_L1)
+ return "text/html"_L1;
+ return QString();
+}
+
+QVariant
+QMacMimeRtfText::convertToMime(const QString &mimeType,
+ const QList<QByteArray> &data, const QString &uti) const
+{
+ if (!canConvert(mimeType, uti))
+ return QVariant();
+ if (data.count() > 1)
+ qWarning("QMacMimeHTMLText: Cannot handle multiple member data");
+
+ // Read RTF into to NSAttributedString, then convert the string to HTML
+ NSAttributedString *string = [[NSAttributedString alloc] initWithData:data.at(0).toNSData()
+ options:@{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType}
+ documentAttributes:nil
+ error:nil];
+
+ NSError *error;
+ NSRange range = NSMakeRange(0, [string length]);
+ NSDictionary *dict = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType};
+ NSData *htmlData = [string dataFromRange:range documentAttributes:dict error:&error];
+ return QByteArray::fromNSData(htmlData);
+}
+
+QList<QByteArray>
+QMacMimeRtfText::convertFromMime(const QString &mime,
+ const QVariant &data, const QString &uti) const
+{
+ QList<QByteArray> ret;
+ if (!canConvert(mime, uti))
+ return ret;
+
+ NSAttributedString *string = [[NSAttributedString alloc] initWithData:data.toByteArray().toNSData()
+ options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType}
+ documentAttributes:nil
+ error:nil];
+
+ NSError *error;
+ NSRange range = NSMakeRange(0, [string length]);
+ NSDictionary *dict = @{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType};
+ NSData *rtfData = [string dataFromRange:range documentAttributes:dict error:&error];
+ ret << QByteArray::fromNSData(rtfData);
+ return ret;
+}
+
+class QMacMimeFileUri : public QUtiMimeConverter
+{
+public:
+ QString utiForMime(const QString &mime) const override;
+ QString mimeForUti(const QString &uti) const override;
+ QVariant convertToMime(const QString &mime, const QList<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data,
+ const QString &uti) const override;
+ int count(const QMimeData *mimeData) const override;
+};
+
+QString QMacMimeFileUri::utiForMime(const QString &mime) const
+{
+ if (mime == "text/uri-list"_L1)
+ return "public.file-url"_L1;
+ return QString();
+}
+
+QString QMacMimeFileUri::mimeForUti(const QString &uti) const
+{
+ if (uti == "public.file-url"_L1)
+ return "text/uri-list"_L1;
+ return QString();
+}
+
+QVariant
+QMacMimeFileUri::convertToMime(const QString &mime,
+ const QList<QByteArray> &data, const QString &uti) const
+{
+ if (!canConvert(mime, uti))
+ return QVariant();
+ QList<QVariant> ret;
+ for (int i = 0; i < data.size(); ++i) {
+ const QByteArray &a = data.at(i);
+ NSString *urlString = [[[NSString alloc] initWithBytesNoCopy:(void *)a.data() length:a.size()
+ encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease];
+ NSURL *nsurl = [NSURL URLWithString:urlString];
+ QUrl url;
+ // OS X 10.10 sends file references instead of file paths
+ if ([nsurl isFileReferenceURL]) {
+ url = QUrl::fromNSURL([nsurl filePathURL]);
+ } else {
+ url = QUrl::fromNSURL(nsurl);
+ }
+
+ if (url.host().toLower() == "localhost"_L1)
+ url.setHost(QString());
+
+ url.setPath(url.path().normalized(QString::NormalizationForm_C));
+ ret.append(url);
+ }
+ return QVariant(ret);
+}
+
+QList<QByteArray>
+QMacMimeFileUri::convertFromMime(const QString &mime,
+ const QVariant &data, const QString &uti) const
+{
+ QList<QByteArray> ret;
+ if (!canConvert(mime, uti))
+ return ret;
+ QList<QVariant> urls = data.toList();
+ for (int i = 0; i < urls.size(); ++i) {
+ QUrl url = urls.at(i).toUrl();
+ if (url.scheme().isEmpty())
+ url.setScheme("file"_L1);
+ if (url.scheme() == "file"_L1) {
+ if (url.host().isEmpty())
+ url.setHost("localhost"_L1);
+ url.setPath(url.path().normalized(QString::NormalizationForm_D));
+ }
+ if (url.isLocalFile())
+ ret.append(url.toEncoded());
+ }
+ return ret;
+}
+
+int QMacMimeFileUri::count(const QMimeData *mimeData) const
+{
+ return mimeData->urls().count();
+}
+
+class QMacMimeUrl : public QUtiMimeConverter
+{
+public:
+ QString utiForMime(const QString &mime) const override;
+ QString mimeForUti(const QString &uti) const override;
+ QVariant convertToMime(const QString &mime, const QList<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data,
+ const QString &uti) const override;
+};
+
+QString QMacMimeUrl::utiForMime(const QString &mime) const
+{
+ if (mime.startsWith("text/uri-list"_L1))
+ return "public.url"_L1;
+ return QString();
+}
+
+QString QMacMimeUrl::mimeForUti(const QString &uti) const
+{
+ if (uti == "public.url"_L1)
+ return "text/uri-list"_L1;
+ return QString();
+}
+
+QVariant QMacMimeUrl::convertToMime(const QString &mime,
+ const QList<QByteArray> &data, const QString &uti) const
+{
+ if (!canConvert(mime, uti))
+ return QVariant();
+
+ QList<QVariant> ret;
+ for (int i=0; i<data.size(); ++i) {
+ QUrl url = QUrl::fromEncoded(data.at(i));
+ if (url.host().toLower() == "localhost"_L1)
+ url.setHost(QString());
+ url.setPath(url.path().normalized(QString::NormalizationForm_C));
+ ret.append(url);
+ }
+ return QVariant(ret);
+}
+
+QList<QByteArray> QMacMimeUrl::convertFromMime(const QString &mime,
+ const QVariant &data, const QString &uti) const
+{
+ QList<QByteArray> ret;
+ if (!canConvert(mime, uti))
+ return ret;
+
+ QList<QVariant> urls = data.toList();
+ for (int i=0; i<urls.size(); ++i) {
+ QUrl url = urls.at(i).toUrl();
+ if (url.scheme().isEmpty())
+ url.setScheme("file"_L1);
+ if (url.scheme() == "file"_L1) {
+ if (url.host().isEmpty())
+ url.setHost("localhost"_L1);
+ url.setPath(url.path().normalized(QString::NormalizationForm_D));
+ }
+ ret.append(url.toEncoded());
+ }
+ return ret;
+}
+
+class QMacMimeVCard : public QUtiMimeConverter
+{
+public:
+ QString utiForMime(const QString &mime) const override;
+ QString mimeForUti(const QString &uti) const override;
+ QVariant convertToMime(const QString &mime, const QList<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data,
+ const QString &uti) const override;
+};
+
+QString QMacMimeVCard::utiForMime(const QString &mime) const
+{
+ if (mime.startsWith("text/vcard"_L1))
+ return "public.vcard"_L1;
+ return QString();
+}
+
+QString QMacMimeVCard::mimeForUti(const QString &uti) const
+{
+ if (uti == "public.vcard"_L1)
+ return "text/vcard"_L1;
+ return QString();
+}
+
+QVariant QMacMimeVCard::convertToMime(const QString &mime,
+ const QList<QByteArray> &data, const QString &uti) const
+{
+ if (!canConvert(mime, uti))
+ return QVariant();
+ QByteArray cards;
+ if (uti == "public.vcard"_L1) {
+ for (int i=0; i<data.size(); ++i)
+ cards += data[i];
+ }
+ return QVariant(cards);
+}
+
+QList<QByteArray> QMacMimeVCard::convertFromMime(const QString &mime,
+ const QVariant &data, const QString &uti) const
+{
+ QList<QByteArray> ret;
+ if (!canConvert(mime, uti))
+ return ret;
+
+ if (mime == "text/vcard"_L1)
+ ret.append(data.toString().toUtf8());
+ return ret;
+}
+
+extern QImage qt_mac_toQImage(CGImageRef image);
+extern CGImageRef qt_mac_toCGImage(const QImage &qImage);
+
+class QMacMimeTiff : public QUtiMimeConverter
+{
+public:
+ QString utiForMime(const QString &mime) const override;
+ QString mimeForUti(const QString &uti) const override;
+ QVariant convertToMime(const QString &mime, const QList<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data,
+ const QString &uti) const override;
+};
+
+QString QMacMimeTiff::utiForMime(const QString &mime) const
+{
+ if (mime.startsWith("application/x-qt-image"_L1))
+ return "public.tiff"_L1;
+ return QString();
+}
+
+QString QMacMimeTiff::mimeForUti(const QString &uti) const
+{
+ if (uti == "public.tiff"_L1)
+ return "application/x-qt-image"_L1;
+ return QString();
+}
+
+QVariant QMacMimeTiff::convertToMime(const QString &mime,
+ const QList<QByteArray> &data, const QString &uti) const
+{
+ if (data.count() > 1)
+ qWarning("QMacMimeTiff: Cannot handle multiple member data");
+
+ if (!canConvert(mime, uti))
+ return QVariant();
+
+ QCFType<CFDataRef> tiffData = data.first().toRawCFData();
+ QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithData(tiffData, 0);
+
+ if (QCFType<CGImageRef> image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0))
+ return QVariant(qt_mac_toQImage(image));
+
+ return QVariant();
+}
+
+QList<QByteArray> QMacMimeTiff::convertFromMime(const QString &mime,
+ const QVariant &variant, const QString &uti) const
+{
+ if (!canConvert(mime, uti))
+ return QList<QByteArray>();
+
+ QCFType<CFMutableDataRef> data = CFDataCreateMutable(0, 0);
+ QCFType<CGImageDestinationRef> imageDestination = CGImageDestinationCreateWithData(data,
+ (CFStringRef)UTTypeTIFF.identifier, 1, 0);
+
+ if (!imageDestination)
+ return QList<QByteArray>();
+
+ QImage img = qvariant_cast<QImage>(variant);
+ NSDictionary *props = @{
+ static_cast<NSString *>(kCGImagePropertyPixelWidth): @(img.width()),
+ static_cast<NSString *>(kCGImagePropertyPixelHeight): @(img.height())
+ };
+
+ CGImageDestinationAddImage(imageDestination, qt_mac_toCGImage(img),
+ static_cast<CFDictionaryRef>(props));
+ CGImageDestinationFinalize(imageDestination);
+
+ return QList<QByteArray>() << QByteArray::fromCFData(data);
+}
+
+namespace QMacMimeRegistry {
+
+void registerBuiltInTypes()
+{
+ // Create QMacMimeAny first to put it at the end of globalMimeList
+ // with lowest priority. (the constructor prepends to the list)
+ new QMacMimeAny;
+
+ //standard types that we wrap
+ new QMacMimeTiff;
+ new QMacMimePlainTextFallback;
+ new QMacMimeUnicodeText;
+ new QMacMimeRtfText;
+ new QMacMimeHTMLText;
+ new QMacMimeFileUri;
+ new QMacMimeUrl;
+ new QMacMimeTypeName;
+ new QMacMimeVCard;
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/platform/ios/PrivacyInfo.xcprivacy b/src/gui/platform/ios/PrivacyInfo.xcprivacy
new file mode 100644
index 0000000000..bde2b167c7
--- /dev/null
+++ b/src/gui/platform/ios/PrivacyInfo.xcprivacy
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>NSPrivacyTracking</key>
+ <false/>
+ <key>NSPrivacyCollectedDataTypes</key>
+ <array/>
+ <key>NSPrivacyTrackingDomains</key>
+ <array/>
+ <key>NSPrivacyAccessedAPITypes</key>
+ <array>
+ <dict>
+ <key>NSPrivacyAccessedAPIType</key>
+ <string>NSPrivacyAccessedAPICategorySystemBootTime</string>
+ <key>NSPrivacyAccessedAPITypeReasons</key>
+ <array>
+ <string>35F9.1</string> <!-- QUIView event handling -->
+ </array>
+ </dict>
+ </array>
+</dict>
+</plist>
diff --git a/src/gui/platform/macos/qcocoanativeinterface.mm b/src/gui/platform/macos/qcocoanativeinterface.mm
index a41f9b16da..cb6acb4496 100644
--- a/src/gui/platform/macos/qcocoanativeinterface.mm
+++ b/src/gui/platform/macos/qcocoanativeinterface.mm
@@ -1,7 +1,11 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include <QtGui/private/qopenglcontext_p.h>
+#include <QtGui/qtgui-config.h>
+#ifndef QT_NO_OPENGL
+# include <QtGui/private/qopenglcontext_p.h>
+#endif
+
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformopenglcontext.h>
#include <qpa/qplatformintegration.h>
diff --git a/src/gui/platform/unix/dbusmenu/qdbusmenuconnection.cpp b/src/gui/platform/unix/dbusmenu/qdbusmenuconnection.cpp
index 9391b77f6a..1023b16662 100644
--- a/src/gui/platform/unix/dbusmenu/qdbusmenuconnection.cpp
+++ b/src/gui/platform/unix/dbusmenu/qdbusmenuconnection.cpp
@@ -40,14 +40,14 @@ QDBusMenuConnection::QDBusMenuConnection(QObject *parent, const QString &service
, m_connection(serviceName.isNull() ? QDBusConnection::sessionBus()
: QDBusConnection::connectToBus(QDBusConnection::SessionBus, serviceName))
, m_dbusWatcher(new QDBusServiceWatcher(StatusNotifierWatcherService, m_connection, QDBusServiceWatcher::WatchForRegistration, this))
- , m_statusNotifierHostRegistered(false)
+ , m_watcherRegistered(false)
{
#ifndef QT_NO_SYSTEMTRAYICON
- QDBusInterface systrayHost(StatusNotifierWatcherService, StatusNotifierWatcherPath, StatusNotifierWatcherService, m_connection);
- if (systrayHost.isValid() && systrayHost.property("IsStatusNotifierHostRegistered").toBool())
- m_statusNotifierHostRegistered = true;
+ // Start monitoring if any known tray-related services are registered.
+ if (m_connection.interface()->isServiceRegistered(StatusNotifierWatcherService))
+ m_watcherRegistered = true;
else
- qCDebug(qLcMenu) << "StatusNotifierHost is not registered";
+ qCDebug(qLcMenu) << "failed to find service" << StatusNotifierWatcherService;
#endif
}
diff --git a/src/gui/platform/unix/dbusmenu/qdbusmenuconnection_p.h b/src/gui/platform/unix/dbusmenu/qdbusmenuconnection_p.h
index 69713b12bd..37033e2fa3 100644
--- a/src/gui/platform/unix/dbusmenu/qdbusmenuconnection_p.h
+++ b/src/gui/platform/unix/dbusmenu/qdbusmenuconnection_p.h
@@ -39,7 +39,7 @@ public:
~QDBusMenuConnection();
QDBusConnection connection() const { return m_connection; }
QDBusServiceWatcher *dbusWatcher() const { return m_dbusWatcher; }
- bool isStatusNotifierHostRegistered() const { return m_statusNotifierHostRegistered; }
+ bool isWatcherRegistered() const { return m_watcherRegistered; }
#ifndef QT_NO_SYSTEMTRAYICON
bool registerTrayIconMenu(QDBusTrayIcon *item);
void unregisterTrayIconMenu(QDBusTrayIcon *item);
@@ -60,7 +60,7 @@ private:
QString m_serviceName;
QDBusConnection m_connection;
QDBusServiceWatcher *m_dbusWatcher;
- bool m_statusNotifierHostRegistered;
+ bool m_watcherRegistered;
};
QT_END_NAMESPACE
diff --git a/src/gui/platform/unix/dbusmenu/qdbusmenuregistrarproxy_p.h b/src/gui/platform/unix/dbusmenu/qdbusmenuregistrarproxy_p.h
index 4568fd2aa4..8041f3af0a 100644
--- a/src/gui/platform/unix/dbusmenu/qdbusmenuregistrarproxy_p.h
+++ b/src/gui/platform/unix/dbusmenu/qdbusmenuregistrarproxy_p.h
@@ -63,7 +63,7 @@ public Q_SLOTS: // METHODS
{
QDBusMessage reply = call(QDBus::Block, QStringLiteral("GetMenuForWindow"), windowId);
QList<QVariant> arguments = reply.arguments();
- if (reply.type() == QDBusMessage::ReplyMessage && arguments.count() == 2)
+ if (reply.type() == QDBusMessage::ReplyMessage && arguments.size() == 2)
menuObjectPath = qdbus_cast<QDBusObjectPath>(arguments.at(1));
return reply;
}
diff --git a/src/gui/platform/unix/dbusmenu/qdbusmenutypes.cpp b/src/gui/platform/unix/dbusmenu/qdbusmenutypes.cpp
index f70ce67d4e..b7fd035883 100644
--- a/src/gui/platform/unix/dbusmenu/qdbusmenutypes.cpp
+++ b/src/gui/platform/unix/dbusmenu/qdbusmenutypes.cpp
@@ -204,7 +204,7 @@ QString QDBusMenuItem::convertMnemonic(const QString &label)
// convert only the first occurrence of ampersand which is not at the end
// dbusmenu uses underscore instead of ampersand
int idx = label.indexOf(u'&');
- if (idx < 0 || idx == label.length() - 1)
+ if (idx < 0 || idx == label.size() - 1)
return label;
QString ret(label);
ret[idx] = u'_';
@@ -217,19 +217,19 @@ QDBusMenuShortcut QDBusMenuItem::convertKeySequence(const QKeySequence &sequence
QDBusMenuShortcut shortcut;
for (int i = 0; i < sequence.count(); ++i) {
QStringList tokens;
- int key = sequence[i].toCombined();
- if (key & Qt::MetaModifier)
+ auto modifiers = sequence[i].keyboardModifiers();
+ if (modifiers & Qt::MetaModifier)
tokens << QStringLiteral("Super");
- if (key & Qt::ControlModifier)
+ if (modifiers & Qt::ControlModifier)
tokens << QStringLiteral("Control");
- if (key & Qt::AltModifier)
+ if (modifiers & Qt::AltModifier)
tokens << QStringLiteral("Alt");
- if (key & Qt::ShiftModifier)
+ if (modifiers & Qt::ShiftModifier)
tokens << QStringLiteral("Shift");
- if (key & Qt::KeypadModifier)
+ if (modifiers & Qt::KeypadModifier)
tokens << QStringLiteral("Num");
- QString keyName = QKeySequencePrivate::keyName(key, QKeySequence::PortableText);
+ QString keyName = QKeySequencePrivate::keyName(sequence[i].key(), QKeySequence::PortableText);
if (keyName == "+"_L1)
tokens << QStringLiteral("plus");
else if (keyName == "-"_L1)
@@ -271,7 +271,7 @@ QDebug operator<<(QDebug d, const QDBusMenuLayoutItem &item)
{
QDebugStateSaver saver(d);
d.nospace();
- d << "QDBusMenuLayoutItem(id=" << item.m_id << ", properties=" << item.m_properties << ", " << item.m_children.count() << " children)";
+ d << "QDBusMenuLayoutItem(id=" << item.m_id << ", properties=" << item.m_properties << ", " << item.m_children.size() << " children)";
return d;
}
#endif
diff --git a/src/gui/platform/unix/dbustray/qdbustrayicon.cpp b/src/gui/platform/unix/dbustray/qdbustrayicon.cpp
index 18334e5715..0dff9b598e 100644
--- a/src/gui/platform/unix/dbustray/qdbustrayicon.cpp
+++ b/src/gui/platform/unix/dbustray/qdbustrayicon.cpp
@@ -198,7 +198,10 @@ QTemporaryFile *QDBusTrayIcon::tempIcon(const QIcon &icon)
if (!necessary)
return nullptr;
QTemporaryFile *ret = new QTemporaryFile(tempFileTemplate(), this);
- ret->open();
+ if (!ret->open()) {
+ delete ret;
+ return nullptr;
+ }
icon.pixmap(QSize(22, 22)).save(ret);
ret->close();
return ret;
@@ -331,8 +334,11 @@ void QDBusTrayIcon::notificationClosed(uint id, uint reason)
bool QDBusTrayIcon::isSystemTrayAvailable() const
{
QDBusMenuConnection * conn = const_cast<QDBusTrayIcon *>(this)->dBusConnection();
- qCDebug(qLcTray) << conn->isStatusNotifierHostRegistered();
- return conn->isStatusNotifierHostRegistered();
+
+ // If the KDE watcher service is registered, we must be on a desktop
+ // where a StatusNotifier-conforming system tray exists.
+ qCDebug(qLcTray) << conn->isWatcherRegistered();
+ return conn->isWatcherRegistered();
}
QT_END_NAMESPACE
diff --git a/src/gui/platform/unix/dbustray/qdbustraytypes.cpp b/src/gui/platform/unix/dbustray/qdbustraytypes.cpp
index b2d87d7b8c..accbd87e7e 100644
--- a/src/gui/platform/unix/dbustray/qdbustraytypes.cpp
+++ b/src/gui/platform/unix/dbustray/qdbustraytypes.cpp
@@ -45,7 +45,7 @@ QXdgDBusImageVector iconToQXdgDBusImageVector(const QIcon &icon)
bool hasSmallIcon = false;
bool hasMediumIcon = false;
QList<QSize> toRemove;
- for (const QSize &size : qAsConst(sizes)) {
+ for (const QSize &size : std::as_const(sizes)) {
int maxSize = qMax(size.width(), size.height());
if (maxSize <= IconNormalSmallSize)
hasSmallIcon = true;
@@ -54,7 +54,7 @@ QXdgDBusImageVector iconToQXdgDBusImageVector(const QIcon &icon)
else if (maxSize > IconSizeLimit)
toRemove << size;
}
- for (const QSize &size : qAsConst(toRemove))
+ for (const QSize &size : std::as_const(toRemove))
sizes.removeOne(size);
if (!hasSmallIcon)
sizes.append(QSize(IconNormalSmallSize, IconNormalSmallSize));
@@ -62,7 +62,7 @@ QXdgDBusImageVector iconToQXdgDBusImageVector(const QIcon &icon)
sizes.append(QSize(IconNormalMediumSize, IconNormalMediumSize));
ret.reserve(sizes.size());
- for (const QSize &size : qAsConst(sizes)) {
+ for (const QSize &size : std::as_const(sizes)) {
// Protocol specifies ARGB32 format in network byte order
QImage im = engine->pixmap(size, QIcon::Normal, QIcon::Off).toImage().convertToFormat(QImage::Format_ARGB32);
// letterbox if necessary to make it square
diff --git a/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor.cpp b/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor.cpp
index 27a14e408a..2f6c13b6cf 100644
--- a/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor.cpp
+++ b/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor.cpp
@@ -136,6 +136,12 @@ void QStatusNotifierItemAdaptor::ContextMenu(int x, int y)
emit m_trayIcon->activated(QPlatformSystemTrayIcon::Context);
}
+void QStatusNotifierItemAdaptor::ProvideXdgActivationToken(const QString &token)
+{
+ qCDebug(qLcTray) << token;
+ qputenv("XDG_ACTIVATION_TOKEN", token.toUtf8());
+}
+
void QStatusNotifierItemAdaptor::Scroll(int w, const QString &s)
{
qCDebug(qLcTray) << w << s;
diff --git a/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor_p.h b/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor_p.h
index 286aafa9bb..103fc974dd 100644
--- a/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor_p.h
+++ b/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor_p.h
@@ -73,6 +73,9 @@ class QStatusNotifierItemAdaptor: public QDBusAbstractAdaptor
" <property access=\"read\" type=\"(sa(iiay)ss)\" name=\"ToolTip\">\n"
" <annotation value=\"QXdgDBusToolTipStruct\" name=\"org.qtproject.QtDBus.QtTypeName\"/>\n"
" </property>\n"
+" <method name=\"ProvideXdgActivationToken\">\n"
+" <arg name=\"token\" type=\"s\" direction=\"in\"/>\n"
+" </method>\n"
" <method name=\"ContextMenu\">\n"
" <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
" <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
@@ -150,6 +153,7 @@ public: // PROPERTIES
public Q_SLOTS: // METHODS
void Activate(int x, int y);
void ContextMenu(int x, int y);
+ void ProvideXdgActivationToken(const QString &token);
void Scroll(int delta, const QString &orientation);
void SecondaryActivate(int x, int y);
Q_SIGNALS: // SIGNALS
diff --git a/src/gui/platform/unix/dbustray/qxdgnotificationproxy_p.h b/src/gui/platform/unix/dbustray/qxdgnotificationproxy_p.h
index fe61723bd2..dfbc64f33b 100644
--- a/src/gui/platform/unix/dbustray/qxdgnotificationproxy_p.h
+++ b/src/gui/platform/unix/dbustray/qxdgnotificationproxy_p.h
@@ -77,7 +77,7 @@ public Q_SLOTS: // METHODS
inline QDBusReply<QString> getServerInformation(QString &vendor, QString &version, QString &specVersion)
{
QDBusMessage reply = call(QDBus::Block, QStringLiteral("GetServerInformation"));
- if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().count() == 4) {
+ if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().size() == 4) {
vendor = qdbus_cast<QString>(reply.arguments().at(1));
version = qdbus_cast<QString>(reply.arguments().at(2));
specVersion = qdbus_cast<QString>(reply.arguments().at(3));
diff --git a/src/gui/platform/unix/qgenericunixservices.cpp b/src/gui/platform/unix/qgenericunixservices.cpp
index a0e5466c58..bfd2556b1e 100644
--- a/src/gui/platform/unix/qgenericunixservices.cpp
+++ b/src/gui/platform/unix/qgenericunixservices.cpp
@@ -3,6 +3,11 @@
#include "qgenericunixservices_p.h"
#include <QtGui/private/qtguiglobal_p.h>
+#include "qguiapplication.h"
+#include "qwindow.h"
+#include <QtGui/qpa/qplatformwindow_p.h>
+#include <QtGui/qpa/qplatformwindow.h>
+#include <QtGui/qpa/qplatformnativeinterface.h>
#include <QtCore/QDebug>
#include <QtCore/QFile>
@@ -126,8 +131,13 @@ static inline bool detectWebBrowser(const QByteArray &desktop,
return false;
}
-static inline bool launch(const QString &launcher, const QUrl &url)
+static inline bool launch(const QString &launcher, const QUrl &url,
+ const QString &xdgActivationToken)
{
+ if (!xdgActivationToken.isEmpty()) {
+ qputenv("XDG_ACTIVATION_TOKEN", xdgActivationToken.toUtf8());
+ }
+
const QString command = launcher + u' ' + QLatin1StringView(url.toEncoded());
if (debug)
qDebug("Launching %s", qPrintable(command));
@@ -143,16 +153,20 @@ static inline bool launch(const QString &launcher, const QUrl &url)
#endif
if (!ok)
qWarning("Launch failed (%s)", qPrintable(command));
+
+ qunsetenv("XDG_ACTIVATION_TOKEN");
+
return ok;
}
#if QT_CONFIG(dbus)
static inline bool checkNeedPortalSupport()
{
- return !QStandardPaths::locate(QStandardPaths::RuntimeLocation, "flatpak-info"_L1).isEmpty() || qEnvironmentVariableIsSet("SNAP");
+ return QFileInfo::exists("/.flatpak-info"_L1) || qEnvironmentVariableIsSet("SNAP");
}
-static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url)
+static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url, const QString &parentWindow,
+ const QString &xdgActivationToken)
{
// DBus signature:
// OpenFile (IN s parent_window,
@@ -163,8 +177,7 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url)
// handle_token (s) - A string that will be used as the last element of the @handle.
// writable (b) - Whether to allow the chosen application to write to the file.
-#ifdef O_PATH
- const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_PATH);
+ const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_RDONLY);
if (fd != -1) {
QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.portal.Desktop"_L1,
"/org/freedesktop/portal/desktop"_L1,
@@ -174,21 +187,22 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url)
QDBusUnixFileDescriptor descriptor;
descriptor.giveFileDescriptor(fd);
- const QVariantMap options = {{"writable"_L1, true}};
+ QVariantMap options = {};
+
+ if (!xdgActivationToken.isEmpty()) {
+ options.insert("activation_token"_L1, xdgActivationToken);
+ }
- // FIXME parent_window_id
- message << QString() << QVariant::fromValue(descriptor) << options;
+ message << parentWindow << QVariant::fromValue(descriptor) << options;
return QDBusConnection::sessionBus().call(message);
}
-#else
- Q_UNUSED(url);
-#endif
return QDBusMessage::createError(QDBusError::InternalError, qt_error_string());
}
-static inline QDBusMessage xdgDesktopPortalOpenUrl(const QUrl &url)
+static inline QDBusMessage xdgDesktopPortalOpenUrl(const QUrl &url, const QString &parentWindow,
+ const QString &xdgActivationToken)
{
// DBus signature:
// OpenURI (IN s parent_window,
@@ -206,12 +220,19 @@ static inline QDBusMessage xdgDesktopPortalOpenUrl(const QUrl &url)
"org.freedesktop.portal.OpenURI"_L1,
"OpenURI"_L1);
// FIXME parent_window_id and handle writable option
- message << QString() << url.toString() << QVariantMap();
+ QVariantMap options;
+
+ if (!xdgActivationToken.isEmpty()) {
+ options.insert("activation_token"_L1, xdgActivationToken);
+ }
+
+ message << parentWindow << url.toString() << options;
return QDBusConnection::sessionBus().call(message);
}
-static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url)
+static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url, const QString &parentWindow,
+ const QString &xdgActivationToken)
{
// DBus signature:
// ComposeEmail (IN s parent_window,
@@ -246,72 +267,274 @@ static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url)
options.insert("attachment_fds"_L1, QVariant::fromValue(attachments));
#endif
+ if (!xdgActivationToken.isEmpty()) {
+ options.insert("activation_token"_L1, xdgActivationToken);
+ }
+
QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.portal.Desktop"_L1,
"/org/freedesktop/portal/desktop"_L1,
"org.freedesktop.portal.Email"_L1,
"ComposeEmail"_L1);
- // FIXME parent_window_id
- message << QString() << options;
+ message << parentWindow << options;
return QDBusConnection::sessionBus().call(message);
}
+
+namespace {
+struct XDGDesktopColor
+{
+ double r = 0;
+ double g = 0;
+ double b = 0;
+
+ QColor toQColor() const
+ {
+ constexpr auto rgbMax = 255;
+ return { static_cast<int>(r * rgbMax), static_cast<int>(g * rgbMax),
+ static_cast<int>(b * rgbMax) };
+ }
+};
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, XDGDesktopColor &myStruct)
+{
+ argument.beginStructure();
+ argument >> myStruct.r >> myStruct.g >> myStruct.b;
+ argument.endStructure();
+ return argument;
+}
+
+class XdgDesktopPortalColorPicker : public QPlatformServiceColorPicker
+{
+ Q_OBJECT
+public:
+ XdgDesktopPortalColorPicker(const QString &parentWindowId, QWindow *parent)
+ : QPlatformServiceColorPicker(parent), m_parentWindowId(parentWindowId)
+ {
+ }
+
+ void pickColor() override
+ {
+ // DBus signature:
+ // PickColor (IN s parent_window,
+ // IN a{sv} options
+ // OUT o handle)
+ // Options:
+ // handle_token (s) - A string that will be used as the last element of the @handle.
+
+ QDBusMessage message = QDBusMessage::createMethodCall(
+ "org.freedesktop.portal.Desktop"_L1, "/org/freedesktop/portal/desktop"_L1,
+ "org.freedesktop.portal.Screenshot"_L1, "PickColor"_L1);
+ message << m_parentWindowId << QVariantMap();
+
+ QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message);
+ auto watcher = new QDBusPendingCallWatcher(pendingCall, this);
+ connect(watcher, &QDBusPendingCallWatcher::finished, this,
+ [this](QDBusPendingCallWatcher *watcher) {
+ watcher->deleteLater();
+ QDBusPendingReply<QDBusObjectPath> reply = *watcher;
+ if (reply.isError()) {
+ qWarning("DBus call to pick color failed: %s",
+ qPrintable(reply.error().message()));
+ Q_EMIT colorPicked({});
+ } else {
+ QDBusConnection::sessionBus().connect(
+ "org.freedesktop.portal.Desktop"_L1, reply.value().path(),
+ "org.freedesktop.portal.Request"_L1, "Response"_L1, this,
+ // clang-format off
+ SLOT(gotColorResponse(uint,QVariantMap))
+ // clang-format on
+ );
+ }
+ });
+ }
+
+private Q_SLOTS:
+ void gotColorResponse(uint result, const QVariantMap &map)
+ {
+ if (result != 0)
+ return;
+ if (map.contains(u"color"_s)) {
+ XDGDesktopColor color{};
+ map.value(u"color"_s).value<QDBusArgument>() >> color;
+ Q_EMIT colorPicked(color.toQColor());
+ } else {
+ Q_EMIT colorPicked({});
+ }
+ deleteLater();
+ }
+
+private:
+ const QString m_parentWindowId;
+};
+} // namespace
+
#endif // QT_CONFIG(dbus)
+QGenericUnixServices::QGenericUnixServices()
+{
+#if QT_CONFIG(dbus)
+ if (qEnvironmentVariableIntValue("QT_NO_XDG_DESKTOP_PORTAL") > 0) {
+ return;
+ }
+ QDBusMessage message = QDBusMessage::createMethodCall(
+ "org.freedesktop.portal.Desktop"_L1, "/org/freedesktop/portal/desktop"_L1,
+ "org.freedesktop.DBus.Properties"_L1, "Get"_L1);
+ message << "org.freedesktop.portal.Screenshot"_L1
+ << "version"_L1;
+
+ QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message);
+ auto watcher = new QDBusPendingCallWatcher(pendingCall);
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher,
+ [this](QDBusPendingCallWatcher *watcher) {
+ watcher->deleteLater();
+ QDBusPendingReply<QVariant> reply = *watcher;
+ if (!reply.isError() && reply.value().toUInt() >= 2)
+ m_hasScreenshotPortalWithColorPicking = true;
+ });
+
+#endif
+}
+
+QPlatformServiceColorPicker *QGenericUnixServices::colorPicker(QWindow *parent)
+{
+#if QT_CONFIG(dbus)
+ // Make double sure that we are in a wayland environment. In particular check
+ // WAYLAND_DISPLAY so also XWayland apps benefit from portal-based color picking.
+ // Outside wayland we'll rather rely on other means than the XDG desktop portal.
+ if (!qEnvironmentVariableIsEmpty("WAYLAND_DISPLAY")
+ || QGuiApplication::platformName().startsWith("wayland"_L1)) {
+ return new XdgDesktopPortalColorPicker(portalWindowIdentifier(parent), parent);
+ }
+ return nullptr;
+#else
+ Q_UNUSED(parent);
+ return nullptr;
+#endif
+}
+
QByteArray QGenericUnixServices::desktopEnvironment() const
{
static const QByteArray result = detectDesktopEnvironment();
return result;
}
+template<typename F>
+void runWithXdgActivationToken(F &&functionToCall)
+{
+#if QT_CONFIG(wayland)
+ QWindow *window = qGuiApp->focusWindow();
+
+ if (!window) {
+ functionToCall({});
+ return;
+ }
+
+ auto waylandApp = dynamic_cast<QNativeInterface::QWaylandApplication *>(
+ qGuiApp->platformNativeInterface());
+ auto waylandWindow =
+ dynamic_cast<QNativeInterface::Private::QWaylandWindow *>(window->handle());
+
+ if (!waylandWindow || !waylandApp) {
+ functionToCall({});
+ return;
+ }
+
+ QObject::connect(waylandWindow,
+ &QNativeInterface::Private::QWaylandWindow::xdgActivationTokenCreated,
+ waylandWindow, functionToCall, Qt::SingleShotConnection);
+ waylandWindow->requestXdgActivationToken(waylandApp->lastInputSerial());
+#else
+ functionToCall({});
+#endif
+}
+
bool QGenericUnixServices::openUrl(const QUrl &url)
{
- if (url.scheme() == "mailto"_L1) {
-#if QT_CONFIG(dbus)
+ auto openUrlInternal = [this](const QUrl &url, const QString &xdgActivationToken) {
+ if (url.scheme() == "mailto"_L1) {
+# if QT_CONFIG(dbus)
+ if (checkNeedPortalSupport()) {
+ const QString parentWindow = QGuiApplication::focusWindow()
+ ? portalWindowIdentifier(QGuiApplication::focusWindow())
+ : QString();
+ QDBusError error = xdgDesktopPortalSendEmail(url, parentWindow, xdgActivationToken);
+ if (!error.isValid())
+ return true;
+
+ // service not running, fall back
+ }
+# endif
+ return openDocument(url);
+ }
+
+# if QT_CONFIG(dbus)
if (checkNeedPortalSupport()) {
- QDBusError error = xdgDesktopPortalSendEmail(url);
+ const QString parentWindow = QGuiApplication::focusWindow()
+ ? portalWindowIdentifier(QGuiApplication::focusWindow())
+ : QString();
+ QDBusError error = xdgDesktopPortalOpenUrl(url, parentWindow, xdgActivationToken);
if (!error.isValid())
return true;
+ }
+# endif
- // service not running, fall back
+ if (m_webBrowser.isEmpty()
+ && !detectWebBrowser(desktopEnvironment(), true, &m_webBrowser)) {
+ qWarning("Unable to detect a web browser to launch '%s'", qPrintable(url.toString()));
+ return false;
}
-#endif
- return openDocument(url);
- }
+ return launch(m_webBrowser, url, xdgActivationToken);
+ };
-#if QT_CONFIG(dbus)
- if (checkNeedPortalSupport()) {
- QDBusError error = xdgDesktopPortalOpenUrl(url);
- if (!error.isValid())
- return true;
- }
-#endif
+ if (QGuiApplication::platformName().startsWith("wayland"_L1)) {
+ runWithXdgActivationToken(
+ [openUrlInternal, url](const QString &token) { openUrlInternal(url, token); });
+
+ return true;
- if (m_webBrowser.isEmpty() && !detectWebBrowser(desktopEnvironment(), true, &m_webBrowser)) {
- qWarning("Unable to detect a web browser to launch '%s'", qPrintable(url.toString()));
- return false;
+ } else {
+ return openUrlInternal(url, QString());
}
- return launch(m_webBrowser, url);
}
bool QGenericUnixServices::openDocument(const QUrl &url)
{
-#if QT_CONFIG(dbus)
- if (checkNeedPortalSupport()) {
- QDBusError error = xdgDesktopPortalOpenFile(url);
- if (!error.isValid())
- return true;
- }
-#endif
+ auto openDocumentInternal = [this](const QUrl &url, const QString &xdgActivationToken) {
- if (m_documentLauncher.isEmpty() && !detectWebBrowser(desktopEnvironment(), false, &m_documentLauncher)) {
- qWarning("Unable to detect a launcher for '%s'", qPrintable(url.toString()));
- return false;
+# if QT_CONFIG(dbus)
+ if (checkNeedPortalSupport()) {
+ const QString parentWindow = QGuiApplication::focusWindow()
+ ? portalWindowIdentifier(QGuiApplication::focusWindow())
+ : QString();
+ QDBusError error = xdgDesktopPortalOpenFile(url, parentWindow, xdgActivationToken);
+ if (!error.isValid())
+ return true;
+ }
+# endif
+
+ if (m_documentLauncher.isEmpty()
+ && !detectWebBrowser(desktopEnvironment(), false, &m_documentLauncher)) {
+ qWarning("Unable to detect a launcher for '%s'", qPrintable(url.toString()));
+ return false;
+ }
+ return launch(m_documentLauncher, url, xdgActivationToken);
+ };
+
+ if (QGuiApplication::platformName().startsWith("wayland"_L1)) {
+ runWithXdgActivationToken([openDocumentInternal, url](const QString &token) {
+ openDocumentInternal(url, token);
+ });
+
+ return true;
+ } else {
+ return openDocumentInternal(url, QString());
}
- return launch(m_documentLauncher, url);
}
#else
+QGenericUnixServices::QGenericUnixServices() = default;
+
QByteArray QGenericUnixServices::desktopEnvironment() const
{
return QByteArrayLiteral("UNKNOWN");
@@ -331,6 +554,60 @@ bool QGenericUnixServices::openDocument(const QUrl &url)
return false;
}
+QPlatformServiceColorPicker *QGenericUnixServices::colorPicker(QWindow *parent)
+{
+ Q_UNUSED(parent);
+ return nullptr;
+}
+
#endif // QT_NO_MULTIPROCESS
+QString QGenericUnixServices::portalWindowIdentifier(QWindow *window)
+{
+ Q_UNUSED(window);
+ return QString();
+}
+
+bool QGenericUnixServices::hasCapability(Capability capability) const
+{
+ switch (capability) {
+ case Capability::ColorPicking:
+ return m_hasScreenshotPortalWithColorPicking;
+ }
+ return false;
+}
+
+void QGenericUnixServices::setApplicationBadge(qint64 number)
+{
+#if QT_CONFIG(dbus)
+ if (qGuiApp->desktopFileName().isEmpty()) {
+ qWarning("QGuiApplication::desktopFileName() is empty");
+ return;
+ }
+
+
+ const QString launcherUrl = QStringLiteral("application://") + qGuiApp->desktopFileName() + QStringLiteral(".desktop");
+ const qint64 count = qBound(0, number, 9999);
+ QVariantMap dbusUnityProperties;
+
+ if (count > 0) {
+ dbusUnityProperties[QStringLiteral("count")] = count;
+ dbusUnityProperties[QStringLiteral("count-visible")] = true;
+ } else {
+ dbusUnityProperties[QStringLiteral("count-visible")] = false;
+ }
+
+ auto signal = QDBusMessage::createSignal(QStringLiteral("/com/canonical/unity/launcherentry/")
+ + qGuiApp->applicationName(), QStringLiteral("com.canonical.Unity.LauncherEntry"), QStringLiteral("Update"));
+
+ signal.setArguments({launcherUrl, dbusUnityProperties});
+
+ QDBusConnection::sessionBus().send(signal);
+#else
+ Q_UNUSED(number)
+#endif
+}
+
QT_END_NAMESPACE
+
+#include "qgenericunixservices.moc"
diff --git a/src/gui/platform/unix/qgenericunixservices_p.h b/src/gui/platform/unix/qgenericunixservices_p.h
index 8d1228375f..56e15103f7 100644
--- a/src/gui/platform/unix/qgenericunixservices_p.h
+++ b/src/gui/platform/unix/qgenericunixservices_p.h
@@ -21,19 +21,27 @@
QT_BEGIN_NAMESPACE
+class QWindow;
+
class Q_GUI_EXPORT QGenericUnixServices : public QPlatformServices
{
public:
- QGenericUnixServices() {}
+ QGenericUnixServices();
QByteArray desktopEnvironment() const override;
+ bool hasCapability(Capability capability) const override;
bool openUrl(const QUrl &url) override;
bool openDocument(const QUrl &url) override;
+ QPlatformServiceColorPicker *colorPicker(QWindow *parent = nullptr) override;
+
+ void setApplicationBadge(qint64 number);
+ virtual QString portalWindowIdentifier(QWindow *window);
private:
QString m_webBrowser;
QString m_documentLauncher;
+ bool m_hasScreenshotPortalWithColorPicking = false;
};
QT_END_NAMESPACE
diff --git a/src/gui/platform/unix/qgenericunixthemes.cpp b/src/gui/platform/unix/qgenericunixthemes.cpp
index d90d5de098..fc4b2296d2 100644
--- a/src/gui/platform/unix/qgenericunixthemes.cpp
+++ b/src/gui/platform/unix/qgenericunixthemes.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qgenericunixthemes_p.h"
@@ -33,6 +33,12 @@
#include <QDBusConnectionInterface>
#include <private/qdbusplatformmenu_p.h>
#include <private/qdbusmenubar_p.h>
+#include <private/qflatmap_p.h>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QJsonObject>
+#include <QJsonValue>
+#include <QJsonParseError>
#endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
#include <private/qdbustrayicon_p.h>
@@ -41,6 +47,9 @@
#include <algorithm>
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_DBUS
+Q_LOGGING_CATEGORY(lcQpaThemeDBus, "qt.qpa.theme.dbus")
+#endif
using namespace Qt::StringLiterals;
@@ -74,7 +83,7 @@ static bool isDBusTrayAvailable() {
static bool dbusTrayAvailableKnown = false;
if (!dbusTrayAvailableKnown) {
QDBusMenuConnection conn;
- if (conn.isStatusNotifierHostRegistered())
+ if (conn.isWatcherRegistered())
dbusTrayAvailable = true;
dbusTrayAvailableKnown = true;
qCDebug(qLcTray) << "D-Bus tray available:" << dbusTrayAvailable;
@@ -83,6 +92,20 @@ static bool isDBusTrayAvailable() {
}
#endif
+static QString mouseCursorTheme()
+{
+ static QString themeName = qEnvironmentVariable("XCURSOR_THEME");
+ return themeName;
+}
+
+static QSize mouseCursorSize()
+{
+ constexpr int defaultCursorSize = 24;
+ static const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE");
+ static const int s = xCursorSize > 0 ? xCursorSize : defaultCursorSize;
+ return QSize(s, s);
+}
+
#ifndef QT_NO_DBUS
static bool checkDBusGlobalMenuAvailable()
{
@@ -98,7 +121,288 @@ static bool isDBusGlobalMenuAvailable()
static bool dbusGlobalMenuAvailable = checkDBusGlobalMenuAvailable();
return dbusGlobalMenuAvailable;
}
+
+/*!
+ * \internal
+ * The QGenericUnixThemeDBusListener class listens to the SettingChanged DBus signal
+ * and translates it into combinations of the enums \c Provider and \c Setting.
+ * Upon construction, it logs success/failure of the DBus connection.
+ *
+ * The signal settingChanged delivers the normalized setting type and the new value as a string.
+ * It is emitted on known setting types only.
+ */
+
+class QGenericUnixThemeDBusListener : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ enum class Provider {
+ Kde,
+ Gtk,
+ Gnome,
+ };
+ Q_ENUM(Provider)
+
+ enum class Setting {
+ Theme,
+ ApplicationStyle,
+ ColorScheme,
+ };
+ Q_ENUM(Setting)
+
+ QGenericUnixThemeDBusListener();
+ QGenericUnixThemeDBusListener(const QString &service, const QString &path,
+ const QString &interface, const QString &signal);
+
+private Q_SLOTS:
+ void onSettingChanged(const QString &location, const QString &key, const QDBusVariant &value);
+
+Q_SIGNALS:
+ void settingChanged(QGenericUnixThemeDBusListener::Provider provider,
+ QGenericUnixThemeDBusListener::Setting setting,
+ const QString &value);
+
+private:
+ struct DBusKey
+ {
+ QString location;
+ QString key;
+ DBusKey(const QString &loc, const QString &k) : location(loc), key(k) {};
+ bool operator<(const DBusKey &other) const
+ {
+ return location + key < other.location + other.key;
+ }
+ };
+
+ struct ChangeSignal
+ {
+ Provider provider;
+ Setting setting;
+ ChangeSignal(Provider p, Setting s) : provider(p), setting(s) {}
+ ChangeSignal() {}
+ };
+
+ // Json keys
+ static constexpr QLatin1StringView s_dbusLocation = QLatin1StringView("DBusLocation");
+ static constexpr QLatin1StringView s_dbusKey = QLatin1StringView("DBusKey");
+ static constexpr QLatin1StringView s_provider = QLatin1StringView("Provider");
+ static constexpr QLatin1StringView s_setting = QLatin1StringView("Setting");
+ static constexpr QLatin1StringView s_signals = QLatin1StringView("DbusSignals");
+ static constexpr QLatin1StringView s_root = QLatin1StringView("Qt.qpa.DBusSignals");
+
+ QFlatMap <DBusKey, ChangeSignal> m_signalMap;
+
+ void init(const QString &service, const QString &path,
+ const QString &interface, const QString &signal);
+
+ std::optional<ChangeSignal> findSignal(const QString &location, const QString &key) const;
+ void populateSignalMap();
+ void loadJson(const QString &fileName);
+ void saveJson(const QString &fileName) const;
+};
+
+QGenericUnixThemeDBusListener::QGenericUnixThemeDBusListener(const QString &service,
+ const QString &path, const QString &interface, const QString &signal)
+{
+ init (service, path, interface, signal);
+}
+
+QGenericUnixThemeDBusListener::QGenericUnixThemeDBusListener()
+{
+ static constexpr QLatin1StringView service("");
+ static constexpr QLatin1StringView path("/org/freedesktop/portal/desktop");
+ static constexpr QLatin1StringView interface("org.freedesktop.portal.Settings");
+ static constexpr QLatin1StringView signal("SettingChanged");
+
+ init (service, path, interface, signal);
+}
+
+void QGenericUnixThemeDBusListener::init(const QString &service, const QString &path,
+ const QString &interface, const QString &signal)
+{
+ QDBusConnection dbus = QDBusConnection::sessionBus();
+ const bool dBusRunning = dbus.isConnected();
+ bool dBusSignalConnected = false;
+#define LOG service << path << interface << signal;
+
+ if (dBusRunning) {
+ populateSignalMap();
+ qRegisterMetaType<QDBusVariant>();
+ dBusSignalConnected = dbus.connect(service, path, interface, signal, this,
+ SLOT(onSettingChanged(QString,QString,QDBusVariant)));
+ }
+
+ if (dBusSignalConnected) {
+ // Connection successful
+ qCDebug(lcQpaThemeDBus) << LOG;
+ } else {
+ if (dBusRunning) {
+ // DBus running, but connection failed
+ qCWarning(lcQpaThemeDBus) << "DBus connection failed:" << LOG;
+ } else {
+ // DBus not running
+ qCWarning(lcQpaThemeDBus) << "Session DBus not running.";
+ }
+ qCWarning(lcQpaThemeDBus) << "Application will not react to setting changes.\n"
+ << "Check your DBus installation.";
+ }
+#undef LOG
+}
+
+void QGenericUnixThemeDBusListener::loadJson(const QString &fileName)
+{
+ Q_ASSERT(!fileName.isEmpty());
+#define CHECK(cond, warning)\
+ if (!cond) {\
+ qCWarning(lcQpaThemeDBus) << fileName << warning << "Falling back to default.";\
+ return;\
+ }
+
+#define PARSE(var, enumeration, string)\
+ enumeration var;\
+ {\
+ bool success;\
+ const int val = QMetaEnum::fromType<enumeration>().keyToValue(string.toLatin1(), &success);\
+ CHECK(success, "Parse Error: Invalid value" << string << "for" << #var);\
+ var = static_cast<enumeration>(val);\
+ }
+
+ QFile file(fileName);
+ CHECK(file.exists(), fileName << "doesn't exist.");
+ CHECK(file.open(QIODevice::ReadOnly), "could not be opened for reading.");
+
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
+ CHECK((error.error == QJsonParseError::NoError), error.errorString());
+ CHECK(doc.isObject(), "Parse Error: Expected root object" << s_root);
+
+ const QJsonObject &root = doc.object();
+ CHECK(root.contains(s_root), "Parse Error: Expected root object" << s_root);
+ CHECK(root[s_root][s_signals].isArray(), "Parse Error: Expected array" << s_signals);
+
+ const QJsonArray &sigs = root[s_root][s_signals].toArray();
+ CHECK((sigs.count() > 0), "Parse Error: Found empty array" << s_signals);
+
+ for (auto sig = sigs.constBegin(); sig != sigs.constEnd(); ++sig) {
+ CHECK(sig->isObject(), "Parse Error: Expected object array" << s_signals);
+ const QJsonObject &obj = sig->toObject();
+ CHECK(obj.contains(s_dbusLocation), "Parse Error: Expected key" << s_dbusLocation);
+ CHECK(obj.contains(s_dbusKey), "Parse Error: Expected key" << s_dbusKey);
+ CHECK(obj.contains(s_provider), "Parse Error: Expected key" << s_provider);
+ CHECK(obj.contains(s_setting), "Parse Error: Expected key" << s_setting);
+ const QString &location = obj[s_dbusLocation].toString();
+ const QString &key = obj[s_dbusKey].toString();
+ const QString &providerString = obj[s_provider].toString();
+ const QString &settingString = obj[s_setting].toString();
+ PARSE(provider, Provider, providerString);
+ PARSE(setting, Setting, settingString);
+ const DBusKey dkey(location, key);
+ CHECK (!m_signalMap.contains(dkey), "Duplicate key" << location << key);
+ m_signalMap.insert(dkey, ChangeSignal(provider, setting));
+ }
+#undef PARSE
+#undef CHECK
+
+ if (m_signalMap.count() > 0)
+ qCInfo(lcQpaThemeDBus) << "Successfully imported" << fileName;
+ else
+ qCWarning(lcQpaThemeDBus) << "No data imported from" << fileName << "falling back to default.";
+
+#ifdef QT_DEBUG
+ const int count = m_signalMap.count();
+ if (count == 0)
+ return;
+
+ qCDebug(lcQpaThemeDBus) << "Listening to" << count << "signals:";
+ for (auto it = m_signalMap.constBegin(); it != m_signalMap.constEnd(); ++it) {
+ qDebug() << it.key().key << it.key().location << "mapped to"
+ << it.value().provider << it.value().setting;
+ }
+
#endif
+}
+
+void QGenericUnixThemeDBusListener::saveJson(const QString &fileName) const
+{
+ Q_ASSERT(!m_signalMap.isEmpty());
+ Q_ASSERT(!fileName.isEmpty());
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly)) {
+ qCWarning(lcQpaThemeDBus) << fileName << "could not be opened for writing.";
+ return;
+ }
+
+ QJsonArray sigs;
+ for (auto sig = m_signalMap.constBegin(); sig != m_signalMap.constEnd(); ++sig) {
+ const DBusKey &dkey = sig.key();
+ const ChangeSignal &csig = sig.value();
+ QJsonObject obj;
+ obj[s_dbusLocation] = dkey.location;
+ obj[s_dbusKey] = dkey.key;
+ obj[s_provider] = QLatin1StringView(QMetaEnum::fromType<Provider>()
+ .valueToKey(static_cast<int>(csig.provider)));
+ obj[s_setting] = QLatin1StringView(QMetaEnum::fromType<Setting>()
+ .valueToKey(static_cast<int>(csig.setting)));
+ sigs.append(obj);
+ }
+ QJsonObject obj;
+ obj[s_signals] = sigs;
+ QJsonObject root;
+ root[s_root] = obj;
+ QJsonDocument doc(root);
+ file.write(doc.toJson());
+ file.close();
+}
+
+void QGenericUnixThemeDBusListener::populateSignalMap()
+{
+ m_signalMap.clear();
+ const QString &loadJsonFile = qEnvironmentVariable("QT_QPA_DBUS_SIGNALS");
+ if (!loadJsonFile.isEmpty())
+ loadJson(loadJsonFile);
+ if (!m_signalMap.isEmpty())
+ return;
+
+ m_signalMap.insert(DBusKey("org.kde.kdeglobals.KDE"_L1, "widgetStyle"_L1),
+ ChangeSignal(Provider::Kde, Setting::ApplicationStyle));
+
+ m_signalMap.insert(DBusKey("org.kde.kdeglobals.General"_L1, "ColorScheme"_L1),
+ ChangeSignal(Provider::Kde, Setting::Theme));
+
+ m_signalMap.insert(DBusKey("org.gnome.desktop.interface"_L1, "gtk-theme"_L1),
+ ChangeSignal(Provider::Gtk, Setting::Theme));
+
+ m_signalMap.insert(DBusKey("org.freedesktop.appearance"_L1, "color-scheme"_L1),
+ ChangeSignal(Provider::Gnome, Setting::ColorScheme));
+
+ const QString &saveJsonFile = qEnvironmentVariable("QT_QPA_DBUS_SIGNALS_SAVE");
+ if (!saveJsonFile.isEmpty())
+ saveJson(saveJsonFile);
+}
+
+std::optional<QGenericUnixThemeDBusListener::ChangeSignal>
+ QGenericUnixThemeDBusListener::findSignal(const QString &location, const QString &key) const
+{
+ const DBusKey dkey(location, key);
+ std::optional<QGenericUnixThemeDBusListener::ChangeSignal> ret;
+ if (m_signalMap.contains(dkey))
+ ret.emplace(m_signalMap.value(dkey));
+
+ return ret;
+}
+
+void QGenericUnixThemeDBusListener::onSettingChanged(const QString &location, const QString &key, const QDBusVariant &value)
+{
+ auto sig = findSignal(location, key);
+ if (!sig.has_value())
+ return;
+
+ emit settingChanged(sig.value().provider, sig.value().setting, value.variant().toString());
+}
+
+#endif //QT_NO_DBUS
class QGenericUnixThemePrivate : public QPlatformThemePrivate
{
@@ -198,6 +502,10 @@ QVariant QGenericUnixTheme::themeHint(ThemeHint hint) const
return QVariant(int(X11KeyboardScheme));
case QPlatformTheme::UiEffects:
return QVariant(int(HoverEffect));
+ case QPlatformTheme::MouseCursorTheme:
+ return QVariant(mouseCursorTheme());
+ case QPlatformTheme::MouseCursorSize:
+ return QVariant(mouseCursorSize());
default:
break;
}
@@ -231,11 +539,9 @@ static QIcon xdgFileIcon(const QFileInfo &fileInfo)
#if QT_CONFIG(settings)
class QKdeThemePrivate : public QPlatformThemePrivate
{
+
public:
- QKdeThemePrivate(const QStringList &kdeDirs, int kdeVersion)
- : kdeDirs(kdeDirs)
- , kdeVersion(kdeVersion)
- { }
+ QKdeThemePrivate(const QStringList &kdeDirs, int kdeVersion);
static QString kdeGlobals(const QString &kdeDir, int kdeVersion)
{
@@ -266,8 +572,66 @@ public:
int startDragDist = 10;
int startDragTime = 500;
int cursorBlinkRate = 1000;
+ Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown;
+ void updateColorScheme(const QString &themeName);
+
+#ifndef QT_NO_DBUS
+private:
+ std::unique_ptr<QGenericUnixThemeDBusListener> dbus;
+ bool initDbus();
+ void settingChangedHandler(QGenericUnixThemeDBusListener::Provider provider,
+ QGenericUnixThemeDBusListener::Setting setting,
+ const QString &value);
+#endif // QT_NO_DBUS
};
+#ifndef QT_NO_DBUS
+void QKdeThemePrivate::settingChangedHandler(QGenericUnixThemeDBusListener::Provider provider,
+ QGenericUnixThemeDBusListener::Setting setting,
+ const QString &value)
+{
+ if (provider != QGenericUnixThemeDBusListener::Provider::Kde)
+ return;
+
+ switch (setting) {
+ case QGenericUnixThemeDBusListener::Setting::ColorScheme:
+ qCDebug(lcQpaThemeDBus) << "KDE color theme changed to:" << value;
+ break;
+ case QGenericUnixThemeDBusListener::Setting::Theme:
+ qCDebug(lcQpaThemeDBus) << "KDE global theme changed to:" << value;
+ break;
+ case QGenericUnixThemeDBusListener::Setting::ApplicationStyle:
+ qCDebug(lcQpaThemeDBus) << "KDE application style changed to:" << value;
+ break;
+ }
+
+ refresh();
+}
+
+bool QKdeThemePrivate::initDbus()
+{
+ dbus.reset(new QGenericUnixThemeDBusListener());
+ Q_ASSERT(dbus);
+
+ // Wrap slot in a lambda to avoid inheriting QKdeThemePrivate from QObject
+ auto wrapper = [this](QGenericUnixThemeDBusListener::Provider provider,
+ QGenericUnixThemeDBusListener::Setting setting,
+ const QString &value) {
+ settingChangedHandler(provider, setting, value);
+ };
+
+ return QObject::connect(dbus.get(), &QGenericUnixThemeDBusListener::settingChanged, dbus.get(), wrapper);
+}
+#endif // QT_NO_DBUS
+
+QKdeThemePrivate::QKdeThemePrivate(const QStringList &kdeDirs, int kdeVersion)
+ : kdeDirs(kdeDirs), kdeVersion(kdeVersion)
+{
+#ifndef QT_NO_DBUS
+ initDbus();
+#endif // QT_NO_DBUS
+}
+
void QKdeThemePrivate::refresh()
{
resources.clear();
@@ -297,6 +661,14 @@ void QKdeThemePrivate::refresh()
styleNames.push_front(style);
}
+ const QVariant colorScheme = readKdeSetting(QStringLiteral("ColorScheme"), kdeDirs,
+ kdeVersion, kdeSettings);
+
+ if (colorScheme.isValid())
+ updateColorScheme(colorScheme.toString());
+ else
+ m_colorScheme = Qt::ColorScheme::Unknown;
+
const QVariant singleClickValue = readKdeSetting(QStringLiteral("KDE/SingleClick"), kdeDirs, kdeVersion, kdeSettings);
if (singleClickValue.isValid())
singleClick = singleClickValue.toBool();
@@ -368,6 +740,8 @@ void QKdeThemePrivate::refresh()
if (QFont *toolBarFont = kdeFont(readKdeSetting(QStringLiteral("toolBarFont"), kdeDirs, kdeVersion, kdeSettings)))
resources.fonts[QPlatformTheme::ToolButtonFont] = toolBarFont;
+ QWindowSystemInterface::handleThemeChange();
+
qCDebug(lcQpaFonts) << "default fonts: system" << resources.fonts[QPlatformTheme::SystemFont]
<< "fixed" << resources.fonts[QPlatformTheme::FixedFont];
qDeleteAll(kdeSettings);
@@ -556,6 +930,10 @@ QVariant QKdeTheme::themeHint(QPlatformTheme::ThemeHint hint) const
return QVariant(d->cursorBlinkRate);
case QPlatformTheme::UiEffects:
return QVariant(int(HoverEffect));
+ case QPlatformTheme::MouseCursorTheme:
+ return QVariant(mouseCursorTheme());
+ case QPlatformTheme::MouseCursorSize:
+ return QVariant(mouseCursorSize());
default:
break;
}
@@ -572,6 +950,48 @@ QIcon QKdeTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions
#endif
}
+Qt::ColorScheme QKdeTheme::colorScheme() const
+{
+ return d_func()->m_colorScheme;
+}
+
+/*!
+ \internal
+ \brief QKdeTheme::updateColorScheme - guess and set appearance for unix themes.
+ KDE themes do not have an appearance property.
+ The key words "dark" or "light" should be part of the theme name.
+ This is, however, not a mandatory convention.
+
+ If \param themeName contains a key word, the respective appearance is set.
+ If it doesn't, the appearance is heuristically determined by comparing text and base color
+ of the system palette.
+ */
+void QKdeThemePrivate::updateColorScheme(const QString &themeName)
+{
+ if (themeName.contains(QLatin1StringView("light"), Qt::CaseInsensitive)) {
+ m_colorScheme = Qt::ColorScheme::Light;
+ return;
+ }
+ if (themeName.contains(QLatin1StringView("dark"), Qt::CaseInsensitive)) {
+ m_colorScheme = Qt::ColorScheme::Dark;
+ return;
+ }
+
+ if (systemPalette) {
+ if (systemPalette->text().color().lightness() < systemPalette->base().color().lightness()) {
+ m_colorScheme = Qt::ColorScheme::Light;
+ return;
+ }
+ if (systemPalette->text().color().lightness() > systemPalette->base().color().lightness()) {
+ m_colorScheme = Qt::ColorScheme::Dark;
+ return;
+ }
+ }
+
+ m_colorScheme = Qt::ColorScheme::Unknown;
+}
+
+
const QPalette *QKdeTheme::palette(Palette type) const
{
Q_D(const QKdeTheme);
@@ -672,8 +1092,8 @@ const char *QGnomeTheme::name = "gnome";
class QGnomeThemePrivate : public QPlatformThemePrivate
{
public:
- QGnomeThemePrivate() : systemFont(nullptr), fixedFont(nullptr) {}
- ~QGnomeThemePrivate() { delete systemFont; delete fixedFont; }
+ QGnomeThemePrivate();
+ ~QGnomeThemePrivate();
void configureFonts(const QString &gtkFontName) const
{
@@ -688,10 +1108,70 @@ public:
qCDebug(lcQpaFonts) << "default fonts: system" << systemFont << "fixed" << fixedFont;
}
- mutable QFont *systemFont;
- mutable QFont *fixedFont;
+ mutable QFont *systemFont = nullptr;
+ mutable QFont *fixedFont = nullptr;
+
+#ifndef QT_NO_DBUS
+ Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown;
+private:
+ std::unique_ptr<QGenericUnixThemeDBusListener> dbus;
+ bool initDbus();
+ void updateColorScheme(const QString &themeName);
+#endif // QT_NO_DBUS
};
+QGnomeThemePrivate::QGnomeThemePrivate()
+{
+#ifndef QT_NO_DBUS
+ initDbus();
+#endif // QT_NO_DBUS
+}
+QGnomeThemePrivate::~QGnomeThemePrivate()
+{
+ if (systemFont)
+ delete systemFont;
+ if (fixedFont)
+ delete fixedFont;
+}
+
+#ifndef QT_NO_DBUS
+bool QGnomeThemePrivate::initDbus()
+{
+ dbus.reset(new QGenericUnixThemeDBusListener());
+ Q_ASSERT(dbus);
+
+ // Wrap slot in a lambda to avoid inheriting QGnomeThemePrivate from QObject
+ auto wrapper = [this](QGenericUnixThemeDBusListener::Provider provider,
+ QGenericUnixThemeDBusListener::Setting setting,
+ const QString &value) {
+ if (provider != QGenericUnixThemeDBusListener::Provider::Gnome
+ && provider != QGenericUnixThemeDBusListener::Provider::Gtk) {
+ return;
+ }
+
+ if (setting == QGenericUnixThemeDBusListener::Setting::Theme)
+ updateColorScheme(value);
+ };
+
+ return QObject::connect(dbus.get(), &QGenericUnixThemeDBusListener::settingChanged, dbus.get(), wrapper);
+}
+
+void QGnomeThemePrivate::updateColorScheme(const QString &themeName)
+{
+ const auto oldColorScheme = m_colorScheme;
+ if (themeName.contains(QLatin1StringView("light"), Qt::CaseInsensitive)) {
+ m_colorScheme = Qt::ColorScheme::Light;
+ } else if (themeName.contains(QLatin1StringView("dark"), Qt::CaseInsensitive)) {
+ m_colorScheme = Qt::ColorScheme::Dark;
+ } else {
+ m_colorScheme = Qt::ColorScheme::Unknown;
+ }
+
+ if (oldColorScheme != m_colorScheme)
+ QWindowSystemInterface::handleThemeChange();
+}
+#endif // QT_NO_DBUS
+
QGnomeTheme::QGnomeTheme()
: QPlatformTheme(new QGnomeThemePrivate())
{
@@ -726,6 +1206,12 @@ QVariant QGnomeTheme::themeHint(QPlatformTheme::ThemeHint hint) const
case QPlatformTheme::ButtonPressKeys:
return QVariant::fromValue(
QList<Qt::Key>({ Qt::Key_Space, Qt::Key_Return, Qt::Key_Enter, Qt::Key_Select }));
+ case QPlatformTheme::PreselectFirstFileInDirectory:
+ return true;
+ case QPlatformTheme::MouseCursorTheme:
+ return QVariant(mouseCursorTheme());
+ case QPlatformTheme::MouseCursorSize:
+ return QVariant(mouseCursorSize());
default:
break;
}
@@ -769,6 +1255,12 @@ QPlatformMenuBar *QGnomeTheme::createPlatformMenuBar() const
return new QDBusMenuBar();
return nullptr;
}
+
+Qt::ColorScheme QGnomeTheme::colorScheme() const
+{
+ return d_func()->m_colorScheme;
+}
+
#endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
@@ -853,3 +1345,7 @@ QStringList QGenericUnixTheme::themeNames()
}
QT_END_NAMESPACE
+
+#ifndef QT_NO_DBUS
+#include "qgenericunixthemes.moc"
+#endif // QT_NO_DBUS
diff --git a/src/gui/platform/unix/qgenericunixthemes_p.h b/src/gui/platform/unix/qgenericunixthemes_p.h
index f5f60865aa..63b20651e6 100644
--- a/src/gui/platform/unix/qgenericunixthemes_p.h
+++ b/src/gui/platform/unix/qgenericunixthemes_p.h
@@ -77,6 +77,7 @@ public:
QPlatformTheme::IconOptions iconOptions = { }) const override;
const QPalette *palette(Palette type = SystemPalette) const override;
+ Qt::ColorScheme colorScheme() const override;
const QFont *font(Font type) const override;
#ifndef QT_NO_DBUS
@@ -106,6 +107,7 @@ public:
virtual QString gtkFontName() const;
#ifndef QT_NO_DBUS
QPlatformMenuBar *createPlatformMenuBar() const override;
+ Qt::ColorScheme colorScheme() const override;
#endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;
diff --git a/src/gui/platform/unix/qunixnativeinterface.cpp b/src/gui/platform/unix/qunixnativeinterface.cpp
index 4ea88a590b..09561d9ada 100644
--- a/src/gui/platform/unix/qunixnativeinterface.cpp
+++ b/src/gui/platform/unix/qunixnativeinterface.cpp
@@ -3,7 +3,9 @@
#include <QtGui/private/qtguiglobal_p.h>
-#include <QtGui/private/qopenglcontext_p.h>
+#if QT_CONFIG(opengl)
+# include <QtGui/private/qopenglcontext_p.h>
+#endif
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformopenglcontext.h>
@@ -121,6 +123,22 @@ QOpenGLContext *QNativeInterface::QGLXContext::fromNative(GLXContext visualBased
\return the EGLDisplay associated with the underlying EGLContext.
*/
+
+/*!
+ \fn void QNativeInterface::QEGLContext::invalidateContext()
+ \since 6.5
+ \brief Marks the context as invalid
+
+ If this context is used by the Qt Quick scenegraph, this will trigger the
+ SceneGraph to destroy this context and create a new one.
+
+ Similarly to QPlatformWindow::invalidateSurface(),
+ this function can only be expected to have an effect on certain platforms,
+ such as eglfs.
+
+ \sa QOpenGLContext::isValid(), QPlatformWindow::invalidateSurface()
+*/
+
QT_DEFINE_NATIVE_INTERFACE(QEGLContext);
QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QEGLIntegration);
@@ -213,4 +231,81 @@ QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QEvdevKeyMapper);
#endif // QT_CONFIG(evdev)
+#if QT_CONFIG(wayland)
+
+/*!
+ \class QNativeInterface::QWaylandApplication
+ \inheaderfile QGuiApplication
+ \since 6.5
+ \brief Native interface to a Wayland application.
+
+ Accessed through QGuiApplication::nativeInterface().
+ \inmodule QtGui
+ \ingroup native-interfaces
+ \ingroup native-interfaces-qguiapplication
+*/
+/*!
+ \fn wl_display *QNativeInterface::QWaylandApplication::display() const
+ \return the wl_display that the application is using.
+*/
+/*!
+ \fn wl_compositor *QNativeInterface::QWaylandApplication::compositor() const
+ \return the wl_compositor that the application is using.
+*/
+/*!
+ \fn wl_keyboard *QNativeInterface::QWaylandApplication::keyboard() const
+ \return the wl_keyboard belonging to seat() if available.
+*/
+/*!
+ \fn wl_pointer *QNativeInterface::QWaylandApplication::pointer() const
+ \return the wl_pointer belonging to seat() if available.
+*/
+/*!
+ \fn wl_touch *QNativeInterface::QWaylandApplication::touch() const
+ \return the wl_touch belonging to seat() if available.
+*/
+/*!
+ \fn uint *QNativeInterface::QWaylandApplication::lastInputSerial() const
+ \return the serial of the last input event on any seat.
+*/
+/*!
+ \fn wl_seat *QNativeInterface::QWaylandApplication::lastInputSeat() const
+ \return the seat on which the last input event happened.
+*/
+/*!
+ \fn wl_seat *QNativeInterface::QWaylandApplication::seat() const
+ \return the seat associated with the default input device.
+*/
+
+QT_DEFINE_NATIVE_INTERFACE(QWaylandApplication);
+
+/*!
+ \class QNativeInterface::QWaylandScreen
+ \since 6.7
+ \brief Native interface to a screen on Wayland.
+
+ Accessed through QScreen::nativeInterface().
+ \inmodule QtGui
+ \ingroup native-interfaces
+ \ingroup native-interfaces-qscreen
+*/
+/*!
+ \fn wl_output *QNativeInterface::QWaylandScreen::output() const
+ \return the underlying wl_output of this QScreen.
+*/
+QT_DEFINE_NATIVE_INTERFACE(QWaylandScreen);
+
+/*!
+ \class QNativeInterface::QWaylandWindow
+ \since 6.5
+ \internal
+ \brief Native interface to a Wayland window.
+ \inmodule QtGui
+ \ingroup native-interfaces
+*/
+
+QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QWaylandWindow);
+
+#endif // QT_CONFIG(wayland)
+
QT_END_NAMESPACE
diff --git a/src/gui/platform/unix/qxkbcommon.cpp b/src/gui/platform/unix/qxkbcommon.cpp
index b3ee0f4948..ed29db3005 100644
--- a/src/gui/platform/unix/qxkbcommon.cpp
+++ b/src/gui/platform/unix/qxkbcommon.cpp
@@ -17,8 +17,6 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcXkbcommon, "qt.xkbcommon")
-
static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers,
xkb_state *state, xkb_keycode_t code,
bool superAsMeta, bool hyperAsMeta);
@@ -239,10 +237,14 @@ static constexpr const auto KeyTbl = qMakeArray(
Xkb2Qt<XKB_KEY_dead_small_schwa, Qt::Key_Dead_Small_Schwa>,
Xkb2Qt<XKB_KEY_dead_capital_schwa, Qt::Key_Dead_Capital_Schwa>,
Xkb2Qt<XKB_KEY_dead_greek, Qt::Key_Dead_Greek>,
+/* The following four XKB_KEY_dead keys got removed in libxkbcommon 1.6.0
+ The define check is kind of version check here. */
+#ifdef XKB_KEY_dead_lowline
Xkb2Qt<XKB_KEY_dead_lowline, Qt::Key_Dead_Lowline>,
Xkb2Qt<XKB_KEY_dead_aboveverticalline, Qt::Key_Dead_Aboveverticalline>,
Xkb2Qt<XKB_KEY_dead_belowverticalline, Qt::Key_Dead_Belowverticalline>,
Xkb2Qt<XKB_KEY_dead_longsolidusoverlay, Qt::Key_Dead_Longsolidusoverlay>,
+#endif
// Special keys from X.org - This include multimedia keys,
// wireless/bluetooth/uwb keys, special launcher keys, etc.
@@ -298,6 +300,7 @@ static constexpr const auto KeyTbl = qMakeArray(
Xkb2Qt<XKB_KEY_XF86Book, Qt::Key_Book>,
Xkb2Qt<XKB_KEY_XF86CD, Qt::Key_CD>,
Xkb2Qt<XKB_KEY_XF86Calculater, Qt::Key_Calculator>,
+ Xkb2Qt<XKB_KEY_XF86Calculator, Qt::Key_Calculator>,
Xkb2Qt<XKB_KEY_XF86Clear, Qt::Key_Clear>,
Xkb2Qt<XKB_KEY_XF86ClearGrab, Qt::Key_ClearGrab>,
Xkb2Qt<XKB_KEY_XF86Close, Qt::Key_Close>,
@@ -461,7 +464,7 @@ QList<xkb_keysym_t> QXkbCommon::toKeysym(QKeyEvent *event)
// From libxkbcommon keysym-utf.c:
// "We allow to represent any UCS character in the range U-00000000 to
// U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff."
- for (uint utf32 : qAsConst(ucs4))
+ for (uint utf32 : std::as_const(ucs4))
keysyms.append(utf32 | 0x01000000);
return keysyms;
@@ -512,13 +515,13 @@ static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers mod
// numeric keypad keys
qtKey = Qt::Key_0 + (keysym - XKB_KEY_KP_0);
} else if (QXkbCommon::isLatin1(keysym)) {
- // Upper-case first, since Qt::Keys are defined in terms of their
- // upper-case versions.
+ // Most Qt::Key values are determined by their upper-case version,
+ // where this is in the Latin-1 repertoire. So start with that:
qtKey = QXkbCommon::qxkbcommon_xkb_keysym_to_upper(keysym);
- // Upper-casing a Latin1 character might move it out of Latin1 range,
- // for example U+00B5 MICRO SIGN, which upper-case equivalent is
- // U+039C GREEK CAPITAL LETTER MU. If that's the case, then map the
- // original lower-case character.
+ // However, Key_mu and Key_ydiaeresis are U+00B5 MICRO SIGN and
+ // U+00FF LATIN SMALL LETTER Y WITH DIAERESIS, both lower-case,
+ // with upper-case forms outside Latin-1, so use them as they are
+ // since they're the Qt::Key values.
if (!QXkbCommon::isLatin1(qtKey))
qtKey = keysym;
} else {
@@ -564,7 +567,7 @@ static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers mod
return qtKey;
}
-Qt::KeyboardModifiers QXkbCommon::modifiers(struct xkb_state *state)
+Qt::KeyboardModifiers QXkbCommon::modifiers(struct xkb_state *state, xkb_keysym_t keysym)
{
Qt::KeyboardModifiers modifiers = Qt::NoModifier;
@@ -577,6 +580,9 @@ Qt::KeyboardModifiers QXkbCommon::modifiers(struct xkb_state *state)
if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) > 0)
modifiers |= Qt::MetaModifier;
+ if (isKeypad(keysym))
+ modifiers |= Qt::KeypadModifier;
+
return modifiers;
}
@@ -593,10 +599,24 @@ static const Qt::KeyboardModifiers ModsTbl[] = {
Qt::NoModifier // Fall-back to raw Key_*, for non-latin1 kb layouts
};
+/*
+ Compatibility until all sub modules have transitioned to new API below
+*/
QList<int> QXkbCommon::possibleKeys(xkb_state *state, const QKeyEvent *event,
bool superAsMeta, bool hyperAsMeta)
{
QList<int> result;
+ auto keyCombinations = possibleKeyCombinations(state, event, superAsMeta, hyperAsMeta);
+ for (auto keyCombination : keyCombinations)
+ result << keyCombination.toCombined();
+
+ return result;
+}
+
+QList<QKeyCombination> QXkbCommon::possibleKeyCombinations(xkb_state *state, const QKeyEvent *event,
+ bool superAsMeta, bool hyperAsMeta)
+{
+ QList<QKeyCombination> result;
quint32 keycode = event->nativeScanCode();
if (!keycode)
return result;
@@ -610,7 +630,7 @@ QList<int> QXkbCommon::possibleKeys(xkb_state *state, const QKeyEvent *event,
ScopedXKBState scopedXkbQueryState(xkb_state_new(keymap));
xkb_state *queryState = scopedXkbQueryState.get();
if (!queryState) {
- qCWarning(lcXkbcommon) << Q_FUNC_INFO << "failed to compile xkb keymap";
+ qCWarning(lcQpaKeyMapper) << Q_FUNC_INFO << "failed to compile xkb keymap";
return result;
}
// get kb state from the master state and update the temporary state
@@ -636,7 +656,7 @@ QList<int> QXkbCommon::possibleKeys(xkb_state *state, const QKeyEvent *event,
int baseQtKey = keysymToQtKey_internal(sym, modifiers, queryState, keycode, superAsMeta, hyperAsMeta);
if (baseQtKey)
- result += (baseQtKey + int(modifiers));
+ result += QKeyCombination::fromCombined(baseQtKey + int(modifiers));
xkb_mod_index_t shiftMod = xkb_keymap_mod_get_index(keymap, "Shift");
xkb_mod_index_t altMod = xkb_keymap_mod_get_index(keymap, "Alt");
@@ -682,8 +702,9 @@ QList<int> QXkbCommon::possibleKeys(xkb_state *state, const QKeyEvent *event,
// catch only more specific shortcuts, i.e. Ctrl+Shift+= also generates Ctrl++ and +,
// but Ctrl++ is more specific than +, so we should skip the last one
bool ambiguous = false;
- for (int shortcut : qAsConst(result)) {
- if (int(shortcut & ~Qt::KeyboardModifierMask) == qtKey && (shortcut & mods) == mods) {
+ for (auto keyCombination : std::as_const(result)) {
+ if (keyCombination.key() == qtKey
+ && (keyCombination.keyboardModifiers() & mods) == mods) {
ambiguous = true;
break;
}
@@ -691,7 +712,7 @@ QList<int> QXkbCommon::possibleKeys(xkb_state *state, const QKeyEvent *event,
if (ambiguous)
continue;
- result += (qtKey + int(mods));
+ result += QKeyCombination::fromCombined(qtKey + int(mods));
}
}
@@ -723,13 +744,15 @@ void QXkbCommon::verifyHasLatinLayout(xkb_keymap *keymap)
// selected layouts is irrelevant. Properly functioning desktop environments
// handle this behind the scenes, even if no latin key based layout has been
// explicitly listed in the selected layouts.
- qCDebug(lcXkbcommon, "no keyboard layouts with latin keys present");
+ qCDebug(lcQpaKeyMapper, "no keyboard layouts with latin keys present");
}
xkb_keysym_t QXkbCommon::lookupLatinKeysym(xkb_state *state, xkb_keycode_t keycode)
{
xkb_layout_index_t layout;
xkb_keysym_t sym = XKB_KEY_NoSymbol;
+ if (!state)
+ return sym;
xkb_keymap *keymap = xkb_state_get_keymap(state);
const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts_for_key(keymap, keycode);
const xkb_layout_index_t currentLayout = xkb_state_key_get_layout(state, keycode);
@@ -795,7 +818,7 @@ void QXkbCommon::setXkbContext(QPlatformInputContext *inputContext, struct xkb_c
QMetaMethod method = inputContext->metaObject()->method(methodIndex);
Q_ASSERT(method.isValid());
if (!method.isValid())
- qCWarning(lcXkbcommon) << normalizedSignature << "not found on" << inputContextClassName;
+ qCWarning(lcQpaKeyMapper) << normalizedSignature << "not found on" << inputContextClassName;
return method;
}();
diff --git a/src/gui/platform/unix/qxkbcommon_p.h b/src/gui/platform/unix/qxkbcommon_p.h
index adc96b2ad4..a40d794451 100644
--- a/src/gui/platform/unix/qxkbcommon_p.h
+++ b/src/gui/platform/unix/qxkbcommon_p.h
@@ -23,12 +23,12 @@
#include <xkbcommon/xkbcommon.h>
+#include <qpa/qplatformkeymapper.h>
+
#include <memory>
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcXkbcommon)
-
class QEvent;
class QKeyEvent;
class QPlatformInputContext;
@@ -44,26 +44,67 @@ public:
static int keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers);
static int keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers,
xkb_state *state, xkb_keycode_t code,
- bool superAsMeta = false, bool hyperAsMeta = false);
+ bool superAsMeta = true, bool hyperAsMeta = true);
// xkbcommon_* API is part of libxkbcommon internals, with modifications as
// described in the header of the implementation file.
static void xkbcommon_XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t *upper);
static xkb_keysym_t qxkbcommon_xkb_keysym_to_upper(xkb_keysym_t ks);
- static Qt::KeyboardModifiers modifiers(struct xkb_state *state);
+ static Qt::KeyboardModifiers modifiers(struct xkb_state *state, xkb_keysym_t keysym = XKB_KEY_VoidSymbol);
- static QList<int> possibleKeys(xkb_state *state, const QKeyEvent *event,
- bool superAsMeta = false, bool hyperAsMeta = false);
+ static QList<int> possibleKeys(xkb_state *state,
+ const QKeyEvent *event, bool superAsMeta = false, bool hyperAsMeta = false);
+ static QList<QKeyCombination> possibleKeyCombinations(xkb_state *state,
+ const QKeyEvent *event, bool superAsMeta = false, bool hyperAsMeta = false);
static void verifyHasLatinLayout(xkb_keymap *keymap);
static xkb_keysym_t lookupLatinKeysym(xkb_state *state, xkb_keycode_t keycode);
static bool isLatin1(xkb_keysym_t sym) {
- return sym <= 0xff;
+ return sym >= 0x20 && sym <= 0xff;
}
static bool isKeypad(xkb_keysym_t sym) {
- return sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_9;
+ switch (sym) {
+ case XKB_KEY_KP_Space:
+ case XKB_KEY_KP_Tab:
+ case XKB_KEY_KP_Enter:
+ case XKB_KEY_KP_F1:
+ case XKB_KEY_KP_F2:
+ case XKB_KEY_KP_F3:
+ case XKB_KEY_KP_F4:
+ case XKB_KEY_KP_Home:
+ case XKB_KEY_KP_Left:
+ case XKB_KEY_KP_Up:
+ case XKB_KEY_KP_Right:
+ case XKB_KEY_KP_Down:
+ case XKB_KEY_KP_Prior:
+ case XKB_KEY_KP_Next:
+ case XKB_KEY_KP_End:
+ case XKB_KEY_KP_Begin:
+ case XKB_KEY_KP_Insert:
+ case XKB_KEY_KP_Delete:
+ case XKB_KEY_KP_Equal:
+ case XKB_KEY_KP_Multiply:
+ case XKB_KEY_KP_Add:
+ case XKB_KEY_KP_Separator:
+ case XKB_KEY_KP_Subtract:
+ case XKB_KEY_KP_Decimal:
+ case XKB_KEY_KP_Divide:
+ case XKB_KEY_KP_0:
+ case XKB_KEY_KP_1:
+ case XKB_KEY_KP_2:
+ case XKB_KEY_KP_3:
+ case XKB_KEY_KP_4:
+ case XKB_KEY_KP_5:
+ case XKB_KEY_KP_6:
+ case XKB_KEY_KP_7:
+ case XKB_KEY_KP_8:
+ case XKB_KEY_KP_9:
+ return true;
+ default:
+ return false;
+ }
}
static void setXkbContext(QPlatformInputContext *inputContext, struct xkb_context *context);
diff --git a/src/gui/platform/wasm/qlocalfileapi.cpp b/src/gui/platform/wasm/qlocalfileapi.cpp
new file mode 100644
index 0000000000..76b99361c4
--- /dev/null
+++ b/src/gui/platform/wasm/qlocalfileapi.cpp
@@ -0,0 +1,211 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qlocalfileapi_p.h"
+#include <private/qstdweb_p.h>
+#include <QtCore/QRegularExpression>
+
+QT_BEGIN_NAMESPACE
+namespace LocalFileApi {
+namespace {
+std::string qtFilterListToFileInputAccept(const QStringList &filterList)
+{
+ QStringList transformed;
+ for (const auto &filter : filterList) {
+ const auto type = Type::fromQt(filter);
+ if (type && type->accept()) {
+ const auto &extensions = type->accept()->mimeType().extensions();
+ std::transform(extensions.begin(), extensions.end(), std::back_inserter(transformed),
+ [](const Type::Accept::MimeType::Extension &extension) {
+ return extension.value().toString();
+ });
+ }
+ }
+ return transformed.join(QStringLiteral(",")).toStdString();
+}
+
+std::optional<emscripten::val> qtFilterListToTypes(const QStringList &filterList)
+{
+ using namespace qstdweb;
+ using namespace emscripten;
+ auto types = emscripten::val::array();
+
+ for (const auto &fileFilter : filterList) {
+ auto type = Type::fromQt(fileFilter);
+ if (type) {
+ auto jsType = emscripten::val::object();
+ jsType.set("description", type->description().toString().toStdString());
+ if (type->accept()) {
+ jsType.set("accept", ([&mimeType = type->accept()->mimeType()]() {
+ val acceptDict = val::object();
+
+ QList<emscripten::val> extensions;
+ extensions.reserve(mimeType.extensions().size());
+ std::transform(
+ mimeType.extensions().begin(), mimeType.extensions().end(),
+ std::back_inserter(extensions),
+ [](const Type::Accept::MimeType::Extension &extension) {
+ return val(extension.value().toString().toStdString());
+ });
+ acceptDict.set("application/octet-stream",
+ emscripten::val::array(extensions.begin(),
+ extensions.end()));
+ return acceptDict;
+ })());
+ }
+ types.call<void>("push", std::move(jsType));
+ }
+ }
+
+ return types["length"].as<int>() == 0 ? std::optional<emscripten::val>() : types;
+}
+} // namespace
+
+Type::Type(QStringView description, std::optional<Accept> accept)
+ : m_description(description.trimmed()), m_accept(std::move(accept))
+{
+}
+
+Type::~Type() = default;
+
+std::optional<Type> Type::fromQt(QStringView type)
+{
+ using namespace emscripten;
+
+ // Accepts either a string in format:
+ // GROUP3
+ // or in this format:
+ // GROUP1 (GROUP2)
+ // Group 1 is treated as the description, whereas group 2 or 3 are treated as the filter list.
+ static QRegularExpression regex(
+ QString(QStringLiteral("(?:(?:([^(]*)\\(([^()]+)\\)[^)]*)|([^()]+))")));
+ const auto match = regex.matchView(type);
+
+ if (!match.hasMatch())
+ return std::nullopt;
+
+ constexpr size_t DescriptionIndex = 1;
+ constexpr size_t FilterListFromParensIndex = 2;
+ constexpr size_t PlainFilterListIndex = 3;
+
+ const auto description = match.hasCaptured(DescriptionIndex)
+ ? match.capturedView(DescriptionIndex)
+ : QStringView();
+ const auto filterList = match.capturedView(match.hasCaptured(FilterListFromParensIndex)
+ ? FilterListFromParensIndex
+ : PlainFilterListIndex);
+
+ auto accept = Type::Accept::fromQt(filterList);
+ if (!accept)
+ return std::nullopt;
+
+ return Type(description, std::move(*accept));
+}
+
+Type::Accept::Accept() = default;
+
+Type::Accept::~Accept() = default;
+
+std::optional<Type::Accept> Type::Accept::fromQt(QStringView qtRepresentation)
+{
+ Accept accept;
+
+ // Used for accepting multiple extension specifications on a filter list.
+ // The next group of non-empty characters.
+ static QRegularExpression internalRegex(QString(QStringLiteral("([^\\s]+)\\s*")));
+ int offset = 0;
+ auto internalMatch = internalRegex.matchView(qtRepresentation, offset);
+ MimeType mimeType;
+
+ while (internalMatch.hasMatch()) {
+ auto webExtension = MimeType::Extension::fromQt(internalMatch.capturedView(1));
+
+ if (!webExtension)
+ return std::nullopt;
+
+ mimeType.addExtension(*webExtension);
+
+ internalMatch = internalRegex.matchView(qtRepresentation, internalMatch.capturedEnd());
+ }
+
+ accept.setMimeType(mimeType);
+ return accept;
+}
+
+void Type::Accept::setMimeType(MimeType mimeType)
+{
+ m_mimeType = std::move(mimeType);
+}
+
+Type::Accept::MimeType::MimeType() = default;
+
+Type::Accept::MimeType::~MimeType() = default;
+
+void Type::Accept::MimeType::addExtension(Extension extension)
+{
+ m_extensions.push_back(std::move(extension));
+}
+
+Type::Accept::MimeType::Extension::Extension(QStringView extension) : m_value(extension) { }
+
+Type::Accept::MimeType::Extension::~Extension() = default;
+
+std::optional<Type::Accept::MimeType::Extension>
+Type::Accept::MimeType::Extension::fromQt(QStringView qtRepresentation)
+{
+ // Checks for a filter that matches everything:
+ // Any number of asterisks or any number of asterisks with a '.' between them.
+ // The web filter does not support wildcards.
+ static QRegularExpression qtAcceptAllRegex(
+ QRegularExpression::anchoredPattern(QString(QStringLiteral("[*]+|[*]+\\.[*]+"))));
+ if (qtAcceptAllRegex.matchView(qtRepresentation).hasMatch())
+ return std::nullopt;
+
+ // Checks for correctness. The web filter only allows filename extensions and does not filter
+ // the actual filenames, therefore we check whether the filter provided only filters for the
+ // extension.
+ static QRegularExpression qtFilenameMatcherRegex(
+ QRegularExpression::anchoredPattern(QString(QStringLiteral("(\\*?)(\\.[^*]+)"))));
+
+ auto extensionMatch = qtFilenameMatcherRegex.matchView(qtRepresentation);
+ if (extensionMatch.hasMatch())
+ return Extension(extensionMatch.capturedView(2));
+
+ // Mapping impossible.
+ return std::nullopt;
+}
+
+emscripten::val makeOpenFileOptions(const QStringList &filterList, bool acceptMultiple)
+{
+ auto options = emscripten::val::object();
+ if (auto typeList = qtFilterListToTypes(filterList); typeList) {
+ options.set("types", std::move(*typeList));
+ options.set("excludeAcceptAllOption", true);
+ }
+
+ options.set("multiple", acceptMultiple);
+
+ return options;
+}
+
+emscripten::val makeSaveFileOptions(const QStringList &filterList, const std::string& suggestedName)
+{
+ auto options = emscripten::val::object();
+
+ if (!suggestedName.empty())
+ options.set("suggestedName", emscripten::val(suggestedName));
+
+ if (auto typeList = qtFilterListToTypes(filterList))
+ options.set("types", emscripten::val(std::move(*typeList)));
+
+ return options;
+}
+
+std::string makeFileInputAccept(const QStringList &filterList)
+{
+ return qtFilterListToFileInputAccept(filterList);
+}
+
+} // namespace LocalFileApi
+
+QT_END_NAMESPACE
diff --git a/src/gui/platform/wasm/qlocalfileapi_p.h b/src/gui/platform/wasm/qlocalfileapi_p.h
new file mode 100644
index 0000000000..1398d674d8
--- /dev/null
+++ b/src/gui/platform/wasm/qlocalfileapi_p.h
@@ -0,0 +1,94 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QLOCALFILEAPI_P_H
+#define QLOCALFILEAPI_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 <private/qglobal_p.h>
+#include <qstringview.h>
+#include <emscripten/val.h>
+#include <cstdint>
+#include <functional>
+
+QT_BEGIN_NAMESPACE
+
+namespace LocalFileApi {
+class Q_AUTOTEST_EXPORT Type
+{
+public:
+ class Accept {
+ public:
+ class MimeType {
+ public:
+ class Extension {
+ public:
+ static std::optional<Extension> fromQt(QStringView extension);
+
+ ~Extension();
+
+ const QStringView &value() const { return m_value; }
+
+ private:
+ explicit Extension(QStringView extension);
+
+ QStringView m_value;
+ };
+
+ MimeType();
+ ~MimeType();
+
+ void addExtension(Extension type);
+
+ const std::vector<Extension> &extensions() const { return m_extensions; }
+
+ private:
+ std::vector<Extension> m_extensions;
+ };
+
+ static std::optional<Accept> fromQt(QStringView type);
+
+ ~Accept();
+
+ void setMimeType(MimeType mimeType);
+
+ const MimeType &mimeType() const { return m_mimeType; }
+
+ private:
+ Accept();
+ MimeType m_mimeType;
+ };
+
+ Type(QStringView description, std::optional<Accept> accept);
+ ~Type();
+
+ static std::optional<Type> fromQt(QStringView type);
+ const QStringView &description() const { return m_description; }
+ const std::optional<Accept> &accept() const { return m_accept; }
+
+private:
+ QStringView m_description;
+ std::optional<Accept> m_accept;
+};
+
+Q_AUTOTEST_EXPORT emscripten::val makeOpenFileOptions(const QStringList &filterList,
+ bool acceptMultiple);
+Q_AUTOTEST_EXPORT emscripten::val makeSaveFileOptions(const QStringList &filterList,
+ const std::string &suggestedName);
+
+Q_AUTOTEST_EXPORT std::string makeFileInputAccept(const QStringList &filterList);
+
+} // namespace LocalFileApi
+QT_END_NAMESPACE
+
+#endif // QLOCALFILEAPI_P_H
diff --git a/src/gui/platform/wasm/qwasmlocalfileaccess.cpp b/src/gui/platform/wasm/qwasmlocalfileaccess.cpp
index 172c8f6814..a946cda043 100644
--- a/src/gui/platform/wasm/qwasmlocalfileaccess.cpp
+++ b/src/gui/platform/wasm/qwasmlocalfileaccess.cpp
@@ -2,19 +2,110 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwasmlocalfileaccess_p.h"
+#include "qlocalfileapi_p.h"
#include <private/qstdweb_p.h>
#include <emscripten.h>
#include <emscripten/bind.h>
#include <emscripten/html5.h>
#include <emscripten/val.h>
+#include <QtCore/qregularexpression.h>
+
QT_BEGIN_NAMESPACE
namespace QWasmLocalFileAccess {
+namespace FileDialog {
+namespace {
+bool hasLocalFilesApi()
+{
+ return !qstdweb::window()["showOpenFilePicker"].isUndefined();
+}
+
+void showOpenViaHTMLPolyfill(const QStringList &accept, FileSelectMode fileSelectMode,
+ qstdweb::PromiseCallbacks onFilesSelected)
+{
+ // Create file input html element which will display a native file dialog
+ // and call back to our onchange handler once the user has selected
+ // one or more files.
+ emscripten::val document = emscripten::val::global("document");
+ emscripten::val input = document.call<emscripten::val>("createElement", std::string("input"));
+ input.set("type", "file");
+ input.set("style", "display:none");
+ input.set("accept", LocalFileApi::makeFileInputAccept(accept));
+ Q_UNUSED(accept);
+ input.set("multiple", emscripten::val(fileSelectMode == FileSelectMode::MultipleFiles));
+
+ // Note: there is no event in case the user cancels the file dialog.
+ static std::unique_ptr<qstdweb::EventCallback> changeEvent;
+ auto callback = [=](emscripten::val) { onFilesSelected.thenFunc(input["files"]); };
+ changeEvent = std::make_unique<qstdweb::EventCallback>(input, "change", callback);
+
+ // Activate file input
+ emscripten::val body = document["body"];
+ body.call<void>("appendChild", input);
+ input.call<void>("click");
+ body.call<void>("removeChild", input);
+}
+
+void showOpenViaLocalFileApi(const QStringList &accept, FileSelectMode fileSelectMode,
+ qstdweb::PromiseCallbacks callbacks)
+{
+ using namespace qstdweb;
+
+ auto options = LocalFileApi::makeOpenFileOptions(accept, fileSelectMode == FileSelectMode::MultipleFiles);
+
+ Promise::make(
+ window(), QStringLiteral("showOpenFilePicker"),
+ {
+ .thenFunc = [=](emscripten::val fileHandles) mutable {
+ std::vector<emscripten::val> filePromises;
+ filePromises.reserve(fileHandles["length"].as<int>());
+ for (int i = 0; i < fileHandles["length"].as<int>(); ++i)
+ filePromises.push_back(fileHandles[i].call<emscripten::val>("getFile"));
+ Promise::all(std::move(filePromises), callbacks);
+ },
+ .catchFunc = callbacks.catchFunc,
+ .finallyFunc = callbacks.finallyFunc,
+ }, std::move(options));
+}
+void showSaveViaLocalFileApi(const std::string &fileNameHint, qstdweb::PromiseCallbacks callbacks)
+{
+ using namespace qstdweb;
+ using namespace emscripten;
+
+ auto options = LocalFileApi::makeSaveFileOptions(QStringList(), fileNameHint);
+
+ Promise::make(
+ window(), QStringLiteral("showSaveFilePicker"),
+ std::move(callbacks), std::move(options));
+}
+} // namespace
+
+void showOpen(const QStringList &accept, FileSelectMode fileSelectMode,
+ qstdweb::PromiseCallbacks callbacks)
+{
+ hasLocalFilesApi() ?
+ showOpenViaLocalFileApi(accept, fileSelectMode, std::move(callbacks)) :
+ showOpenViaHTMLPolyfill(accept, fileSelectMode, std::move(callbacks));
+}
+
+bool canShowSave()
+{
+ return hasLocalFilesApi();
+}
+
+void showSave(const std::string &fileNameHint, qstdweb::PromiseCallbacks callbacks)
+{
+ Q_ASSERT(canShowSave());
+ showSaveViaLocalFileApi(fileNameHint, std::move(callbacks));
+}
+} // namespace FileDialog
+
+namespace {
void readFiles(const qstdweb::FileList &fileList,
- const std::function<char *(uint64_t size, const std::string name)> &acceptFile,
- const std::function<void ()> &fileDataReady)
+ const std::function<char *(uint64_t size, const std::string name)> &acceptFile,
+ const std::function<void ()> &fileDataReady)
{
auto readFile = std::make_shared<std::function<void(int)>>();
@@ -25,7 +116,7 @@ void readFiles(const qstdweb::FileList &fileList,
return;
}
- const qstdweb::File file = fileList[fileIndex];
+ const qstdweb::File file = qstdweb::File(fileList[fileIndex]);
// Ask caller if the file should be accepted
char *buffer = acceptFile(file.size(), file.name());
@@ -35,7 +126,7 @@ void readFiles(const qstdweb::FileList &fileList,
}
// Read file data into caller-provided buffer
- file.stream(buffer, [=]() {
+ file.stream(buffer, [readFile = readFile.get(), fileIndex, fileDataReady]() {
fileDataReady();
(*readFile)(fileIndex + 1);
});
@@ -44,53 +135,21 @@ void readFiles(const qstdweb::FileList &fileList,
(*readFile)(0);
}
-typedef std::function<void (const qstdweb::FileList &fileList)> OpenFileDialogCallback;
-void openFileDialog(const std::string &accept, FileSelectMode fileSelectMode,
- const OpenFileDialogCallback &filesSelected)
+QStringList makeFilterList(const std::string &qtAcceptList)
{
- // Create file input html element which will display a native file dialog
- // and call back to our onchange handler once the user has selected
- // one or more files.
- emscripten::val document = emscripten::val::global("document");
- emscripten::val input = document.call<emscripten::val>("createElement", std::string("input"));
- input.set("type", "file");
- input.set("style", "display:none");
- input.set("accept", emscripten::val(accept));
- input.set("multiple", emscripten::val(fileSelectMode == MultipleFiles));
-
- // Note: there is no event in case the user cancels the file dialog.
- static std::unique_ptr<qstdweb::EventCallback> changeEvent;
- auto callback = [=](emscripten::val) { filesSelected(qstdweb::FileList(input["files"])); };
- changeEvent.reset(new qstdweb::EventCallback(input, "change", callback));
-
- // Activate file input
- emscripten::val body = document["body"];
- body.call<void>("appendChild", input);
- input.call<void>("click");
- body.call<void>("removeChild", input);
-}
+ // copy of qt_make_filter_list() from qfiledialog.cpp
+ auto filter = QString::fromStdString(qtAcceptList);
+ if (filter.isEmpty())
+ return QStringList();
+ QString sep(";;");
+ if (!filter.contains(sep) && filter.contains(u'\n'))
+ sep = u'\n';
-void openFiles(const std::string &accept, FileSelectMode fileSelectMode,
- const std::function<void (int fileCount)> &fileDialogClosed,
- const std::function<char *(uint64_t size, const std::string name)> &acceptFile,
- const std::function<void()> &fileDataReady)
-{
- openFileDialog(accept, fileSelectMode, [=](const qstdweb::FileList &files) {
- fileDialogClosed(files.length());
- readFiles(files, acceptFile, fileDataReady);
- });
+ return filter.split(sep);
}
-
-void openFile(const std::string &accept,
- const std::function<void (bool fileSelected)> &fileDialogClosed,
- const std::function<char *(uint64_t size, const std::string name)> &acceptFile,
- const std::function<void()> &fileDataReady)
-{
- auto fileDialogClosedWithInt = [=](int fileCount) { fileDialogClosed(fileCount != 0); };
- openFiles(accept, FileSelectMode::SingleFile, fileDialogClosedWithInt, acceptFile, fileDataReady);
}
-void saveFile(const char *content, size_t size, const std::string &fileNameHint)
+void downloadDataAsFile(const char *content, size_t size, const std::string &fileNameHint)
{
// Save a file by creating programmatically clicking a download
// link to an object url to a Blob containing a copy of the file
@@ -98,7 +157,7 @@ void saveFile(const char *content, size_t size, const std::string &fileNameHint)
// can be released as soon as this function returns.
qstdweb::Blob contentBlob = qstdweb::Blob::copyFrom(content, size);
emscripten::val document = emscripten::val::global("document");
- emscripten::val window = emscripten::val::global("window");
+ emscripten::val window = qstdweb::window();
emscripten::val contentUrl = window["URL"].call<emscripten::val>("createObjectURL", contentBlob.val());
emscripten::val contentLink = document.call<emscripten::val>("createElement", std::string("a"));
contentLink.set("href", contentUrl);
@@ -113,6 +172,118 @@ void saveFile(const char *content, size_t size, const std::string &fileNameHint)
window["URL"].call<emscripten::val>("revokeObjectURL", contentUrl);
}
+void openFiles(const std::string &accept, FileSelectMode fileSelectMode,
+ const std::function<void (int fileCount)> &fileDialogClosed,
+ const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
+ const std::function<void()> &fileDataReady)
+{
+ FileDialog::showOpen(makeFilterList(accept), fileSelectMode, {
+ .thenFunc = [=](emscripten::val result) {
+ auto files = qstdweb::FileList(result);
+ fileDialogClosed(files.length());
+ readFiles(files, acceptFile, fileDataReady);
+ },
+ .catchFunc = [=](emscripten::val) {
+ fileDialogClosed(0);
+ }
+ });
+}
+
+void openFile(const std::string &accept,
+ const std::function<void (bool fileSelected)> &fileDialogClosed,
+ const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
+ const std::function<void()> &fileDataReady)
+{
+ auto fileDialogClosedWithInt = [=](int fileCount) { fileDialogClosed(fileCount != 0); };
+ openFiles(accept, FileSelectMode::SingleFile, fileDialogClosedWithInt, acceptFile, fileDataReady);
+}
+
+void saveDataToFileInChunks(emscripten::val fileHandle, const QByteArray &data)
+{
+ using namespace emscripten;
+ using namespace qstdweb;
+
+ Promise::make(fileHandle, QStringLiteral("createWritable"), {
+ .thenFunc = [=](val writable) {
+ struct State {
+ size_t written;
+ std::function<void(val result)> continuation;
+ };
+
+ static constexpr size_t desiredChunkSize = 1024u;
+#if defined(__EMSCRIPTEN_SHARED_MEMORY__)
+ qstdweb::Uint8Array chunkArray(desiredChunkSize);
+#endif
+
+ auto state = std::make_shared<State>();
+ state->written = 0u;
+ state->continuation = [=](val) mutable {
+ const size_t remaining = data.size() - state->written;
+ if (remaining == 0) {
+ Promise::make(writable, QStringLiteral("close"), { .thenFunc = [=](val) {} });
+ state.reset();
+ return;
+ }
+
+ const auto currentChunkSize = std::min(remaining, desiredChunkSize);
+
+#if defined(__EMSCRIPTEN_SHARED_MEMORY__)
+ // If shared memory is used, WebAssembly.Memory is instantiated with the 'shared'
+ // option on. Passing a typed_memory_view to SharedArrayBuffer to
+ // FileSystemWritableFileStream.write is disallowed by security policies, so we
+ // need to make a copy of the data to a chunk array buffer.
+ Promise::make(
+ writable, QStringLiteral("write"),
+ {
+ .thenFunc = state->continuation,
+ },
+ chunkArray.copyFrom(data.constData() + state->written, currentChunkSize)
+ .val()
+ .call<emscripten::val>("subarray", emscripten::val(0),
+ emscripten::val(currentChunkSize)));
+#else
+ Promise::make(writable, QStringLiteral("write"),
+ {
+ .thenFunc = state->continuation,
+ },
+ val(typed_memory_view(currentChunkSize, data.constData() + state->written)));
+#endif
+ state->written += currentChunkSize;
+ };
+
+ state->continuation(val::undefined());
+ },
+ });
+}
+
+void saveFile(const QByteArray &data, const std::string &fileNameHint)
+{
+ if (!FileDialog::canShowSave()) {
+ downloadDataAsFile(data.constData(), data.size(), fileNameHint);
+ return;
+ }
+
+ FileDialog::showSave(fileNameHint, {
+ .thenFunc = [=](emscripten::val result) {
+ saveDataToFileInChunks(result, data);
+ },
+ });
+}
+
+void saveFile(const char *content, size_t size, const std::string &fileNameHint)
+{
+ if (!FileDialog::canShowSave()) {
+ downloadDataAsFile(content, size, fileNameHint);
+ return;
+ }
+
+ FileDialog::showSave(fileNameHint, {
+ .thenFunc = [=](emscripten::val result) {
+ saveDataToFileInChunks(result, QByteArray(content, size));
+ },
+ });
+}
+
} // namespace QWasmLocalFileAccess
QT_END_NAMESPACE
diff --git a/src/gui/platform/wasm/qwasmlocalfileaccess_p.h b/src/gui/platform/wasm/qwasmlocalfileaccess_p.h
index eb73463759..77b14577f7 100644
--- a/src/gui/platform/wasm/qwasmlocalfileaccess_p.h
+++ b/src/gui/platform/wasm/qwasmlocalfileaccess_p.h
@@ -23,18 +23,19 @@ QT_BEGIN_NAMESPACE
namespace QWasmLocalFileAccess {
-enum FileSelectMode { SingleFile, MultipleFiles };
+enum class FileSelectMode { SingleFile, MultipleFiles };
Q_CORE_EXPORT void openFiles(const std::string &accept, FileSelectMode fileSelectMode,
const std::function<void (int fileCount)> &fileDialogClosed,
- const std::function<char *(uint64_t size, const std::string name)> &acceptFile,
+ const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
const std::function<void()> &fileDataReady);
Q_CORE_EXPORT void openFile(const std::string &accept,
const std::function<void (bool fileSelected)> &fileDialogClosed,
- const std::function<char *(uint64_t size, const std::string name)> &acceptFile,
+ const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
const std::function<void()> &fileDataReady);
+Q_CORE_EXPORT void saveFile(const QByteArray &data, const std::string &fileNameHint);
Q_CORE_EXPORT void saveFile(const char *content, size_t size, const std::string &fileNameHint);
} // namespace QWasmLocalFileAccess
diff --git a/src/gui/platform/wasm/qwasmnativeinterface.cpp b/src/gui/platform/wasm/qwasmnativeinterface.cpp
new file mode 100644
index 0000000000..7313629a8d
--- /dev/null
+++ b/src/gui/platform/wasm/qwasmnativeinterface.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+
+
+#include <QtGui/private/qguiapplication_p.h>
+#include <qpa/qplatformintegration.h>
+#include <qpa/qplatformwindow_p.h>
+
+
+QT_BEGIN_NAMESPACE
+
+using namespace QNativeInterface::Private;
+
+QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QWasmWindow);
+
+QT_END_NAMESPACE
diff --git a/src/gui/platform/windows/qwindowsguieventdispatcher.cpp b/src/gui/platform/windows/qwindowsguieventdispatcher.cpp
index f70655380d..c2f0efe96e 100644
--- a/src/gui/platform/windows/qwindowsguieventdispatcher.cpp
+++ b/src/gui/platform/windows/qwindowsguieventdispatcher.cpp
@@ -197,3 +197,5 @@ const char *QWindowsGuiEventDispatcher::windowsMessageName(UINT msg)
}
QT_END_NAMESPACE
+
+#include "moc_qwindowsguieventdispatcher_p.cpp"
diff --git a/src/gui/platform/windows/qwindowsmimeconverter.cpp b/src/gui/platform/windows/qwindowsmimeconverter.cpp
new file mode 100644
index 0000000000..49d524cb99
--- /dev/null
+++ b/src/gui/platform/windows/qwindowsmimeconverter.cpp
@@ -0,0 +1,170 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwindowsmimeconverter.h"
+
+#include <QtCore/qt_windows.h>
+
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformintegration.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QWindowsMimeConverter
+ \brief The QWindowsMimeConverter class maps open-standard MIME to Window Clipboard formats.
+ \inmodule QtGui
+
+ Qt's drag-and-drop and clipboard facilities use the MIME standard.
+ On X11, this maps trivially to the Xdnd protocol, but on Windows
+ although some applications use MIME types to describe clipboard
+ formats, others use arbitrary non-standardized naming conventions,
+ or unnamed built-in formats of Windows.
+
+ By instantiating subclasses of QWindowsMimeConverter that provide
+ conversions between Windows Clipboard and MIME formats, you can convert
+ proprietary clipboard formats to MIME formats.
+
+ Construct an instance of your converter implementation after instantiating
+ QGuiApplication:
+
+ \code
+ int main(int argc, char **argv)
+ {
+ QGuiApplication app(argc, argv);
+ JsonMimeConverter jsonConverter;
+ }
+ \endcode
+
+ Destroying the instance will unregister the converter and remove support
+ for the conversion. It is also valid to heap-allocate the converter
+ instance; Qt takes ownership and will delete the converter object during
+ QGuiApplication shut-down.
+
+ Qt has predefined support for the following Windows Clipboard formats:
+
+ \table
+ \header \li Windows Format \li Equivalent MIME type
+ \row \li \c CF_UNICODETEXT \li \c text/plain
+ \row \li \c CF_TEXT \li \c text/plain
+ \row \li \c CF_DIB \li \c{image/xyz}, where \c xyz is
+ a \l{QImageWriter::supportedImageFormats()}{Qt image format}
+ \row \li \c CF_HDROP \li \c text/uri-list
+ \row \li \c CF_INETURL \li \c text/uri-list
+ \row \li \c CF_HTML \li \c text/html
+ \endtable
+
+ An example use of this class would be to map the Windows Metafile
+ clipboard format (\c CF_METAFILEPICT) to and from the MIME type
+ \c{image/x-wmf}. This conversion might simply be adding or removing
+ a header, or even just passing on the data. See \l{Drag and Drop}
+ for more information on choosing and definition MIME types.
+
+ You can check if a MIME type is convertible using canConvertFromMime() and
+ can perform conversions with convertToMime() and convertFromMime().
+*/
+
+
+/*!
+ \fn bool QWindowsMimeConverter::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+
+ Returns \c true if the converter can convert from the \a mimeData to
+ the format specified in \a formatetc.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn bool QWindowsMimeConverter::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
+
+ Returns \c true if the converter can convert to the \a mimeType from
+ the available formats in \a pDataObj.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QString QWindowsMimeConverter::mimeForFormat(const FORMATETC &formatetc) const
+
+ Returns the mime type that will be created form the format specified
+ in \a formatetc, or an empty string if this converter does not support
+ \a formatetc.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QList<FORMATETC> QWindowsMimeConverter::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
+
+ Returns a QList of FORMATETC structures representing the different windows clipboard
+ formats that can be provided for the \a mimeType from the \a mimeData.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QVariant QWindowsMimeConverter::convertToMime(const QString &mimeType, IDataObject *pDataObj,
+ QMetaType preferredType) const
+
+ Returns a QVariant containing the converted data for \a mimeType from \a pDataObj.
+ If possible the QVariant should be of the \a preferredType to avoid needless conversions.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn bool QWindowsMimeConverter::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
+
+ Convert the \a mimeData to the format specified in \a formatetc.
+ The converted data should then be placed in \a pmedium structure.
+
+ Return true if the conversion was successful.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ Constructs a QWindowsMimeConverter instance.
+
+ The instance is automatically registered, and will be called to convert data during
+ clipboard or drag'n'drop operations.
+
+ Call this constructor after QGuiApplication has been created.
+*/
+QWindowsMimeConverter::QWindowsMimeConverter()
+{
+ using QWindowsApplication = QNativeInterface::Private::QWindowsApplication;
+ auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(QGuiApplicationPrivate::platformIntegration());
+ Q_ASSERT(nativeWindowsApp);
+ nativeWindowsApp->registerMime(this);
+}
+
+/*!
+ Constructs a QWindowsMimeConverter instance.
+
+ The instance is automatically unregistered.
+*/
+QWindowsMimeConverter::~QWindowsMimeConverter()
+{
+ using QWindowsApplication = QNativeInterface::Private::QWindowsApplication;
+ auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(QGuiApplicationPrivate::platformIntegration());
+ Q_ASSERT(nativeWindowsApp);
+ nativeWindowsApp->unregisterMime(this);
+}
+
+/*!
+ Registers the MIME type \a mimeType, and returns an ID number
+ identifying the format on Windows.
+
+ A mime type \c {application/x-qt-windows-mime;value="WindowsType"} will be
+ registered as the clipboard format for \c WindowsType.
+*/
+int QWindowsMimeConverter::registerMimeType(const QString &mimeType)
+{
+ using QWindowsApplication = QNativeInterface::Private::QWindowsApplication;
+ auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(QGuiApplicationPrivate::platformIntegration());
+ Q_ASSERT(nativeWindowsApp);
+ return nativeWindowsApp->registerMimeType(mimeType);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/platform/windows/qwindowsmime_p.h b/src/gui/platform/windows/qwindowsmimeconverter.h
index a2fbd38119..145355fe15 100644
--- a/src/gui/platform/windows/qwindowsmime_p.h
+++ b/src/gui/platform/windows/qwindowsmimeconverter.h
@@ -1,36 +1,31 @@
-// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QWINDOWSMIME_P_H
-#define QWINDOWSMIME_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 <QtCore/qt_windows.h>
-#include <QtCore/qvariant.h>
+#ifndef QWINDOWSMIMECONVERTER_P_H
+#define QWINDOWSMIMECONVERTER_P_H
#include <QtGui/qtguiglobal.h>
-#include <QtCore/private/qglobal_p.h>
+
+struct tagFORMATETC;
+using FORMATETC = tagFORMATETC;
+struct tagSTGMEDIUM;
+using STGMEDIUM = tagSTGMEDIUM;
+struct IDataObject;
QT_BEGIN_NAMESPACE
+class QMetaType;
class QMimeData;
+class QVariant;
-namespace QNativeInterface::Private {
-
-class Q_GUI_EXPORT QWindowsMime
+class Q_GUI_EXPORT QWindowsMimeConverter
{
+ Q_DISABLE_COPY(QWindowsMimeConverter)
public:
- virtual ~QWindowsMime() = default;
+ QWindowsMimeConverter();
+ virtual ~QWindowsMimeConverter();
+
+ static int registerMimeType(const QString &mimeType);
// for converting from Qt
virtual bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const = 0;
@@ -43,8 +38,6 @@ public:
virtual QString mimeForFormat(const FORMATETC &formatetc) const = 0;
};
-} // QNativeInterface::Private
-
QT_END_NAMESPACE
-#endif // QWINDOWSMIME_P_H
+#endif // QWINDOWSMIMECONVERTER_H
diff --git a/src/gui/platform/windows/qwindowsnativeinterface.cpp b/src/gui/platform/windows/qwindowsnativeinterface.cpp
index e4339c9aa9..44f230e1d3 100644
--- a/src/gui/platform/windows/qwindowsnativeinterface.cpp
+++ b/src/gui/platform/windows/qwindowsnativeinterface.cpp
@@ -3,11 +3,11 @@
#include <QtGui/qopenglcontext.h>
#include <QtGui/private/qguiapplication_p.h>
-#include <QtGui/private/qwindowsmime_p.h>
#include <qpa/qplatformopenglcontext.h>
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformwindow.h>
#include <qpa/qplatformwindow_p.h>
+#include <qpa/qplatformscreen_p.h>
QT_BEGIN_NAMESPACE
@@ -89,6 +89,21 @@ QOpenGLContext *QNativeInterface::QWGLContext::fromNative(HGLRC context, HWND wi
QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QWindowsApplication);
/*!
+ \class QNativeInterface::QWindowsScreen
+ \since 6.7
+ \brief Native interface to a screen.
+
+ Accessed through QScreen::nativeInterface().
+ \inmodule QtGui
+ \ingroup native-interfaces
+ \ingroup native-interfaces-qscreen
+*/
+/*!
+ * \fn HWMONITOR QNativeInterface::QWindowsScreen::handle() const;
+ * \return The underlying HWMONITOR of the screen.
+ */
+QT_DEFINE_NATIVE_INTERFACE(QWindowsScreen);
+/*!
\enum QNativeInterface::Private::QWindowsApplication::TouchWindowTouchType
This enum represents the supported TouchWindow touch flags for registerTouchWindow().
@@ -166,15 +181,7 @@ QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QWindowsApplication);
\value DarkModeStyle The Windows Vista style will be turned off and
a simple dark style will be used.
- \sa isDarkMode(), setDarkModeHandling()
-*/
-
-/*!
- \fn bool QNativeInterface::Private::QWindowsApplication::isDarkMode() const = 0
- \internal
-
- Returns \c true if Windows 10 is configured to use dark mode for
- applications.
+ \sa setDarkModeHandling()
*/
/*!
@@ -208,21 +215,21 @@ QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QWindowsApplication);
*/
/*!
- \fn bool QNativeInterface::Private::QWindowsApplication::registerMime(QWindowsMime *mime)
+ \fn bool QNativeInterface::Private::QWindowsApplication::registerMime(QWindowsMimeConverter *mime)
\internal
Registers the converter \a mime to the system.
- \sa QNativeInterface::Private::QWindowsMime, unregisterMime()
+ \sa QWindowsMimeConverter, unregisterMime()
*/
/*!
- \fn void QNativeInterface::Private::QWindowsApplication::unregisterMime(QWindowsMime *mime)
+ \fn void QNativeInterface::Private::QWindowsApplication::unregisterMime(QWindowsMimeConverter *mime)
\internal
Unregisters the converter \a mime from the system.
- \sa QNativeInterface::Private::QWindowsMime, registerMime()
+ \sa QWindowsMimeConverter, registerMime()
*/
/*!
diff --git a/src/gui/platform/windows/qwindowsthemecache.cpp b/src/gui/platform/windows/qwindowsthemecache.cpp
new file mode 100644
index 0000000000..3bb92e67ca
--- /dev/null
+++ b/src/gui/platform/windows/qwindowsthemecache.cpp
@@ -0,0 +1,79 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwindowsthemecache_p.h"
+#include <QtCore/qdebug.h>
+#include <QtCore/qhash.h>
+
+QT_BEGIN_NAMESPACE
+
+// Theme names matching the QWindowsVistaStylePrivate::Theme enumeration.
+constexpr const wchar_t *themeNames[] = {
+ L"BUTTON", L"COMBOBOX", L"EDIT", L"HEADER", L"LISTVIEW",
+ L"MENU", L"PROGRESS", L"REBAR", L"SCROLLBAR", L"SPIN",
+ L"TAB", L"TASKDIALOG", L"TOOLBAR", L"TOOLTIP", L"TRACKBAR",
+ L"WINDOW", L"STATUS", L"TREEVIEW"
+};
+
+typedef std::array<HTHEME, std::size(themeNames)> ThemeArray;
+typedef QHash<HWND, ThemeArray> ThemesCache;
+Q_GLOBAL_STATIC(ThemesCache, themesCache);
+
+QString QWindowsThemeCache::themeName(int theme)
+{
+ return theme >= 0 && theme < int(std::size(themeNames))
+ ? QString::fromWCharArray(themeNames[theme]) : QString();
+}
+
+HTHEME QWindowsThemeCache::createTheme(int theme, HWND hwnd)
+{
+ if (Q_UNLIKELY(theme < 0 || theme >= int(std::size(themeNames)) || !hwnd)) {
+ qWarning("Invalid parameters #%d, %p", theme, hwnd);
+ return nullptr;
+ }
+
+ // Get or create themes array for this window.
+ ThemesCache *cache = themesCache();
+ auto it = cache->find(hwnd);
+ if (it == cache->end())
+ it = cache->insert(hwnd, ThemeArray {});
+
+ // Get or create theme data
+ ThemeArray &themes = *it;
+ if (!themes[theme]) {
+ const wchar_t *name = themeNames[theme];
+ themes[theme] = OpenThemeData(hwnd, name);
+ if (Q_UNLIKELY(!themes[theme]))
+ qErrnoWarning("OpenThemeData() failed for theme %d (%s).",
+ theme, qPrintable(themeName(theme)));
+ }
+ return themes[theme];
+}
+
+static void clearThemes(ThemeArray &themes)
+{
+ for (auto &theme : themes) {
+ if (theme) {
+ CloseThemeData(theme);
+ theme = nullptr;
+ }
+ }
+}
+
+void QWindowsThemeCache::clearThemeCache(HWND hwnd)
+{
+ ThemesCache *cache = themesCache();
+ auto it = cache->find(hwnd);
+ if (it == cache->end())
+ return;
+ clearThemes(*it);
+}
+
+void QWindowsThemeCache::clearAllThemeCaches()
+{
+ ThemesCache *cache = themesCache();
+ for (auto &themeArray : *cache)
+ clearThemes(themeArray);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/platform/windows/qwindowsthemecache_p.h b/src/gui/platform/windows/qwindowsthemecache_p.h
new file mode 100644
index 0000000000..beb724dc5c
--- /dev/null
+++ b/src/gui/platform/windows/qwindowsthemecache_p.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWINDOWSTHEME_CACHE_P_H
+#define QWINDOWSTHEME_CACHE_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 "QtGui/private/qtguiglobal_p.h"
+
+#include <QtCore/qt_windows.h>
+#include <uxtheme.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QWindowsThemeCache
+{
+ Q_GUI_EXPORT QString themeName(int theme);
+ Q_GUI_EXPORT HTHEME createTheme(int theme, HWND hwnd);
+ Q_GUI_EXPORT void clearThemeCache(HWND hwnd);
+ Q_GUI_EXPORT void clearAllThemeCaches();
+}
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSTHEME_CACHE_P_H
diff --git a/src/gui/qt_cmdline.cmake b/src/gui/qt_cmdline.cmake
index 3663a24a46..379cc417e7 100644
--- a/src/gui/qt_cmdline.cmake
+++ b/src/gui/qt_cmdline.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
qt_commandline_option(accessibility TYPE boolean)
qt_commandline_option(direct2d TYPE boolean)
qt_commandline_option(directfb TYPE boolean)
diff --git a/src/gui/qtgui.tracepoints b/src/gui/qtgui.tracepoints
deleted file mode 100644
index 0be765a4f3..0000000000
--- a/src/gui/qtgui.tracepoints
+++ /dev/null
@@ -1,41 +0,0 @@
-{
-QT_BEGIN_NAMESPACE
-class QImageReader;
-QT_END_NAMESPACE
-}
-
-QGuiApplicationPrivate_init_entry()
-QGuiApplicationPrivate_init_exit()
-
-QGuiApplicationPrivate_processWindowSystemEvent_entry(int type)
-QGuiApplicationPrivate_processWindowSystemEvent_exit()
-
-QFontDatabase_addApplicationFont(const QString &filename)
-QFontDatabase_load(const QStringList &family, int pointSize)
-QFontDatabase_loadEngine(const QStringList &families, int pointSize)
-QFontDatabasePrivate_addAppFont(const QString &fileName)
-
-QImageData_create_entry(const QSize &size, int format)
-QImageData_create_exit()
-QImage_copy_entry(const QRect& r)
-QImage_copy_exit()
-QImage_scaled_entry(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode)
-QImage_scaled_exit()
-QImage_scaledToWidth_entry(int w, Qt::TransformationMode mode)
-QImage_scaledToWidth_exit()
-QImage_scaledToHeight_entry(int h, Qt::TransformationMode mode)
-QImage_scaledToHeight_exit()
-QImage_rgbSwapped_helper_entry()
-QImage_rgbSwapped_helper_exit()
-QImage_transformed_entry(const QTransform &matrix, Qt::TransformationMode mode)
-QImage_transformed_exit()
-
-QPixmap_scaled_entry(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode)
-QPixmap_scaled_exit()
-QPixmap_scaledToWidth_entry(int w, Qt::TransformationMode mode)
-QPixmap_scaledToWidth_exit()
-QPixmap_scaledToHeight_entry(int h, Qt::TransformationMode mode)
-QPixmap_scaledToHeight_exit()
-
-QImageReader_read_before_reading(QImageReader *reader, const QString &filename)
-QImageReader_read_after_reading(QImageReader *reader, bool result)
diff --git a/src/gui/rhi/MiniEngine_LICENSE.txt b/src/gui/rhi/MiniEngine_LICENSE.txt
new file mode 100644
index 0000000000..b8b569d774
--- /dev/null
+++ b/src/gui/rhi/MiniEngine_LICENSE.txt
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Microsoft
+
+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/gui/rhi/cs_mipmap_p.h b/src/gui/rhi/cs_mipmap_p.h
new file mode 100644
index 0000000000..317cbe7b2e
--- /dev/null
+++ b/src/gui/rhi/cs_mipmap_p.h
@@ -0,0 +1,939 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef CS_MIPMAP_P_H
+#define CS_MIPMAP_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qglobal_p.h>
+
+#ifdef Q_OS_WIN
+
+#include <qt_windows.h>
+
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer CB0
+// {
+//
+// uint SrcMipLevel; // Offset: 0 Size: 4
+// uint NumMipLevels; // Offset: 4 Size: 4
+// float2 TexelSize; // Offset: 8 Size: 8
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// BilinearClamp sampler NA NA s0 1
+// SrcMip texture float4 2d t0 1
+// OutMip1 UAV float4 2d u0 1
+// OutMip2 UAV float4 2d u1 1
+// OutMip3 UAV float4 2d u2 1
+// OutMip4 UAV float4 2d u3 1
+// CB0 cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// no Input
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// no Output
+cs_5_0
+dcl_globalFlags refactoringAllowed
+dcl_constantbuffer CB0[1], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_uav_typed_texture2d (float,float,float,float) u0
+dcl_uav_typed_texture2d (float,float,float,float) u1
+dcl_uav_typed_texture2d (float,float,float,float) u2
+dcl_uav_typed_texture2d (float,float,float,float) u3
+dcl_input vThreadIDInGroupFlattened
+dcl_input vThreadID.xy
+dcl_temps 6
+dcl_tgsm_structured g0, 4, 64
+dcl_tgsm_structured g1, 4, 64
+dcl_tgsm_structured g2, 4, 64
+dcl_tgsm_structured g3, 4, 64
+dcl_thread_group 8, 8, 1
+utof r0.xy, vThreadID.xyxx
+add r0.xy, r0.xyxx, l(0.250000, 0.250000, 0.000000, 0.000000)
+mul r0.zw, r0.xxxy, cb0[0].zzzw
+utof r1.x, cb0[0].x
+sample_l_indexable(texture2d)(float,float,float,float) r2.xyzw, r0.zwzz, t0.xyzw, s0, r1.x
+mul r3.xyz, cb0[0].zwzz, l(0.500000, 0.500000, 0.500000, 0.000000)
+mov r3.w, l(0)
+mad r3.xyzw, cb0[0].zwzw, r0.xyxy, r3.zwxy
+sample_l_indexable(texture2d)(float,float,float,float) r4.xyzw, r3.xyxx, t0.xyzw, s0, r1.x
+add r2.xyzw, r2.xyzw, r4.xyzw
+mov r3.x, l(0)
+mul r3.y, cb0[0].w, l(0.500000)
+mad r0.xy, cb0[0].zwzz, r0.xyxx, r3.xyxx
+sample_l_indexable(texture2d)(float,float,float,float) r0.xyzw, r0.xyxx, t0.xyzw, s0, r1.x
+add r0.xyzw, r0.xyzw, r2.xyzw
+sample_l_indexable(texture2d)(float,float,float,float) r1.xyzw, r3.zwzz, t0.xyzw, s0, r1.x
+add r0.xyzw, r0.xyzw, r1.xyzw
+mul r1.xyzw, r0.xyzw, l(0.250000, 0.250000, 0.250000, 0.250000)
+store_uav_typed u0.xyzw, vThreadID.xyyy, r1.xyzw
+ieq r2.x, cb0[0].y, l(1)
+if_nz r2.x
+ ret
+endif
+store_structured g0.x, vThreadIDInGroupFlattened.x, l(0), r1.x
+store_structured g1.x, vThreadIDInGroupFlattened.x, l(0), r1.y
+store_structured g2.x, vThreadIDInGroupFlattened.x, l(0), r1.z
+store_structured g3.x, vThreadIDInGroupFlattened.x, l(0), r1.w
+sync_g_t
+and r2.x, vThreadIDInGroupFlattened.x, l(9)
+if_z r2.x
+ iadd r2.xyz, vThreadIDInGroupFlattened.xxxx, l(1, 8, 9, 0)
+ ld_structured r3.x, r2.x, l(0), g0.xxxx
+ ld_structured r3.y, r2.x, l(0), g1.xxxx
+ ld_structured r3.z, r2.x, l(0), g2.xxxx
+ ld_structured r3.w, r2.x, l(0), g3.xxxx
+ ld_structured r4.x, r2.y, l(0), g0.xxxx
+ ld_structured r4.y, r2.y, l(0), g1.xxxx
+ ld_structured r4.z, r2.y, l(0), g2.xxxx
+ ld_structured r4.w, r2.y, l(0), g3.xxxx
+ ld_structured r5.x, r2.z, l(0), g0.xxxx
+ ld_structured r5.y, r2.z, l(0), g1.xxxx
+ ld_structured r5.z, r2.z, l(0), g2.xxxx
+ ld_structured r5.w, r2.z, l(0), g3.xxxx
+ mad r0.xyzw, r0.xyzw, l(0.250000, 0.250000, 0.250000, 0.250000), r3.xyzw
+ add r0.xyzw, r4.xyzw, r0.xyzw
+ add r0.xyzw, r5.xyzw, r0.xyzw
+ mul r1.xyzw, r0.xyzw, l(0.250000, 0.250000, 0.250000, 0.250000)
+ ushr r0.xyzw, vThreadID.xyyy, l(1, 1, 1, 1)
+ store_uav_typed u1.xyzw, r0.xyzw, r1.xyzw
+ store_structured g0.x, vThreadIDInGroupFlattened.x, l(0), r1.x
+ store_structured g1.x, vThreadIDInGroupFlattened.x, l(0), r1.y
+ store_structured g2.x, vThreadIDInGroupFlattened.x, l(0), r1.z
+ store_structured g3.x, vThreadIDInGroupFlattened.x, l(0), r1.w
+endif
+ieq r0.x, cb0[0].y, l(2)
+if_nz r0.x
+ ret
+endif
+sync_g_t
+and r0.x, vThreadIDInGroupFlattened.x, l(27)
+if_z r0.x
+ iadd r0.xyz, vThreadIDInGroupFlattened.xxxx, l(2, 16, 18, 0)
+ ld_structured r2.x, r0.x, l(0), g0.xxxx
+ ld_structured r2.y, r0.x, l(0), g1.xxxx
+ ld_structured r2.z, r0.x, l(0), g2.xxxx
+ ld_structured r2.w, r0.x, l(0), g3.xxxx
+ ld_structured r3.x, r0.y, l(0), g0.xxxx
+ ld_structured r3.y, r0.y, l(0), g1.xxxx
+ ld_structured r3.z, r0.y, l(0), g2.xxxx
+ ld_structured r3.w, r0.y, l(0), g3.xxxx
+ ld_structured r4.x, r0.z, l(0), g0.xxxx
+ ld_structured r4.y, r0.z, l(0), g1.xxxx
+ ld_structured r4.z, r0.z, l(0), g2.xxxx
+ ld_structured r4.w, r0.z, l(0), g3.xxxx
+ add r0.xyzw, r1.xyzw, r2.xyzw
+ add r0.xyzw, r3.xyzw, r0.xyzw
+ add r0.xyzw, r4.xyzw, r0.xyzw
+ mul r1.xyzw, r0.xyzw, l(0.250000, 0.250000, 0.250000, 0.250000)
+ ushr r0.xyzw, vThreadID.xyyy, l(2, 2, 2, 2)
+ store_uav_typed u2.xyzw, r0.xyzw, r1.xyzw
+ store_structured g0.x, vThreadIDInGroupFlattened.x, l(0), r1.x
+ store_structured g1.x, vThreadIDInGroupFlattened.x, l(0), r1.y
+ store_structured g2.x, vThreadIDInGroupFlattened.x, l(0), r1.z
+ store_structured g3.x, vThreadIDInGroupFlattened.x, l(0), r1.w
+endif
+ieq r0.x, cb0[0].y, l(3)
+if_nz r0.x
+ ret
+endif
+sync_g_t
+if_z vThreadIDInGroupFlattened.x
+ ld_structured r0.x, l(4), l(0), g0.xxxx
+ ld_structured r0.y, l(4), l(0), g1.xxxx
+ ld_structured r0.z, l(4), l(0), g2.xxxx
+ ld_structured r0.w, l(4), l(0), g3.xxxx
+ ld_structured r2.x, l(32), l(0), g0.xxxx
+ ld_structured r2.y, l(32), l(0), g1.xxxx
+ ld_structured r2.z, l(32), l(0), g2.xxxx
+ ld_structured r2.w, l(32), l(0), g3.xxxx
+ ld_structured r3.x, l(36), l(0), g0.xxxx
+ ld_structured r3.y, l(36), l(0), g1.xxxx
+ ld_structured r3.z, l(36), l(0), g2.xxxx
+ ld_structured r3.w, l(36), l(0), g3.xxxx
+ add r0.xyzw, r0.xyzw, r1.xyzw
+ add r0.xyzw, r2.xyzw, r0.xyzw
+ add r0.xyzw, r3.xyzw, r0.xyzw
+ mul r0.xyzw, r0.xyzw, l(0.250000, 0.250000, 0.250000, 0.250000)
+ ushr r1.xyzw, vThreadID.xyyy, l(3, 3, 3, 3)
+ store_uav_typed u3.xyzw, r1.xyzw, r0.xyzw
+endif
+ret
+// Approximately 111 instruction slots used
+#endif
+
+inline constexpr BYTE g_csMipmap[] =
+{
+ 68, 88, 66, 67, 133, 122,
+ 5, 181, 163, 163, 140, 185,
+ 158, 179, 4, 65, 180, 238,
+ 158, 10, 1, 0, 0, 0,
+ 60, 17, 0, 0, 5, 0,
+ 0, 0, 52, 0, 0, 0,
+ 200, 2, 0, 0, 216, 2,
+ 0, 0, 232, 2, 0, 0,
+ 160, 16, 0, 0, 82, 68,
+ 69, 70, 140, 2, 0, 0,
+ 1, 0, 0, 0, 88, 1,
+ 0, 0, 7, 0, 0, 0,
+ 60, 0, 0, 0, 0, 5,
+ 83, 67, 0, 1, 0, 0,
+ 100, 2, 0, 0, 82, 68,
+ 49, 49, 60, 0, 0, 0,
+ 24, 0, 0, 0, 32, 0,
+ 0, 0, 40, 0, 0, 0,
+ 36, 0, 0, 0, 12, 0,
+ 0, 0, 0, 0, 0, 0,
+ 28, 1, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 42, 1, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 49, 1,
+ 0, 0, 4, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 57, 1, 0, 0, 4, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 1, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 65, 1, 0, 0,
+ 4, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 2, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 73, 1,
+ 0, 0, 4, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 81, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 66, 105, 108, 105,
+ 110, 101, 97, 114, 67, 108,
+ 97, 109, 112, 0, 83, 114,
+ 99, 77, 105, 112, 0, 79,
+ 117, 116, 77, 105, 112, 49,
+ 0, 79, 117, 116, 77, 105,
+ 112, 50, 0, 79, 117, 116,
+ 77, 105, 112, 51, 0, 79,
+ 117, 116, 77, 105, 112, 52,
+ 0, 67, 66, 48, 0, 171,
+ 171, 171, 81, 1, 0, 0,
+ 3, 0, 0, 0, 112, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 232, 1, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 252, 1, 0, 0, 0, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 32, 2, 0, 0, 4, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 252, 1,
+ 0, 0, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 255, 255, 255, 255,
+ 0, 0, 0, 0, 45, 2,
+ 0, 0, 8, 0, 0, 0,
+ 8, 0, 0, 0, 2, 0,
+ 0, 0, 64, 2, 0, 0,
+ 0, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 83, 114, 99, 77,
+ 105, 112, 76, 101, 118, 101,
+ 108, 0, 100, 119, 111, 114,
+ 100, 0, 171, 171, 0, 0,
+ 19, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 244, 1, 0, 0, 78, 117,
+ 109, 77, 105, 112, 76, 101,
+ 118, 101, 108, 115, 0, 84,
+ 101, 120, 101, 108, 83, 105,
+ 122, 101, 0, 102, 108, 111,
+ 97, 116, 50, 0, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 55, 2, 0, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 49,
+ 48, 46, 49, 0, 73, 83,
+ 71, 78, 8, 0, 0, 0,
+ 0, 0, 0, 0, 8, 0,
+ 0, 0, 79, 83, 71, 78,
+ 8, 0, 0, 0, 0, 0,
+ 0, 0, 8, 0, 0, 0,
+ 83, 72, 69, 88, 176, 13,
+ 0, 0, 80, 0, 5, 0,
+ 108, 3, 0, 0, 106, 8,
+ 0, 1, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 0, 0, 0, 0,
+ 85, 85, 0, 0, 156, 24,
+ 0, 4, 0, 224, 17, 0,
+ 0, 0, 0, 0, 85, 85,
+ 0, 0, 156, 24, 0, 4,
+ 0, 224, 17, 0, 1, 0,
+ 0, 0, 85, 85, 0, 0,
+ 156, 24, 0, 4, 0, 224,
+ 17, 0, 2, 0, 0, 0,
+ 85, 85, 0, 0, 156, 24,
+ 0, 4, 0, 224, 17, 0,
+ 3, 0, 0, 0, 85, 85,
+ 0, 0, 95, 0, 0, 2,
+ 0, 64, 2, 0, 95, 0,
+ 0, 2, 50, 0, 2, 0,
+ 104, 0, 0, 2, 6, 0,
+ 0, 0, 160, 0, 0, 5,
+ 0, 240, 17, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 64, 0, 0, 0, 160, 0,
+ 0, 5, 0, 240, 17, 0,
+ 1, 0, 0, 0, 4, 0,
+ 0, 0, 64, 0, 0, 0,
+ 160, 0, 0, 5, 0, 240,
+ 17, 0, 2, 0, 0, 0,
+ 4, 0, 0, 0, 64, 0,
+ 0, 0, 160, 0, 0, 5,
+ 0, 240, 17, 0, 3, 0,
+ 0, 0, 4, 0, 0, 0,
+ 64, 0, 0, 0, 155, 0,
+ 0, 4, 8, 0, 0, 0,
+ 8, 0, 0, 0, 1, 0,
+ 0, 0, 86, 0, 0, 4,
+ 50, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 2, 0,
+ 0, 0, 0, 10, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 62, 0, 0,
+ 128, 62, 0, 0, 0, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 194, 0, 16, 0,
+ 0, 0, 0, 0, 6, 4,
+ 16, 0, 0, 0, 0, 0,
+ 166, 142, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 86, 0, 0, 6, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 72, 0, 0, 141, 194, 0,
+ 0, 128, 67, 85, 21, 0,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 230, 10, 16, 0,
+ 0, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 11, 114, 0, 16, 0,
+ 3, 0, 0, 0, 230, 138,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 63,
+ 0, 0, 0, 63, 0, 0,
+ 0, 63, 0, 0, 0, 0,
+ 54, 0, 0, 5, 130, 0,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 230, 142, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 70, 4, 16, 0,
+ 0, 0, 0, 0, 230, 4,
+ 16, 0, 3, 0, 0, 0,
+ 72, 0, 0, 141, 194, 0,
+ 0, 128, 67, 85, 21, 0,
+ 242, 0, 16, 0, 4, 0,
+ 0, 0, 70, 0, 16, 0,
+ 3, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 7, 242, 0, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 4, 0,
+ 0, 0, 54, 0, 0, 5,
+ 18, 0, 16, 0, 3, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 34, 0, 16, 0,
+ 3, 0, 0, 0, 58, 128,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 0, 63,
+ 50, 0, 0, 10, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 3, 0, 0, 0, 72, 0,
+ 0, 141, 194, 0, 0, 128,
+ 67, 85, 21, 0, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 0, 0, 0, 7,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 72, 0, 0, 141, 194, 0,
+ 0, 128, 67, 85, 21, 0,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 230, 10, 16, 0,
+ 3, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 7, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 56, 0, 0, 10,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 62,
+ 0, 0, 128, 62, 0, 0,
+ 128, 62, 0, 0, 128, 62,
+ 164, 0, 0, 6, 242, 224,
+ 17, 0, 0, 0, 0, 0,
+ 70, 5, 2, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 32, 0, 0, 8, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 1, 0,
+ 0, 0, 31, 0, 4, 3,
+ 10, 0, 16, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 21, 0, 0, 1, 168, 0,
+ 0, 8, 18, 240, 17, 0,
+ 0, 0, 0, 0, 10, 64,
+ 2, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 168, 0, 0, 8, 18, 240,
+ 17, 0, 1, 0, 0, 0,
+ 10, 64, 2, 0, 1, 64,
+ 0, 0, 0, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 168, 0, 0, 8,
+ 18, 240, 17, 0, 2, 0,
+ 0, 0, 10, 64, 2, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 42, 0, 16, 0,
+ 1, 0, 0, 0, 168, 0,
+ 0, 8, 18, 240, 17, 0,
+ 3, 0, 0, 0, 10, 64,
+ 2, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 1, 0, 0, 0,
+ 190, 24, 0, 1, 1, 0,
+ 0, 6, 18, 0, 16, 0,
+ 2, 0, 0, 0, 10, 64,
+ 2, 0, 1, 64, 0, 0,
+ 9, 0, 0, 0, 31, 0,
+ 0, 3, 10, 0, 16, 0,
+ 2, 0, 0, 0, 30, 0,
+ 0, 9, 114, 0, 16, 0,
+ 2, 0, 0, 0, 6, 64,
+ 2, 0, 2, 64, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 9, 0, 0, 0,
+ 0, 0, 0, 0, 167, 0,
+ 0, 9, 18, 0, 16, 0,
+ 3, 0, 0, 0, 10, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 0, 0, 0, 0, 167, 0,
+ 0, 9, 34, 0, 16, 0,
+ 3, 0, 0, 0, 10, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 1, 0, 0, 0, 167, 0,
+ 0, 9, 66, 0, 16, 0,
+ 3, 0, 0, 0, 10, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 2, 0, 0, 0, 167, 0,
+ 0, 9, 130, 0, 16, 0,
+ 3, 0, 0, 0, 10, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 3, 0, 0, 0, 167, 0,
+ 0, 9, 18, 0, 16, 0,
+ 4, 0, 0, 0, 26, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 0, 0, 0, 0, 167, 0,
+ 0, 9, 34, 0, 16, 0,
+ 4, 0, 0, 0, 26, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 1, 0, 0, 0, 167, 0,
+ 0, 9, 66, 0, 16, 0,
+ 4, 0, 0, 0, 26, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 2, 0, 0, 0, 167, 0,
+ 0, 9, 130, 0, 16, 0,
+ 4, 0, 0, 0, 26, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 3, 0, 0, 0, 167, 0,
+ 0, 9, 18, 0, 16, 0,
+ 5, 0, 0, 0, 42, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 0, 0, 0, 0, 167, 0,
+ 0, 9, 34, 0, 16, 0,
+ 5, 0, 0, 0, 42, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 1, 0, 0, 0, 167, 0,
+ 0, 9, 66, 0, 16, 0,
+ 5, 0, 0, 0, 42, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 2, 0, 0, 0, 167, 0,
+ 0, 9, 130, 0, 16, 0,
+ 5, 0, 0, 0, 42, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 3, 0, 0, 0, 50, 0,
+ 0, 12, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 62, 0, 0, 128, 62,
+ 0, 0, 128, 62, 0, 0,
+ 128, 62, 70, 14, 16, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 7, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 4, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 5, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 62, 0, 0,
+ 128, 62, 0, 0, 128, 62,
+ 0, 0, 128, 62, 85, 0,
+ 0, 9, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 5,
+ 2, 0, 2, 64, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 164, 0,
+ 0, 7, 242, 224, 17, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 168, 0, 0, 8,
+ 18, 240, 17, 0, 0, 0,
+ 0, 0, 10, 64, 2, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 168, 0,
+ 0, 8, 18, 240, 17, 0,
+ 1, 0, 0, 0, 10, 64,
+ 2, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 168, 0, 0, 8, 18, 240,
+ 17, 0, 2, 0, 0, 0,
+ 10, 64, 2, 0, 1, 64,
+ 0, 0, 0, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 168, 0, 0, 8,
+ 18, 240, 17, 0, 3, 0,
+ 0, 0, 10, 64, 2, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 21, 0,
+ 0, 1, 32, 0, 0, 8,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 26, 128, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 2, 0, 0, 0, 31, 0,
+ 4, 3, 10, 0, 16, 0,
+ 0, 0, 0, 0, 62, 0,
+ 0, 1, 21, 0, 0, 1,
+ 190, 24, 0, 1, 1, 0,
+ 0, 6, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 64,
+ 2, 0, 1, 64, 0, 0,
+ 27, 0, 0, 0, 31, 0,
+ 0, 3, 10, 0, 16, 0,
+ 0, 0, 0, 0, 30, 0,
+ 0, 9, 114, 0, 16, 0,
+ 0, 0, 0, 0, 6, 64,
+ 2, 0, 2, 64, 0, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 0, 18, 0, 0, 0,
+ 0, 0, 0, 0, 167, 0,
+ 0, 9, 18, 0, 16, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 0, 0, 0, 0, 167, 0,
+ 0, 9, 34, 0, 16, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 1, 0, 0, 0, 167, 0,
+ 0, 9, 66, 0, 16, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 2, 0, 0, 0, 167, 0,
+ 0, 9, 130, 0, 16, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 3, 0, 0, 0, 167, 0,
+ 0, 9, 18, 0, 16, 0,
+ 3, 0, 0, 0, 26, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 0, 0, 0, 0, 167, 0,
+ 0, 9, 34, 0, 16, 0,
+ 3, 0, 0, 0, 26, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 1, 0, 0, 0, 167, 0,
+ 0, 9, 66, 0, 16, 0,
+ 3, 0, 0, 0, 26, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 2, 0, 0, 0, 167, 0,
+ 0, 9, 130, 0, 16, 0,
+ 3, 0, 0, 0, 26, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 3, 0, 0, 0, 167, 0,
+ 0, 9, 18, 0, 16, 0,
+ 4, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 0, 0, 0, 0, 167, 0,
+ 0, 9, 34, 0, 16, 0,
+ 4, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 1, 0, 0, 0, 167, 0,
+ 0, 9, 66, 0, 16, 0,
+ 4, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 2, 0, 0, 0, 167, 0,
+ 0, 9, 130, 0, 16, 0,
+ 4, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 7, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 0, 0, 0, 7,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 4, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 62, 0, 0, 128, 62,
+ 0, 0, 128, 62, 0, 0,
+ 128, 62, 85, 0, 0, 9,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 5, 2, 0,
+ 2, 64, 0, 0, 2, 0,
+ 0, 0, 2, 0, 0, 0,
+ 2, 0, 0, 0, 2, 0,
+ 0, 0, 164, 0, 0, 7,
+ 242, 224, 17, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 168, 0, 0, 8, 18, 240,
+ 17, 0, 0, 0, 0, 0,
+ 10, 64, 2, 0, 1, 64,
+ 0, 0, 0, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 168, 0, 0, 8,
+ 18, 240, 17, 0, 1, 0,
+ 0, 0, 10, 64, 2, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 168, 0,
+ 0, 8, 18, 240, 17, 0,
+ 2, 0, 0, 0, 10, 64,
+ 2, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 42, 0,
+ 16, 0, 1, 0, 0, 0,
+ 168, 0, 0, 8, 18, 240,
+ 17, 0, 3, 0, 0, 0,
+ 10, 64, 2, 0, 1, 64,
+ 0, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 1, 0,
+ 0, 0, 21, 0, 0, 1,
+ 32, 0, 0, 8, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 3, 0,
+ 0, 0, 31, 0, 4, 3,
+ 10, 0, 16, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 21, 0, 0, 1, 190, 24,
+ 0, 1, 31, 0, 0, 2,
+ 10, 64, 2, 0, 167, 0,
+ 0, 9, 18, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 4, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 0, 0, 0, 0, 167, 0,
+ 0, 9, 34, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 4, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 1, 0, 0, 0, 167, 0,
+ 0, 9, 66, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 4, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 2, 0, 0, 0, 167, 0,
+ 0, 9, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 4, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 3, 0, 0, 0, 167, 0,
+ 0, 9, 18, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 32, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 0, 0, 0, 0, 167, 0,
+ 0, 9, 34, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 32, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 1, 0, 0, 0, 167, 0,
+ 0, 9, 66, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 32, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 2, 0, 0, 0, 167, 0,
+ 0, 9, 130, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 32, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 3, 0, 0, 0, 167, 0,
+ 0, 9, 18, 0, 16, 0,
+ 3, 0, 0, 0, 1, 64,
+ 0, 0, 36, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 0, 0, 0, 0, 167, 0,
+ 0, 9, 34, 0, 16, 0,
+ 3, 0, 0, 0, 1, 64,
+ 0, 0, 36, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 1, 0, 0, 0, 167, 0,
+ 0, 9, 66, 0, 16, 0,
+ 3, 0, 0, 0, 1, 64,
+ 0, 0, 36, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 2, 0, 0, 0, 167, 0,
+ 0, 9, 130, 0, 16, 0,
+ 3, 0, 0, 0, 1, 64,
+ 0, 0, 36, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 6, 240, 17, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 7, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 0, 0, 0, 7,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 10, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 62, 0, 0, 128, 62,
+ 0, 0, 128, 62, 0, 0,
+ 128, 62, 85, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 5, 2, 0,
+ 2, 64, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 3, 0,
+ 0, 0, 164, 0, 0, 7,
+ 242, 224, 17, 0, 3, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 21, 0, 0, 1, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 148, 0, 0, 0, 111, 0,
+ 0, 0, 6, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 22, 0, 0, 0,
+ 5, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 6, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0
+};
+
+#endif // Q_OS_WIN
+
+#endif // CS_MIPMAP_P_H
diff --git a/src/gui/rhi/cs_tdr_p.h b/src/gui/rhi/cs_tdr_p.h
deleted file mode 100644
index 3e442de39c..0000000000
--- a/src/gui/rhi/cs_tdr_p.h
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef CS_TDR_P_H
-#define CS_TDR_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of other Qt classes. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/private/qglobal_p.h>
-
-#ifdef Q_OS_WIN
-
-#include <qt_windows.h>
-
-#if 0
-//
-// Generated by Microsoft (R) HLSL Shader Compiler 10.1
-//
-//
-// Buffer Definitions:
-//
-// cbuffer ConstantBuffer
-// {
-//
-// uint zero; // Offset: 0 Size: 4
-//
-// }
-//
-//
-// Resource Bindings:
-//
-// Name Type Format Dim HLSL Bind Count
-// ------------------------------ ---------- ------- ----------- -------------- ------
-// uav UAV uint buf u0 1
-// ConstantBuffer cbuffer NA NA cb0 1
-//
-//
-//
-// Input signature:
-//
-// Name Index Mask Register SysValue Format Used
-// -------------------- ----- ------ -------- -------- ------- ------
-// no Input
-//
-// Output signature:
-//
-// Name Index Mask Register SysValue Format Used
-// -------------------- ----- ------ -------- -------- ------- ------
-// no Output
-cs_5_0
-dcl_globalFlags refactoringAllowed
-dcl_constantbuffer CB0[1], immediateIndexed
-dcl_uav_typed_buffer (uint,uint,uint,uint) u0
-dcl_input vThreadID.x
-dcl_thread_group 256, 1, 1
-loop
- breakc_nz cb0[0].x
- store_uav_typed u0.xyzw, vThreadID.xxxx, cb0[0].xxxx
-endloop
-ret
-// Approximately 5 instruction slots used
-#endif
-
-const BYTE g_killDeviceByTimingOut[] =
-{
- 68, 88, 66, 67, 217, 62,
- 220, 38, 136, 51, 86, 245,
- 161, 96, 18, 35, 141, 17,
- 26, 13, 1, 0, 0, 0,
- 164, 2, 0, 0, 5, 0,
- 0, 0, 52, 0, 0, 0,
- 100, 1, 0, 0, 116, 1,
- 0, 0, 132, 1, 0, 0,
- 8, 2, 0, 0, 82, 68,
- 69, 70, 40, 1, 0, 0,
- 1, 0, 0, 0, 144, 0,
- 0, 0, 2, 0, 0, 0,
- 60, 0, 0, 0, 0, 5,
- 83, 67, 0, 1, 0, 0,
- 0, 1, 0, 0, 82, 68,
- 49, 49, 60, 0, 0, 0,
- 24, 0, 0, 0, 32, 0,
- 0, 0, 40, 0, 0, 0,
- 36, 0, 0, 0, 12, 0,
- 0, 0, 0, 0, 0, 0,
- 124, 0, 0, 0, 4, 0,
- 0, 0, 4, 0, 0, 0,
- 1, 0, 0, 0, 255, 255,
- 255, 255, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 128, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 117, 97,
- 118, 0, 67, 111, 110, 115,
- 116, 97, 110, 116, 66, 117,
- 102, 102, 101, 114, 0, 171,
- 128, 0, 0, 0, 1, 0,
- 0, 0, 168, 0, 0, 0,
- 16, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 208, 0, 0, 0, 0, 0,
- 0, 0, 4, 0, 0, 0,
- 2, 0, 0, 0, 220, 0,
- 0, 0, 0, 0, 0, 0,
- 255, 255, 255, 255, 0, 0,
- 0, 0, 255, 255, 255, 255,
- 0, 0, 0, 0, 122, 101,
- 114, 111, 0, 100, 119, 111,
- 114, 100, 0, 171, 0, 0,
- 19, 0, 1, 0, 1, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 213, 0, 0, 0, 77, 105,
- 99, 114, 111, 115, 111, 102,
- 116, 32, 40, 82, 41, 32,
- 72, 76, 83, 76, 32, 83,
- 104, 97, 100, 101, 114, 32,
- 67, 111, 109, 112, 105, 108,
- 101, 114, 32, 49, 48, 46,
- 49, 0, 73, 83, 71, 78,
- 8, 0, 0, 0, 0, 0,
- 0, 0, 8, 0, 0, 0,
- 79, 83, 71, 78, 8, 0,
- 0, 0, 0, 0, 0, 0,
- 8, 0, 0, 0, 83, 72,
- 69, 88, 124, 0, 0, 0,
- 80, 0, 5, 0, 31, 0,
- 0, 0, 106, 8, 0, 1,
- 89, 0, 0, 4, 70, 142,
- 32, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 156, 8,
- 0, 4, 0, 224, 17, 0,
- 0, 0, 0, 0, 68, 68,
- 0, 0, 95, 0, 0, 2,
- 18, 0, 2, 0, 155, 0,
- 0, 4, 0, 1, 0, 0,
- 1, 0, 0, 0, 1, 0,
- 0, 0, 48, 0, 0, 1,
- 3, 0, 4, 4, 10, 128,
- 32, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 164, 0,
- 0, 7, 242, 224, 17, 0,
- 0, 0, 0, 0, 6, 0,
- 2, 0, 6, 128, 32, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 22, 0, 0, 1,
- 62, 0, 0, 1, 83, 84,
- 65, 84, 148, 0, 0, 0,
- 5, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 2, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0
-};
-
-#endif // Q_OS_WIN
-
-#endif // CS_TDR_P_H
diff --git a/src/gui/rhi/mipmap.hlsl b/src/gui/rhi/mipmap.hlsl
new file mode 100644
index 0000000000..ac293e07f9
--- /dev/null
+++ b/src/gui/rhi/mipmap.hlsl
@@ -0,0 +1,117 @@
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+
+RWTexture2D<float4> OutMip1 : register(u0);
+RWTexture2D<float4> OutMip2 : register(u1);
+RWTexture2D<float4> OutMip3 : register(u2);
+RWTexture2D<float4> OutMip4 : register(u3);
+Texture2D<float4> SrcMip : register(t0);
+SamplerState BilinearClamp : register(s0);
+
+cbuffer CB0 : register(b0)
+{
+ uint SrcMipLevel; // Texture level of source mip
+ uint NumMipLevels; // Number of OutMips to write: [1, 4]
+ float2 TexelSize; // 1.0 / OutMip1.Dimensions
+}
+
+// The reason for separating channels is to reduce bank conflicts in the
+// local data memory controller. A large stride will cause more threads
+// to collide on the same memory bank.
+groupshared float gs_R[64];
+groupshared float gs_G[64];
+groupshared float gs_B[64];
+groupshared float gs_A[64];
+
+void StoreColor( uint Index, float4 Color )
+{
+ gs_R[Index] = Color.r;
+ gs_G[Index] = Color.g;
+ gs_B[Index] = Color.b;
+ gs_A[Index] = Color.a;
+}
+
+float4 LoadColor( uint Index )
+{
+ return float4( gs_R[Index], gs_G[Index], gs_B[Index], gs_A[Index]);
+}
+
+[numthreads( 8, 8, 1 )]
+void csMain( uint GI : SV_GroupIndex, uint3 DTid : SV_DispatchThreadID )
+{
+ // Use 4 bilinear samples to guarantee we don't undersample when downsizing by more than 2x
+ // in both directions.
+ float2 UV1 = TexelSize * (DTid.xy + float2(0.25, 0.25));
+ float2 O = TexelSize * 0.5;
+ float4 Src1 = SrcMip.SampleLevel(BilinearClamp, UV1, SrcMipLevel);
+ Src1 += SrcMip.SampleLevel(BilinearClamp, UV1 + float2(O.x, 0.0), SrcMipLevel);
+ Src1 += SrcMip.SampleLevel(BilinearClamp, UV1 + float2(0.0, O.y), SrcMipLevel);
+ Src1 += SrcMip.SampleLevel(BilinearClamp, UV1 + float2(O.x, O.y), SrcMipLevel);
+ Src1 *= 0.25;
+
+ OutMip1[DTid.xy] = Src1;
+
+ // A scalar (constant) branch can exit all threads coherently.
+ if (NumMipLevels == 1)
+ return;
+
+ // Without lane swizzle operations, the only way to share data with other
+ // threads is through LDS.
+ StoreColor(GI, Src1);
+
+ // This guarantees all LDS writes are complete and that all threads have
+ // executed all instructions so far (and therefore have issued their LDS
+ // write instructions.)
+ GroupMemoryBarrierWithGroupSync();
+
+ // With low three bits for X and high three bits for Y, this bit mask
+ // (binary: 001001) checks that X and Y are even.
+ if ((GI & 0x9) == 0)
+ {
+ float4 Src2 = LoadColor(GI + 0x01);
+ float4 Src3 = LoadColor(GI + 0x08);
+ float4 Src4 = LoadColor(GI + 0x09);
+ Src1 = 0.25 * (Src1 + Src2 + Src3 + Src4);
+
+ OutMip2[DTid.xy / 2] = Src1;
+ StoreColor(GI, Src1);
+ }
+
+ if (NumMipLevels == 2)
+ return;
+
+ GroupMemoryBarrierWithGroupSync();
+
+ // This bit mask (binary: 011011) checks that X and Y are multiples of four.
+ if ((GI & 0x1B) == 0)
+ {
+ float4 Src2 = LoadColor(GI + 0x02);
+ float4 Src3 = LoadColor(GI + 0x10);
+ float4 Src4 = LoadColor(GI + 0x12);
+ Src1 = 0.25 * (Src1 + Src2 + Src3 + Src4);
+
+ OutMip3[DTid.xy / 4] = Src1;
+ StoreColor(GI, Src1);
+ }
+
+ if (NumMipLevels == 3)
+ return;
+
+ GroupMemoryBarrierWithGroupSync();
+
+ // This bit mask would be 111111 (X & Y multiples of 8), but only one
+ // thread fits that criteria.
+ if (GI == 0)
+ {
+ float4 Src2 = LoadColor(GI + 0x04);
+ float4 Src3 = LoadColor(GI + 0x20);
+ float4 Src4 = LoadColor(GI + 0x24);
+ Src1 = 0.25 * (Src1 + Src2 + Src3 + Src4);
+
+ OutMip4[DTid.xy / 8] = Src1;
+ }
+}
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index a1abdffd32..a39709c726 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -1,22 +1,23 @@
-// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qrhi_p_p.h"
+#include "qrhi_p.h"
#include <qmath.h>
#include <QLoggingCategory>
-#include "qrhinull_p_p.h"
+#include "qrhinull_p.h"
#ifndef QT_NO_OPENGL
-#include "qrhigles2_p_p.h"
+#include "qrhigles2_p.h"
#endif
#if QT_CONFIG(vulkan)
-#include "qrhivulkan_p_p.h"
+#include "qrhivulkan_p.h"
#endif
#ifdef Q_OS_WIN
-#include "qrhid3d11_p_p.h"
+#include "qrhid3d11_p.h"
+#include "qrhid3d12_p.h"
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
-#include "qrhimetal_p_p.h"
+#if QT_CONFIG(metal)
+#include "qrhimetal_p.h"
#endif
#include <memory>
@@ -27,8 +28,10 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
/*!
\class QRhi
- \internal
- \inmodule QtGui
+ \ingroup painting-3D
+ \inmodule QtGuiPrivate
+ \inheaderfile rhi/qrhi.h
+ \since 6.6
\brief Accelerated 2D/3D graphics API abstraction.
@@ -39,50 +42,55 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
\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
+ \warning The QRhi family of classes in the Qt Gui module, including QShader
+ and QShaderDescription, offer limited compatibility guarantees. There are
+ no source or binary compatibility guarantees for these classes, meaning the
+ API is only guaranteed to work with the Qt version the application was
+ developed against. Source incompatible changes are however aimed to be kept
+ at a minimum and will only be made in minor releases (6.7, 6.8, and so on).
+ To use these classes in an application, link to
+ \c{Qt::GuiPrivate} (if using CMake), and include the headers with the \c
+ rhi prefix, for example \c{#include <rhi/qrhi.h>}.
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).
+ on macOS/iOS, 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 OpenGL 2.1 / OpenGL ES 2.0 or newer. Some extensions and newer core
+ specification features are utilized when present, for example to enable
+ multisample framebuffers or compute shaders. Operating in core profile
+ contexts is supported as well. If necessary, applications can query the
+ \l{QRhi::Feature}{feature flags} at runtime to check for features that are
+ not supported in the OpenGL context backing the QRhi. The OpenGL backend
+ builds on QOpenGLContext, QOpenGLFunctions, and the related cross-platform
+ infrastructure of the Qt GUI module.
+
+ \li Direct3D 11.1 or newer, with Shader Model 5.0 or newer. When the D3D
+ runtime has no support for 11.1 features or Shader Model 5.0,
+ initialization using an accelerated graphics device will fail, but using
+ the
+ \l{https://learn.microsoft.com/en-us/windows/win32/direct3darticles/directx-warp}{software
+ adapter} is still an option.
- \li Direct3D 11.1
+ \li Direct3D 12 on Windows 10 version 1703 and newer, with Shader Model 5.0
+ or newer. Qt requires ID3D12Device2 to be present, hence the requirement
+ for at least version 1703 of Windows 10. The D3D12 device is by default
+ created with specifying a minimum feature level of
+ \c{D3D_FEATURE_LEVEL_11_0}.
- \li Metal
+ \li Metal 1.2 or newer.
- \li Vulkan 1.0, optionally with some extensions that are part of Vulkan 1.1
+ \li Vulkan 1.0 or newer, optionally utilizing some Vulkan 1.1 level
+ features.
- \li Null - A "dummy" backend that issues no graphics calls at all.
+ \li Null, a "dummy" backend that issues no graphics calls at all.
\endlist
@@ -92,15 +100,60 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
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.
+ shaders are not part of QRhi and the Qt GUI module, but the core classes
+ for using such shaders, QShader and QShaderDescription, are. The APIs and
+ tools for performing compilation and translation are part of the Qt Shader
+ Tools module.
+
+ See the \l{RHI Window Example} for an introductory example of creating a
+ portable, cross-platform application that performs accelerated 3D rendering
+ onto a QWindow using QRhi.
+
+ \section1 An Impression of the API
+
+ To provide a quick look at the API with a short yet complete example that
+ does not involve window-related setup, the following is a complete,
+ runnable cross-platform application that renders 20 frames off-screen, and
+ then saves the generated images to files after reading back the texture
+ contents from the GPU. For an example that renders on-screen, which then
+ involves setting up a QWindow and a swapchain, refer to the
+ \l{RHI Window Example}.
- \section2 Design Fundamentals
+ For brevity, the initialization of the QRhi is done based on the platform:
+ the sample code here chooses Direct 3D 12 on Windows, Metal on macOS and
+ iOS, and Vulkan otherwise. OpenGL and Direct 3D 11 are never used by this
+ application, but support for those could be introduced with a few
+ additional lines.
+
+ \snippet rhioffscreen/main.cpp 0
+
+ The result of the application is 20 \c PNG images (frame0.png -
+ frame19.png). These contain a rotating triangle with varying opacity over a
+ green background.
+
+ The vertex and fragment shaders are expected to be processed and packaged
+ into \c{.qsb} files. The Vulkan-compatible GLSL source code is the
+ following:
+
+ \e color.vert
+ \snippet rhioffscreen/color.vert 0
+
+ \e color.frag
+ \snippet rhioffscreen/color.frag 0
+
+ To manually compile and transpile these shaders to a number of targets
+ (SPIR-V, HLSL, MSL, GLSL) and generate the \c{.qsb} files the application
+ loads at run time, run \c{qsb --qt6 color.vert -o color.vert.qsb} and
+ \c{qsb --qt6 color.frag -o color.frag.qsb}. Alternatively, the Qt Shader
+ Tools module offers build system integration for CMake, the
+ \c qt_add_shaders() CMake function, that can achieve the same at build time.
+
+ \section1 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
+ \section2 Resources
Instances of classes deriving from QRhiResource, such as, QRhiBuffer,
QRhiTexture, etc., encapsulate zero, one, or more native graphics
@@ -108,10 +161,10 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
functions of the QRhi, such as, newBuffer(), newTexture(),
newTextureRenderTarget(), newSwapChain().
- \badcode
- vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData));
- if (!vbuf->create()) { error }
- ...
+ \code
+ QRhiBuffer *vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData));
+ if (!vbuf->create()) { error(); }
+ // ...
delete vbuf;
\endcode
@@ -120,9 +173,10 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
\li The returned value from functions like newBuffer() is always 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 create() function of a
- subclass, for example, QRhiBuffer::create() or QRhiTexture::create().
+ \li Just creating an instance of a QRhiResource subclass never allocates or
+ initializes any native resources. That is only done when calling the
+ \c create() function of a subclass, for example, QRhiBuffer::create() or
+ QRhiTexture::create().
\li The exceptions are
QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor(),
@@ -146,15 +200,15 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
\li Note that this does not mean that a QRhiResource can freely be
destroy()'ed or deleted 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::deleteLater() is provided as a convenience.
+ \l{QRhi::beginFrame()}{beginFrame()} - \l{QRhi::endFrame()}{endFrame()}
+ section). As a general rule, all referenced QRhiResource objects must stay
+ unchanged until the frame is submitted by calling
+ \l{QRhi::endFrame()}{endFrame()}. To ease this,
+ QRhiResource::deleteLater() is provided as a convenience.
\endlist
- \section3 Command buffers and deferred command execution
+ \section2 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
@@ -181,7 +235,7 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
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{QRhi::endFrame()}{endFrame()}. On the other hand, calling
\l{QRhiResource::destroy()}{destroy()} or deleting 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
@@ -189,10 +243,20 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
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)create 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
+ the following:
+
+ \list
+ \li (re)create resources
+ \li begin frame
+ \li record/issue uploads and copies
+ \li start recording a render pass
+ \li record draw calls
+ \li end render pass
+ \li end frame
+ \endlist
+
+ 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
@@ -211,7 +275,7 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
remain the primary way of operating since this is what fits Qt's various UI
technologies best.
- \section3 Threading
+ \section2 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
@@ -240,7 +304,7 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
(gui) thread, but becomes important when a separate, dedicated render
thread is used.
- \section3 Resource synchronization
+ \section2 Resource synchronization
QRhi does not expose APIs for resource barriers or image layout
transitions. Such synchronization is done implicitly by the backends, where
@@ -260,7 +324,7 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
different access (one for load, one for store) is supported even within the
same pass.
- \section3 Resource reuse
+ \section2 Resource reuse
From the user's point of view a QRhiResource is reusable immediately after
calling QRhiResource::destroy(). With the exception of swapchains, calling
@@ -280,24 +344,24 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
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);
+ \code
+ QRhiBuffer *ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 256);
ubuf->create();
- srb = rhi->newShaderResourceBindings()
+ QRhiShaderResourceBindings *srb = rhi->newShaderResourceBindings()
srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, ubuf)
});
srb->create();
- ...
+ // ...
// now in a later frame we need to grow the buffer to a larger size
ubuf->setSize(512);
ubuf->create(); // same as ubuf->destroy(); ubuf->create();
- // That's it, srb needs no changes whatsoever, any references in it to
- // ubuf stay valid. When it comes to internal details, such as that
+ // srb needs no changes whatsoever, any references in it to ubuf
+ // stay valid. When it comes to internal details, such as that
// ubuf may now be backed by a completely different native buffer
// resource, that is is recognized and handled automatically by the
// next setShaderResources().
@@ -312,7 +376,7 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
resource underneath, without having to update the QRhiTextureRenderTarget
as that will be done implicitly in beginPass().
- \section3 Pooled objects
+ \section2 Pooled objects
In addition to resources, there are pooled objects as well, such as,
QRhiResourceUpdateBatch. An instance is retrieved via a \c next function,
@@ -322,24 +386,25 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
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::destroy().
+ calling QRhiResourceUpdateBatch::release().
A typical pattern is thus:
- \badcode
+ \code
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();
+ // note the last argument
cb->beginPass(swapchain->currentFrameRenderTarget(), clearCol, clearDs, resUpdates);
\endcode
- \section3 Swapchain specifics
+ \section2 Swapchain specifics
QRhiSwapChain features some special semantics due to the peculiar nature of
swapchains.
@@ -370,30 +435,174 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
\endlist
- \section3 Ownership
+ \section2 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
+ QRhiTexture::nativeTexture(). Most importantly, passing pointers in structs
and via setters does not transfer ownership.
- \section2 Troubleshooting
+ \section1 Troubleshooting and Profiling
+
+ \section2 Error reporting
+
+ Functions such as \l QRhi::create() and the resource classes' \c create()
+ member functions (e.g., \l QRhiBuffer::create()) indicate failure with the
+ return value (\nullptr or
+ \c false, respectively). When working with QShader, \l QShader::fromSerialized()
+ returns an invalid QShader (for which \l{QShader::isValid()}{isValid()} returns
+ \c false) when the data passed to the function cannot be successfully deserialized.
+ Some functions, beginFrame() in particular, may also sometimes report "soft failures",
+ such as \l FrameOpSwapChainOutOfDate, which do not indicate an unrecoverable error,
+ but rather should be seen as a "try again later" response.
+
+ Warnings and errors may get printed at any time to the debug output via
+ qWarning(). It is therefore always advisable to inspect the output of the
+ application.
- Errors are printed to the output via qWarning(). Additional debug messages
- can be enabled via the following logging categories. Messages from these
- categories are not printed by default unless explicitly enabled via
- QLoggingCategory or the \c QT_LOGGING_RULES environment variable.
+ Additional debug messages can be enabled via the following logging
+ categories. Messages from these categories are not printed by default
+ unless explicitly enabled via QLoggingCategory or the \c QT_LOGGING_RULES
+ environment variable. For better interoperation with Qt Quick, the
+ environment variable \c{QSG_INFO} also enables these debug prints.
\list
\li \c{qt.rhi.general}
\endlist
- It is strongly advised to inspect the output with the logging categories
- (\c{qt.rhi.*}) enabled whenever a QRhi-based application is not behaving as
- expected. For better interoperation with Qt Quick, the environment variable
- \c{QSG_INFO} also enables these debug prints.
+ Additionally, applications can query the \l{QRhi::backendName()}{QRhi
+ backend name} and
+ \l{QRhi::driverInfo()}{graphics device information} from a successfully
+ initialized QRhi. This can then be printed to the user or stored in the
+ application logs even in production builds, if desired.
+
+ \section2 Investigating rendering problems
+
+ When the rendering results are not as expected, or the application is
+ experiencing problems, always consider checking with the the native 3D
+ APIs' debug and validation facilities. QRhi itself features limited error
+ checking since replicating the already existing, vast amount of
+ functionality in the underlying layers is not reasonable.
+
+ \list
+
+ \li For Vulkan, controlling the
+ \l{https://github.com/KhronosGroup/Vulkan-ValidationLayers}{Vulkan
+ Validation Layers} is not in the scope of the QRhi, but rather can be
+ achieved by configuring the \l QVulkanInstance with the appropriate layers.
+ For example, call \c{instance.setLayers({ "VK_LAYER_KHRONOS_validation" });}
+ before invoking \l{QVulkanInstance::create()}{create()} on the QVulkanInstance.
+ (note that this assumes that the validation layers are actually installed
+ and available, e.g. from the Vulkan SDK) By default, QVulkanInstance conveniently
+ redirects the Vulkan debug messages to qDebug, meaning the validation messages get
+ printed just like other Qt warnings.
+
+ \li With Direct 3D 11 and 12, a graphics device with the debug layer
+ enabled can be requested by toggling the \c enableDebugLayer flag in the
+ appropriate \l{QRhiD3D11InitParams}{init params struct}. The messages appear on the
+ debug output, which is visible in Qt Creator's messages panel or via a tool
+ such as \l{https://learn.microsoft.com/en-us/sysinternals/downloads/debugview}{DebugView}.
+
+ \li For Metal, controlling Metal Validation is outside of QRhi's scope.
+ Rather, to enable validation, run the application with the environment
+ variable \c{METAL_DEVICE_WRAPPER_TYPE=1} set, or run the application within
+ XCode. There may also be further settings and environment variable in modern
+ XCode and macOS versions. See for instance
+ \l{https://developer.apple.com/documentation/metal/diagnosing_metal_programming_issues_early}{this
+ page}.
+
+ \endlist
+
+ \section2 Frame captures and performance profiling
+
+ A Qt application rendering with QRhi to a window while relying on a 3D API
+ under the hood, is, from the windowing and graphics pipeline perspective at
+ least, no different from any other (non-Qt) applications using the same 3D
+ API. This means that tools and practices for debugging and profiling
+ applications involving 3D graphics, such as games, all apply to such a Qt
+ application as well.
+
+ A few examples of tools that can provide insights into the rendering
+ internals of Qt applications that use QRhi, which includes Qt Quick and Qt
+ Quick 3D based projects as well:
+
+ \list
+
+ \li \l{https://renderdoc.org/}{RenderDoc} allows taking frame captures and
+ introspecting the recorded commands and pipeline state on Windows and Linux
+ for applications using OpenGL, Vulkan, D3D11, or D3D12. When trying to
+ figure out why some parts of the 3D scene do not show up as expected,
+ RenderDoc is often a fast and efficient way to check the pipeline stages
+ and the related state and discover the missing or incorrect value. It is
+ also a tool that is actively used when developing Qt itself.
+
+ \li For NVIDIA-based systems,
+ \l{https://developer.nvidia.com/nsight-graphics}{Nsight Graphics} provides
+ a graphics debugger tool on Windows and Linux. In addition to investigating the commands
+ in the frame and the pipeline, the vendor-specific tools allow looking at timings and
+ hardware performance information, which is not something simple frame captures can provide.
+
+ \li For AMD-based systems, the \l{https://gpuopen.com/rgp/}{Radeon GPU
+ Profiler} can be used to gain deeper insights into the application's
+ rendering and its performance.
+
+ \li As QRhi supports Direct 3D 12, using
+ \l{https://devblogs.microsoft.com/pix/download/}{PIX}, a performance tuning
+ and debugging tool for DirectX 12 games on Windows is an option as well.
+
+ \li On macOS,
+ \l{https://developer.apple.com/documentation/metal/debugging_tools/viewing_your_gpu_workload_with_the_metal_debugger}{the
+ XCode Metal debugger} can be used to take and introspect frame
+ captures, to investigate performance details, and debug shaders. In macOS 13 it is also possible
+ to enable an overlay that displays frame rate and other information for any Metal-based window by
+ setting the environment variable \c{MTL_HUD_ENABLED=1}.
+
+ \endlist
+
+ On mobile and embedded platforms, there may be vendor and platform-specific
+ tools, provided by the GPU or SoC vendor, available to perform performance
+ profiling of application using OpenGL ES or Vulkan.
+
+ When capturing frames, remember that objects and groups of commands can be
+ named via debug markers, as long as \l{QRhi::EnableDebugMarkers}{debug
+ markers were enabled} for the QRhi, and the graphics API in use supports
+ this. To annotate the command stream, call
+ \l{QRhiCommandBuffer::debugMarkBegin()}{debugMarkBegin()},
+ \l{QRhiCommandBuffer::debugMarkEnd()}{debugMarkEnd()} and/or
+ \l{QRhiCommandBuffer::debugMarkMsg()}{debugMarkMsg()}.
+ This can be particularly useful in larger frames with multiple render passes.
+ Resources are named by calling \l{QRhiResource::setName()}{setName()} before create().
+
+ To perform basic timing measurements on the CPU and GPU side within the
+ application, \l QElapsedTimer and
+ \l QRhiCommandBuffer::lastCompletedGpuTime() can be used. The latter is
+ only available with select graphics APIs at the moment and requires opting
+ in via the \l QRhi::EnableTimestamps flag.
+
+ \section2 Resource leak checking
+
+ When destroying a QRhi object without properly destroying all buffers,
+ textures, and other resources created from it, warnings about this are
+ printed to the debug output whenever the application is a debug build, or
+ when the \c QT_RHI_LEAK_CHECK environment variable is set to a non-zero
+ value. This is a simple way to discover design issues around resource
+ handling within the application rendering logic. Note however that some
+ platforms and underlying graphics APIs may perform their own allocation and
+ resource leak detection as well, over which Qt will have no direct control.
+ For example, when using Vulkan, the memory allocator may raise failing
+ assertions in debug builds when resources that own graphics memory
+ allocations are not destroyed before the QRhi. In addition, the Vulkan
+ validation layer, when enabled, will issue warnings about native graphics
+ resources that were not released. Similarly, with Direct 3D warnings may
+ get printed about unreleased COM objects when the application does not
+ destroy the QRhi and its resources in the correct order.
+
+ \sa {RHI Window Example}, QRhiCommandBuffer, QRhiResourceUpdateBatch,
+ QRhiShaderResourceBindings, QShader, QRhiBuffer, QRhiTexture,
+ QRhiRenderBuffer, QRhiSampler, QRhiTextureRenderTarget,
+ QRhiGraphicsPipeline, QRhiComputePipeline, QRhiSwapChain
*/
/*!
@@ -404,6 +613,7 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
\value Vulkan
\value OpenGLES2
\value D3D11
+ \value D3D12
\value Metal
*/
@@ -411,13 +621,18 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
\enum QRhi::Flag
Describes what special features to enable.
- \value EnableProfiling This flag has currently no effect.
-
\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.
+ like QRhiCommandBuffer::debugMarkBegin() will become no-ops. Avoid enabling
+ in production builds as it may involve a small performance impact. Has no
+ effect when the QRhi::DebugMarkers feature is not reported as supported.
+
+ \value EnableTimestamps Enables GPU timestamp collection. When not set,
+ QRhiCommandBuffer::lastCompletedGpuTime() always returns 0. Enable this
+ only when needed since there may be a small amount of extra work involved
+ (e.g. timestamp queries), depending on the underlying graphics API. Has no
+ effect when the QRhi::Timestamps feature is not reported as supported.
\value PreferSoftwareRenderer Indicates that backends should prefer
choosing an adapter or physical device that renders in software on the CPU.
@@ -439,7 +654,7 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
mechanism because the cost of maintaining the related data structures is
not insignificant with some backends. With Vulkan this feature maps
directly to VkPipelineCache, vkGetPipelineCacheData and
- VkPipelineCacheCreateInfo::pInitialData. With D3D11 there is no real
+ VkPipelineCacheCreateInfo::pInitialData. With Direct3D 11 there is no real
pipline cache, but the results of HLSL->DXBC compilations are stored and
can be serialized/deserialized via this mechanism. This allows skipping the
time consuming D3DCompile() in future runs of the applications for shaders
@@ -451,6 +666,17 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
mechanisms for shader/program binaries provided by Qt. Writing to those may
get disabled whenever this flag is set since storing program binaries to
multiple caches is not sensible.
+
+ \value SuppressSmokeTestWarnings Indicates that, with backends where this
+ is relevant, certain, non-fatal QRhi::create() failures should not
+ produce qWarning() calls. For example, with D3D11, passing this flag
+ makes a number of warning messages (that appear due to QRhi::create()
+ failing) to become categorized debug prints instead under the commonly used
+ \c{qt.rhi.general} logging category. This can be used by engines, such as
+ Qt Quick, that feature fallback logic, i.e. they retry calling create()
+ with a different set of flags (such as, \l PreferSoftwareRenderer), in order
+ to hide the unconditional warnings from the output that would be printed
+ when the first create() attempt had failed.
*/
/*!
@@ -488,8 +714,12 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
QRhiCommandBuffer::debugMarkBegin()) are supported.
\value Timestamps Indicates that command buffer timestamps are supported.
- Relevant for addGpuFrameTimeCallback(). Can be expected to be supported on
- D3D11 and Vulkan, assuming the underlying implementation supports it.
+ Relevant for QRhiCommandBuffer::lastCompletedGpuTime(). This can be
+ expected to be supported on Metal, Vulkan, Direct 3D 11 and 12, and OpenGL
+ contexts of version 3.3 or newer. However, with some of these APIs support
+ for timestamp queries is technically optional, and therefore it cannot be
+ guaranteed that this feature is always supported with every implementation
+ of them.
\value Instancing Indicates that instanced drawing is supported. In
practice this feature will be unsupported with OpenGL ES 2.0 and OpenGL
@@ -559,7 +789,7 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
\value WideLines Indicates that lines with a width other than 1 are
supported. When reported as not supported, the line width set on the
graphics pipeline state is ignored. This can always be false with some
- backends (D3D11, Metal). With Vulkan, the value depends on the
+ backends (D3D11, D3D12, Metal). With Vulkan, the value depends on the
implementation. With OpenGL, wide lines are not supported in core profile
contexts.
@@ -587,7 +817,7 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
\value TriangleFanTopology Indicates that QRhiGraphicsPipeline::setTopology()
supports QRhiGraphicsPipeline::TriangleFan. In practice this feature will be
- unsupported with Metal and Direct 3D 11.
+ unsupported with Metal and Direct 3D 11/12.
\value ReadBackNonUniformBuffer Indicates that
\l{QRhiResourceUpdateBatch::readBackBuffer()}{reading buffer contents} is
@@ -677,23 +907,28 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
can be set via
\l{QRhiGraphicsPipeline::setPatchControlPointCount()}{setPatchControlPointCount()},
and shaders for tessellation control and evaluation can be specified in the
- QRhiShaderStage list. \b{Tessellation is considered an experimental feature
- in QRhi and can only be expected to be supported with Vulkan, OpenGL and
- OpenGL ES for the time being}, assuming the implementation reports it as
- supported at run time. Tessellation shaders have portability issues between
+ QRhiShaderStage list. Tessellation shaders have portability issues between
APIs (for example, translating GLSL/SPIR-V to HLSL is problematic due to
the way hull shaders are structured, whereas Metal uses a somewhat
- different tessellation pipeline than others), and therefore no guarantees
- can be given for a universal solution for now.
+ different tessellation pipeline than others), and therefore unexpected
+ issues may still arise, even though basic functionality is implemented
+ across all the underlying APIs. For Direct 3D in particular, handwritten
+ HLSL hull and domain shaders must be injected into each QShader for the
+ tessellation control and evaluation stages, respectively, since qsb cannot
+ generate these from SPIR-V. Note that isoline tessellation should be
+ avoided as it will not be supported by all backends. The maximum patch
+ control point count portable between backends is 32.
\value GeometryShader Indicates that the geometry shader stage is
supported. When supported, a geometry shader can be specified in the
- QRhiShaderStage list. \b{Geometry Shaders are considered an experimental
+ QRhiShaderStage list. Geometry Shaders are considered an experimental
feature in QRhi and can only be expected to be supported with Vulkan,
- OpenGL (3.2+) and OpenGL ES (3.2+) for the time being}, assuming the
- implementation reports it as supported at run time. Geometry shaders have
- portability issues between APIs, and therefore no guarantees can be given
- for a universal solution for now.
+ Direct 3D, OpenGL (3.2+) and OpenGL ES (3.2+), assuming the implementation
+ reports it as supported at run time. Geometry shaders have portability
+ issues between APIs, and therefore no guarantees can be given for a
+ universal solution. They will never be supported with Metal. Whereas with
+ Direct 3D a handwritten HLSL geometry shader must be injected into each
+ QShader for the geometry stage since qsb cannot generate this from SPIR-V.
\value TextureArrayRange Indicates that for
\l{QRhi::newTextureArray()}{texture arrays} it is possible to specify a
@@ -715,13 +950,92 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
with image load/store. This feature is only available with some backends as
it does not map well to all graphics APIs, and it is only meant to provide
support for special cases anyhow. In practice the feature can be expected to
- be supported with Direct3D 11 and Vulkan.
+ be supported with Direct3D 11/12 and Vulkan.
\value NonFillPolygonMode Indicates that setting a PolygonMode other than
the default Fill is supported for QRhiGraphicsPipeline. A common use case
for changing the mode to Line is to get wireframe rendering. This however
is not available as a core OpenGL ES feature, and is optional with Vulkan
as well as some mobile GPUs may not offer the feature.
+
+ \value OneDimensionalTextures Indicates that 1D textures are supported.
+ In practice this feature will be unsupported on OpenGL ES.
+
+ \value OneDimensionalTextureMipmaps Indicates that generating 1D texture
+ mipmaps are supported. In practice this feature will be unsupported on
+ backends that do not report support for
+ \l{OneDimensionalTextures}, Metal, and Direct 3D 12.
+
+ \value HalfAttributes Indicates that specifying input attributes with half
+ precision (16bit) floating point types for a shader pipeline is supported.
+ When not supported, build() will succeed but just show a warning message
+ and the values of the target attributes will be broken. In practice this
+ feature will be unsupported in some OpenGL ES 2.0 and OpenGL 2.x
+ implementations. Note that while Direct3D 11/12 does support half precision
+ input attributes, it does not support the half3 type. The D3D backends pass
+ half3 attributes as half4. To ensure cross platform compatibility, half3
+ inputs should be padded to 8 bytes.
+
+ \value RenderToOneDimensionalTexture Indicates that 1D texture render
+ targets are supported. In practice this feature will be unsupported on
+ backends that do not report support for
+ \l{OneDimensionalTextures}, and Metal.
+
+ \value ThreeDimensionalTextureMipmaps Indicates that generating 3D texture
+ mipmaps are supported. In practice this feature will be unsupported with
+ Direct 3D 12.
+
+ \value MultiView Indicates that multiview, see e.g.
+ \l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_multiview.html}{VK_KHR_multiview}
+ is supported. With OpenGL ES 2.0, Direct 3D 11, and OpenGL (ES)
+ implementations without \c{GL_OVR_multiview2} this feature will not be
+ supported. With Vulkan 1.1 and newer, and Direct 3D 12 multiview is
+ typically supported. When reported as supported, creating a
+ QRhiTextureRenderTarget with a QRhiColorAttachment that references a texture
+ array and has \l{QRhiColorAttachment::setMultiViewCount()}{multiViewCount}
+ set enables recording a render pass that uses multiview rendering. In addition,
+ any QRhiGraphicsPipeline used in that render pass must have
+ \l{QRhiGraphicsPipeline::setMultiViewCount()}{the same view count set}. Note that
+ multiview is only available in combination with 2D texture arrays. It cannot
+ be used to optimize the rendering into individual textures (e.g. two, for
+ the left and right eyes). Rather, the target of a multiview render pass is
+ always a texture array, automatically rendering to the layer (array element)
+ corresponding to each view. Therefore this feature implies \l TextureArrays
+ as well. Multiview rendering is not supported in combination with
+ tessellation or geometry shaders. See QRhiColorAttachment::setMultiViewCount()
+ for further details on multiview rendering. This enum value has been introduced in Qt 6.7.
+
+ \value TextureViewFormat Indicates that setting a
+ \l{QRhiTexture::setWriteViewFormat()}{view format} on a QRhiTexture is
+ effective. When reported as supported, setting the read (sampling) or write
+ (render target / image load-store) view mode changes the texture's viewing
+ format. When unsupported, setting a view format has no effect. Note that Qt
+ has no knowledge or control over format compatibility or resource view rules
+ in the underlying 3D API and its implementation. Passing in unsuitable,
+ incompatible formats may lead to errors and unspecified behavior. This is
+ provided mainly to allow "casting" rendering into a texture created with an
+ sRGB format to non-sRGB to avoid the unwanted linear->sRGB conversion on
+ shader writes. Other types of casting may or may not be functional,
+ depending on the underlying API. Currently implemented for Vulkan and Direct
+ 3D 12. With D3D12 the feature is available only if
+ \c CastingFullyTypedFormatSupported is supported, see
+ \l{https://microsoft.github.io/DirectX-Specs/d3d/RelaxedCasting.html} (and
+ note that QRhi always uses fully typed formats for textures.) This enum
+ value has been introduced in Qt 6.8.
+
+ \value ResolveDepthStencil Indicates that resolving a multisample depth or
+ depth-stencil texture is supported. Otherwise,
+ \l{QRhiTextureRenderTargetDescription::setDepthResolveTexture()}{setting a
+ depth resolve texture} is not functional and must be avoided. Direct 3D 11
+ and 12 have no support for resolving depth/depth-stencil formats, and
+ therefore this feature will never be supported with those. Vulkan 1.0 has no
+ API to request resolving a depth-stencil attachment. Therefore, with Vulkan
+ this feature will only be supported with Vulkan 1.2 and up, and on 1.1
+ implementations with the appropriate extensions present. This feature is
+ provided for the rare case when resolving into a non-multisample depth
+ texture becomes necessary, for example when rendering into an
+ OpenXR-provided depth texture (XR_KHR_composition_layer_depth). This enum
+ value has been introduced in Qt 6.8.
*/
/*!
@@ -833,22 +1147,28 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
/*!
\class QRhiInitParams
- \internal
\inmodule QtGui
+ \since 6.6
\brief Base class for backend-specific initialization parameters.
Contains fields that are relevant to all backends.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
\class QRhiDepthStencilClearValue
- \internal
\inmodule QtGui
+ \since 6.6
\brief Specifies clear values for a depth or stencil buffer.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
- \fn QRhiDepthStencilClearValue::QRhiDepthStencilClearValue()
+ \fn QRhiDepthStencilClearValue::QRhiDepthStencilClearValue() = default
Constructs a depth/stencil clear value with depth clear value 1.0f and
stencil clear value 0.
@@ -865,37 +1185,45 @@ QRhiDepthStencilClearValue::QRhiDepthStencilClearValue(float d, quint32 s)
}
/*!
+ \fn float QRhiDepthStencilClearValue::depthClearValue() const
+ \return the depth clear value. In most cases this is 1.0f.
+ */
+
+/*!
+ \fn void QRhiDepthStencilClearValue::setDepthClearValue(float d)
+ Sets the depth clear value to \a d.
+ */
+
+/*!
+ \fn quint32 QRhiDepthStencilClearValue::stencilClearValue() const
+ \return the stencil clear value. In most cases this is 0.
+ */
+
+/*!
+ \fn void QRhiDepthStencilClearValue::setStencilClearValue(quint32 s)
+ Sets the stencil clear value to \a s.
+ */
+
+/*!
+ \fn bool QRhiDepthStencilClearValue::operator==(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) noexcept
+
\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) noexcept
-{
- return a.depthClearValue() == b.depthClearValue()
- && a.stencilClearValue() == b.stencilClearValue();
-}
/*!
+ \fn bool QRhiDepthStencilClearValue::operator!=(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) noexcept
+
\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) noexcept
-{
- return !(a == b);
-}
/*!
- \return the hash value for \a v, using \a seed to seed the calculation.
+ \fn size_t QRhiDepthStencilClearValue::qHash(const QRhiDepthStencilClearValue &v, size_t seed = 0) noexcept
- \relates QRhiDepthStencilClearValue
+ \return the hash value for \a v, using \a seed to seed the calculation.
*/
-size_t qHash(const QRhiDepthStencilClearValue &v, size_t seed) noexcept
-{
- return seed * (uint(qFloor(qreal(v.depthClearValue()) * 100)) + v.stencilClearValue());
-}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QRhiDepthStencilClearValue &v)
@@ -910,8 +1238,8 @@ QDebug operator<<(QDebug dbg, const QRhiDepthStencilClearValue &v)
/*!
\class QRhiViewport
- \internal
\inmodule QtGui
+ \since 6.6
\brief Specifies a viewport rectangle.
Used with QRhiCommandBuffer::setViewport().
@@ -921,20 +1249,23 @@ QDebug operator<<(QDebug dbg, const QRhiDepthStencilClearValue &v)
Typical usage is like the following:
- \badcode
+ \code
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->beginPass(swapchain->currentFrameRenderTarget(), Qt::black, { 1.0f, 0 });
cb->setGraphicsPipeline(ps);
cb->setViewport(viewport);
- ...
+ // ...
\endcode
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
\sa QRhiCommandBuffer::setViewport(), QRhi::clipSpaceCorrMatrix(), QRhiScissor
*/
/*!
- \fn QRhiViewport::QRhiViewport()
+ \fn QRhiViewport::QRhiViewport() = default
Constructs a viewport description with an empty rectangle and a depth range
of 0.0f - 1.0f.
@@ -960,40 +1291,59 @@ QRhiViewport::QRhiViewport(float x, float y, float w, float h, float minDepth, f
}
/*!
+ \fn std::array<float, 4> QRhiViewport::viewport() const
+ \return the viewport x, y, width, and height.
+ */
+
+/*!
+ \fn void QRhiViewport::setViewport(float x, float y, float w, float h)
+ Sets the viewport's position and size to \a x, \a y, \a w, and \a h.
+
+ \note Viewports are specified in a coordinate system that has its origin in
+ the bottom-left.
+ */
+
+/*!
+ \fn float QRhiViewport::minDepth() const
+ \return the minDepth value of the depth range of the viewport.
+ */
+
+/*!
+ \fn void QRhiViewport::setMinDepth(float minDepth)
+ Sets the \a minDepth of the depth range of the viewport.
+ By default this is set to 0.0f.
+ */
+
+/*!
+ \fn float QRhiViewport::maxDepth() const
+ \return the maxDepth value of the depth range of the viewport.
+ */
+
+/*!
+ \fn void QRhiViewport::setMaxDepth(float maxDepth)
+ Sets the \a maxDepth of the depth range of the viewport.
+ By default this is set to 1.0f.
+ */
+
+/*!
+ \fn bool QRhiViewport::operator==(const QRhiViewport &a, const QRhiViewport &b) noexcept
+
\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) noexcept
-{
- return a.viewport() == b.viewport()
- && a.minDepth() == b.minDepth()
- && a.maxDepth() == b.maxDepth();
-}
/*!
+ \fn bool QRhiViewport::operator!=(const QRhiViewport &a, const QRhiViewport &b) noexcept
+
\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) noexcept
-{
- return !(a == b);
-}
/*!
- \return the hash value for \a v, using \a seed to seed the calculation.
+ \fn size_t QRhiViewport::qHash(const QRhiViewport &v, size_t seed = 0) noexcept
- \relates QRhiViewport
+ \return the hash value for \a v, using \a seed to seed the calculation.
*/
-size_t qHash(const QRhiViewport &v, size_t seed) noexcept
-{
- const std::array<float, 4> r = v.viewport();
- return seed + uint(r[0]) + uint(r[1]) + uint(r[2]) + uint(r[3])
- + uint(qFloor(qreal(v.minDepth()) * 100)) + uint(qFloor(qreal(v.maxDepth()) * 100));
-}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QRhiViewport &v)
@@ -1013,8 +1363,8 @@ QDebug operator<<(QDebug dbg, const QRhiViewport &v)
/*!
\class QRhiScissor
- \internal
\inmodule QtGui
+ \since 6.6
\brief Specifies a scissor rectangle.
Used with QRhiCommandBuffer::setScissor(). Setting a scissor rectangle is
@@ -1028,11 +1378,14 @@ QDebug operator<<(QDebug dbg, const QRhiViewport &v)
appropriate. Therefore, any rendering logic targeting OpenGL can feed
scissor rectangles into QRhiScissor as-is, without any adaptation.
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
\sa QRhiCommandBuffer::setScissor(), QRhiViewport
*/
/*!
- \fn QRhiScissor::QRhiScissor()
+ \fn QRhiScissor::QRhiScissor() = default
Constructs an empty scissor.
*/
@@ -1053,37 +1406,37 @@ QRhiScissor::QRhiScissor(int x, int y, int w, int h)
}
/*!
+ \fn std::array<int, 4> QRhiScissor::scissor() const
+ \return the scissor position and size.
+ */
+
+/*!
+ \fn void QRhiScissor::setScissor(int x, int y, int w, int h)
+ Sets the scissor position and size to \a x, \a y, \a w, \a h.
+
+ \note The position is always expected to be specified in a coordinate
+ system that has its origin in the bottom-left corner, like OpenGL.
+ */
+
+/*!
+ \fn bool QRhiScissor::operator==(const QRhiScissor &a, const QRhiScissor &b) noexcept
+
\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) noexcept
-{
- return a.scissor() == b.scissor();
-}
/*!
+ \fn bool QRhiScissor::operator!=(const QRhiScissor &a, const QRhiScissor &b) noexcept
+
\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) noexcept
-{
- return !(a == b);
-}
/*!
- \return the hash value for \a v, using \a seed to seed the calculation.
+ \fn size_t QRhiScissor::qHash(const QRhiScissor &v, size_t seed = 0) noexcept
- \relates QRhiScissor
+ \return the hash value for \a v, using \a seed to seed the calculation.
*/
-size_t qHash(const QRhiScissor &v, size_t seed) noexcept
-{
- const std::array<int, 4> r = v.scissor();
- return seed + uint(r[0]) + uint(r[1]) + uint(r[2]) + uint(r[3]);
-}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QRhiScissor &s)
@@ -1101,8 +1454,8 @@ QDebug operator<<(QDebug dbg, const QRhiScissor &s)
/*!
\class QRhiVertexInputBinding
- \internal
\inmodule QtGui
+ \since 6.6
\brief Describes a vertex input binding.
Specifies the stride (in bytes, must be a multiple of 4), the
@@ -1120,7 +1473,7 @@ QDebug operator<<(QDebug dbg, const QRhiScissor &s)
format in a buffer (or separate buffers even). Defining two bindings
could then be done like this:
- \badcode
+ \code
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 3 * sizeof(float) },
@@ -1137,7 +1490,7 @@ QDebug operator<<(QDebug dbg, const QRhiScissor &s)
vertices, assuming we have a single buffer with first the positions and
then the texture coordinates:
- \badcode
+ \code
const QRhiCommandBuffer::VertexInput vbufBindings[] = {
{ cubeBuf, 0 },
{ cubeBuf, 36 * 3 * sizeof(float) }
@@ -1153,6 +1506,9 @@ QDebug operator<<(QDebug dbg, const QRhiScissor &s)
\note the stride must always be a multiple of 4.
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
\sa QRhiCommandBuffer::setVertexInput()
*/
@@ -1165,7 +1521,7 @@ QDebug operator<<(QDebug dbg, const QRhiScissor &s)
*/
/*!
- \fn QRhiVertexInputBinding::QRhiVertexInputBinding()
+ \fn QRhiVertexInputBinding::QRhiVertexInputBinding() = default
Constructs a default vertex input binding description.
*/
@@ -1177,7 +1533,7 @@ QDebug operator<<(QDebug dbg, const QRhiScissor &s)
\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)
+QRhiVertexInputBinding::QRhiVertexInputBinding(quint32 stride, Classification cls, quint32 stepRate)
: m_stride(stride),
m_classification(cls),
m_instanceStepRate(stepRate)
@@ -1185,38 +1541,54 @@ QRhiVertexInputBinding::QRhiVertexInputBinding(quint32 stride, Classification cl
}
/*!
+ \fn quint32 QRhiVertexInputBinding::stride() const
+ \return the stride in bytes.
+ */
+
+/*!
+ \fn void QRhiVertexInputBinding::setStride(quint32 s)
+ Sets the stride to \a s.
+ */
+
+/*!
+ \fn QRhiVertexInputBinding::Classification QRhiVertexInputBinding::classification() const
+ \return the input data classification.
+ */
+
+/*!
+ \fn void QRhiVertexInputBinding::setClassification(Classification c)
+ Sets the input data classification \a c. By default this is set to PerVertex.
+ */
+
+/*!
+ \fn quint32 QRhiVertexInputBinding::instanceStepRate() const
+ \return the instance step rate.
+ */
+
+/*!
+ \fn void QRhiVertexInputBinding::setInstanceStepRate(quint32 rate)
+ Sets the instance step \a rate. By default this is set to 1.
+ */
+
+/*!
+ \fn bool QRhiVertexInputBinding::operator==(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) noexcept
+
\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) noexcept
-{
- return a.stride() == b.stride()
- && a.classification() == b.classification()
- && a.instanceStepRate() == b.instanceStepRate();
-}
/*!
+ \fn bool QRhiVertexInputBinding::operator!=(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) noexcept
+
\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) noexcept
-{
- return !(a == b);
-}
/*!
- \return the hash value for \a v, using \a seed to seed the calculation.
+ \fn size_t QRhiVertexInputBinding::qHash(const QRhiVertexInputBinding &v, size_t seed = 0) noexcept
- \relates QRhiVertexInputBinding
+ \return the hash value for \a v, using \a seed to seed the calculation.
*/
-size_t qHash(const QRhiVertexInputBinding &v, size_t seed) noexcept
-{
- return seed + v.stride() + v.classification();
-}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QRhiVertexInputBinding &b)
@@ -1232,14 +1604,15 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputBinding &b)
/*!
\class QRhiVertexInputAttribute
- \internal
\inmodule QtGui
+ \since 6.6
\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
+ \note For HLSL it is assumed that the vertex shader translated from SPIR-V
+ uses
\c{TEXCOORD<location>} as the semantic for each input. Hence no separate
semantic name and index.
@@ -1255,7 +1628,7 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputBinding &b)
non-interleaved format in a buffer (or separate buffers even). Once two
bindings are defined, the attributes could be specified as:
- \badcode
+ \code
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 3 * sizeof(float) },
@@ -1272,7 +1645,7 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputBinding &b)
vertices, assuming we have a single buffer with first the positions and
then the texture coordinates:
- \badcode
+ \code
const QRhiCommandBuffer::VertexInput vbufBindings[] = {
{ cubeBuf, 0 },
{ cubeBuf, 36 * 3 * sizeof(float) }
@@ -1284,7 +1657,7 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputBinding &b)
binding, with multiple attributes referring to that same buffer binding
point:
- \badcode
+ \code
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 5 * sizeof(float) }
@@ -1297,11 +1670,14 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputBinding &b)
and then:
- \badcode
+ \code
const QRhiCommandBuffer::VertexInput vbufBinding(interleavedCubeBuf, 0);
cb->setVertexInput(0, 1, &vbufBinding);
\endcode
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
\sa QRhiCommandBuffer::setVertexInput()
*/
@@ -1324,10 +1700,30 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputBinding &b)
\value SInt3 Three component signed integer vector
\value SInt2 Two component signed integer vector
\value SInt Signed integer
+ \value Half4 Four component half precision (16 bit) float vector
+ \value Half3 Three component half precision (16 bit) float vector
+ \value Half2 Two component half precision (16 bit) float vector
+ \value Half Half precision (16 bit) float
+ \value UShort4 Four component unsigned short (16 bit) integer vector
+ \value UShort3 Three component unsigned short (16 bit) integer vector
+ \value UShort2 Two component unsigned short (16 bit) integer vector
+ \value UShort Unsigned short (16 bit) integer
+ \value SShort4 Four component signed short (16 bit) integer vector
+ \value SShort3 Three component signed short (16 bit) integer vector
+ \value SShort2 Two component signed short (16 bit) integer vector
+ \value SShort Signed short (16 bit) integer
+
+ \note Support for half precision floating point attributes is indicated at
+ run time by the QRhi::Feature::HalfAttributes feature flag.
+
+ \note Direct3D 11/12 supports 16 bit input attributes, but does not support
+ the Half3, UShort3 or SShort3 types. The D3D backends pass through Half3 as
+ Half4, UShort3 as UShort4, and SShort3 as SShort4. To ensure cross platform
+ compatibility, 16 bit inputs should be padded to 8 bytes.
*/
/*!
- \fn QRhiVertexInputAttribute::QRhiVertexInputAttribute()
+ \fn QRhiVertexInputAttribute::QRhiVertexInputAttribute() = default
Constructs a default vertex input attribute description.
*/
@@ -1352,39 +1748,85 @@ QRhiVertexInputAttribute::QRhiVertexInputAttribute(int binding, int location, Fo
}
/*!
+ \fn int QRhiVertexInputAttribute::binding() const
+ \return the binding point index.
+ */
+
+/*!
+ \fn void QRhiVertexInputAttribute::setBinding(int b)
+ Sets the binding point index to \a b.
+ By default this is set to 0.
+ */
+
+/*!
+ \fn int QRhiVertexInputAttribute::location() const
+ \return the location of the vertex input element.
+ */
+
+/*!
+ \fn void QRhiVertexInputAttribute::setLocation(int loc)
+ Sets the location of the vertex input element to \a loc.
+ By default this is set to 0.
+ */
+
+/*!
+ \fn QRhiVertexInputAttribute::Format QRhiVertexInputAttribute::format() const
+ \return the format of the vertex input element.
+ */
+
+/*!
+ \fn void QRhiVertexInputAttribute::setFormat(Format f)
+ Sets the format of the vertex input element to \a f.
+ By default this is set to Float4.
+ */
+
+/*!
+ \fn quint32 QRhiVertexInputAttribute::offset() const
+ \return the byte offset for the input element.
+ */
+
+/*!
+ \fn void QRhiVertexInputAttribute::setOffset(quint32 ofs)
+ Sets the byte offset for the input element to \a ofs.
+ */
+
+/*!
+ \fn int QRhiVertexInputAttribute::matrixSlice() const
+
+ \return the matrix slice if the input element corresponds to a row or
+ column of a matrix, or -1 if not relevant.
+ */
+
+/*!
+ \fn void QRhiVertexInputAttribute::setMatrixSlice(int slice)
+
+ Sets the matrix \a slice. By default this is set to -1, and should be set
+ to a >= 0 value only when this attribute corresponds to a row or column of
+ a matrix (for example, a 4x4 matrix becomes 4 vec4s, consuming 4
+ consecutive vertex input locations), in which case it is the index of the
+ row or column. \c{location - matrixSlice} must always be equal to the \c
+ location for the first row or column of the unrolled matrix.
+ */
+
+/*!
+ \fn bool QRhiVertexInputAttribute::operator==(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) noexcept
+
\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) noexcept
-{
- return a.binding() == b.binding()
- && a.location() == b.location()
- && a.format() == b.format()
- && a.offset() == b.offset();
-}
/*!
+ \fn bool QRhiVertexInputAttribute::operator!=(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) noexcept
+
\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) noexcept
-{
- return !(a == b);
-}
/*!
- \return the hash value for \a v, using \a seed to seed the calculation.
+ \fn size_t QRhiVertexInputAttribute::qHash(const QRhiVertexInputAttribute &v, size_t seed = 0) noexcept
- \relates QRhiVertexInputAttribute
+ \return the hash value for \a v, using \a seed to seed the calculation.
*/
-size_t qHash(const QRhiVertexInputAttribute &v, size_t seed) noexcept
-{
- return seed + uint(v.binding()) + uint(v.location()) + uint(v.format()) + v.offset();
-}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QRhiVertexInputAttribute &a)
@@ -1399,53 +1841,235 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputAttribute &a)
}
#endif
+QRhiVertexInputAttribute::Format QRhiImplementation::shaderDescVariableFormatToVertexInputFormat(QShaderDescription::VariableType type) const
+{
+ switch (type) {
+ case QShaderDescription::Vec4:
+ return QRhiVertexInputAttribute::Float4;
+ case QShaderDescription::Vec3:
+ return QRhiVertexInputAttribute::Float3;
+ case QShaderDescription::Vec2:
+ return QRhiVertexInputAttribute::Float2;
+ case QShaderDescription::Float:
+ return QRhiVertexInputAttribute::Float;
+
+ case QShaderDescription::Int4:
+ return QRhiVertexInputAttribute::SInt4;
+ case QShaderDescription::Int3:
+ return QRhiVertexInputAttribute::SInt3;
+ case QShaderDescription::Int2:
+ return QRhiVertexInputAttribute::SInt2;
+ case QShaderDescription::Int:
+ return QRhiVertexInputAttribute::SInt;
+
+ case QShaderDescription::Uint4:
+ return QRhiVertexInputAttribute::UInt4;
+ case QShaderDescription::Uint3:
+ return QRhiVertexInputAttribute::UInt3;
+ case QShaderDescription::Uint2:
+ return QRhiVertexInputAttribute::UInt2;
+ case QShaderDescription::Uint:
+ return QRhiVertexInputAttribute::UInt;
+
+ case QShaderDescription::Half4:
+ return QRhiVertexInputAttribute::Half4;
+ case QShaderDescription::Half3:
+ return QRhiVertexInputAttribute::Half3;
+ case QShaderDescription::Half2:
+ return QRhiVertexInputAttribute::Half2;
+ case QShaderDescription::Half:
+ return QRhiVertexInputAttribute::Half;
+
+ default:
+ Q_UNREACHABLE_RETURN(QRhiVertexInputAttribute::Float);
+ }
+}
+
+quint32 QRhiImplementation::byteSizePerVertexForVertexInputFormat(QRhiVertexInputAttribute::Format format) const
+{
+ switch (format) {
+ case QRhiVertexInputAttribute::Float4:
+ return 4 * sizeof(float);
+ case QRhiVertexInputAttribute::Float3:
+ return 4 * sizeof(float); // vec3 still takes 16 bytes
+ case QRhiVertexInputAttribute::Float2:
+ return 2 * sizeof(float);
+ case QRhiVertexInputAttribute::Float:
+ return sizeof(float);
+
+ case QRhiVertexInputAttribute::UNormByte4:
+ return 4 * sizeof(quint8);
+ case QRhiVertexInputAttribute::UNormByte2:
+ return 2 * sizeof(quint8);
+ case QRhiVertexInputAttribute::UNormByte:
+ return sizeof(quint8);
+
+ case QRhiVertexInputAttribute::UInt4:
+ return 4 * sizeof(quint32);
+ case QRhiVertexInputAttribute::UInt3:
+ return 4 * sizeof(quint32); // ivec3 still takes 16 bytes
+ case QRhiVertexInputAttribute::UInt2:
+ return 2 * sizeof(quint32);
+ case QRhiVertexInputAttribute::UInt:
+ return sizeof(quint32);
+
+ case QRhiVertexInputAttribute::SInt4:
+ return 4 * sizeof(qint32);
+ case QRhiVertexInputAttribute::SInt3:
+ return 4 * sizeof(qint32); // uvec3 still takes 16 bytes
+ case QRhiVertexInputAttribute::SInt2:
+ return 2 * sizeof(qint32);
+ case QRhiVertexInputAttribute::SInt:
+ return sizeof(qint32);
+
+ case QRhiVertexInputAttribute::Half4:
+ return 4 * sizeof(qfloat16);
+ case QRhiVertexInputAttribute::Half3:
+ return 4 * sizeof(qfloat16); // half3 still takes 8 bytes
+ case QRhiVertexInputAttribute::Half2:
+ return 2 * sizeof(qfloat16);
+ case QRhiVertexInputAttribute::Half:
+ return sizeof(qfloat16);
+
+ case QRhiVertexInputAttribute::UShort4:
+ return 4 * sizeof(quint16);
+ case QRhiVertexInputAttribute::UShort3:
+ return 4 * sizeof(quint16); // ivec3 still takes 8 bytes
+ case QRhiVertexInputAttribute::UShort2:
+ return 2 * sizeof(quint16);
+ case QRhiVertexInputAttribute::UShort:
+ return sizeof(quint16);
+
+ case QRhiVertexInputAttribute::SShort4:
+ return 4 * sizeof(qint16);
+ case QRhiVertexInputAttribute::SShort3:
+ return 4 * sizeof(qint16); // uvec3 still takes 8 bytes
+ case QRhiVertexInputAttribute::SShort2:
+ return 2 * sizeof(qint16);
+ case QRhiVertexInputAttribute::SShort:
+ return sizeof(qint16);
+
+ default:
+ Q_UNREACHABLE_RETURN(1);
+ }
+}
+
/*!
\class QRhiVertexInputLayout
- \internal
\inmodule QtGui
+ \since 6.6
\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.
+
+ As an example, let's assume that we have a single buffer with 3 component
+ vertex positions and 2 component UV coordinates interleaved (\c x, \c y, \c
+ z, \c u, \c v), that the position and UV are expected at input locations 0
+ and 1 by the vertex shader, and that the vertex buffer will be bound at
+ binding point 0 using
+ \l{QRhiCommandBuffer::setVertexInput()}{setVertexInput()} later on:
+
+ \code
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 5 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float2, 3 * sizeof(float) }
+ });
+ \endcode
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
- \fn QRhiVertexInputLayout::QRhiVertexInputLayout()
+ \fn QRhiVertexInputLayout::QRhiVertexInputLayout() = default
Constructs an empty vertex input layout description.
*/
/*!
+ \fn void QRhiVertexInputLayout::setBindings(std::initializer_list<QRhiVertexInputBinding> list)
+ Sets the bindings from the specified \a list.
+ */
+
+/*!
+ \fn template<typename InputIterator> void QRhiVertexInputLayout::setBindings(InputIterator first, InputIterator last)
+ Sets the bindings using the iterators \a first and \a last.
+ */
+
+/*!
+ \fn const QRhiVertexInputBinding *QRhiVertexInputLayout::cbeginBindings() const
+ \return a const iterator pointing to the first item in the binding list.
+ */
+
+/*!
+ \fn const QRhiVertexInputBinding *QRhiVertexInputLayout::cendBindings() const
+ \return a const iterator pointing just after the last item in the binding list.
+ */
+
+/*!
+ \fn const QRhiVertexInputBinding *QRhiVertexInputLayout::bindingAt(qsizetype index) const
+ \return the binding at the given \a index.
+ */
+
+/*!
+ \fn qsizetype QRhiVertexInputLayout::bindingCount() const
+ \return the number of bindings.
+ */
+
+/*!
+ \fn void QRhiVertexInputLayout::setAttributes(std::initializer_list<QRhiVertexInputAttribute> list)
+ Sets the attributes from the specified \a list.
+ */
+
+/*!
+ \fn template<typename InputIterator> void QRhiVertexInputLayout::setAttributes(InputIterator first, InputIterator last)
+ Sets the attributes using the iterators \a first and \a last.
+ */
+
+/*!
+ \fn const QRhiVertexInputAttribute *QRhiVertexInputLayout::cbeginAttributes() const
+ \return a const iterator pointing to the first item in the attribute list.
+ */
+
+/*!
+ \fn const QRhiVertexInputAttribute *QRhiVertexInputLayout::cendAttributes() const
+ \return a const iterator pointing just after the last item in the attribute list.
+ */
+
+/*!
+ \fn const QRhiVertexInputAttribute *QRhiVertexInputLayout::attributeAt(qsizetype index) const
+ \return the attribute at the given \a index.
+ */
+
+/*!
+ \fn qsizetype QRhiVertexInputLayout::attributeCount() const
+ \return the number of attributes.
+ */
+
+/*!
+ \fn bool QRhiVertexInputLayout::operator==(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) noexcept
+
\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) noexcept
-{
- return a.m_bindings == b.m_bindings && a.m_attributes == b.m_attributes;
-}
/*!
+ \fn bool QRhiVertexInputLayout::operator!=(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) noexcept
+
\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) noexcept
-{
- return !(a == b);
-}
/*!
- \return the hash value for \a v, using \a seed to seed the calculation.
+ \fn size_t QRhiVertexInputLayout::qHash(const QRhiVertexInputLayout &v, size_t seed = 0) noexcept
- \relates QRhiVertexInputLayout
+ \return the hash value for \a v, using \a seed to seed the calculation.
*/
-size_t qHash(const QRhiVertexInputLayout &v, size_t seed) noexcept
-{
- return qHash(v.m_bindings, seed) + qHash(v.m_attributes, seed);
-}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QRhiVertexInputLayout &v)
@@ -1460,9 +2084,39 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputLayout &v)
/*!
\class QRhiShaderStage
- \internal
\inmodule QtGui
+ \since 6.6
\brief Specifies the type and the shader code for a shader stage in the pipeline.
+
+ When setting up a QRhiGraphicsPipeline, a collection of shader stages are
+ specified. The QRhiShaderStage contains a QShader and some associated
+ metadata, such as the graphics pipeline stage, and the
+ \l{QShader::Variant}{shader variant} to select. There is no need to specify
+ the shader language or version because the QRhi backend in use at runtime
+ will take care of choosing the appropriate shader version from the
+ collection within the QShader.
+
+ The typical usage is in combination with
+ QRhiGraphicsPipeline::setShaderStages(), shown here with a simple approach
+ to load the QShader from \c{.qsb} files generated offline or at build time:
+
+ \code
+ QShader getShader(const QString &name)
+ {
+ QFile f(name);
+ return f.open(QIODevice::ReadOnly) ? QShader::fromSerialized(f.readAll()) : QShader();
+ }
+
+ QShader vs = getShader("material.vert.qsb");
+ QShader fs = getShader("material.frag.qsb");
+ pipeline->setShaderStages({
+ { QRhiShaderStage::Vertex, vs },
+ { QRhiShaderStage::Fragment, fs }
+ });
+ \endcode
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
@@ -1471,10 +2125,10 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputLayout &v)
\value Vertex Vertex stage
- \value TessellationControlStage Tessellation control (hull shader) stage.
- Must be used only when the QRhi::Tessellation feature is supported.
+ \value TessellationControl Tessellation control (hull shader) stage. Must
+ be used only when the QRhi::Tessellation feature is supported.
- \value TessellationEvaluationStage Tessellation evaluation (domain shader)
+ \value TessellationEvaluation Tessellation evaluation (domain shader)
stage. Must be used only when the QRhi::Tessellation feature is supported.
\value Fragment Fragment (pixel shader) stage
@@ -1487,13 +2141,46 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputLayout &v)
*/
/*!
- \fn QRhiShaderStage::QRhiShaderStage()
+ \fn QRhiShaderStage::QRhiShaderStage() = default
Constructs a shader stage description for the vertex stage with an empty
QShader.
*/
/*!
+ \fn QRhiShaderStage::Type QRhiShaderStage::type() const
+ \return the type of the stage.
+ */
+
+/*!
+ \fn void QRhiShaderStage::setType(Type t)
+
+ Sets the type of the stage to \a t. Setters should rarely be needed in
+ pratice. Most applications will likely use the QRhiShaderStage constructor
+ in most cases.
+ */
+
+/*!
+ \fn QShader QRhiShaderStage::shader() const
+ \return the QShader to be used for this stage in the graphics pipeline.
+ */
+
+/*!
+ \fn void QRhiShaderStage::setShader(const QShader &s)
+ Sets the shader collection \a s.
+ */
+
+/*!
+ \fn QShader::Variant QRhiShaderStage::shaderVariant() const
+ \return the requested shader variant.
+ */
+
+/*!
+ \fn void QRhiShaderStage::setShaderVariant(QShader::Variant v)
+ Sets the requested shader variant \a v.
+ */
+
+/*!
Constructs a shader stage description with the \a type of the stage and the
\a shader.
@@ -1510,38 +2197,24 @@ QRhiShaderStage::QRhiShaderStage(Type type, const QShader &shader, QShader::Vari
}
/*!
+ \fn bool QRhiShaderStage::operator==(const QRhiShaderStage &a, const QRhiShaderStage &b) noexcept
+
\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) noexcept
-{
- return a.type() == b.type()
- && a.shader() == b.shader()
- && a.shaderVariant() == b.shaderVariant();
-}
/*!
+ \fn bool QRhiShaderStage::operator!=(const QRhiShaderStage &a, const QRhiShaderStage &b) noexcept
+
\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) noexcept
-{
- return !(a == b);
-}
/*!
- \return the hash value for \a v, using \a seed to seed the calculation.
+ \fn size_t QRhiShaderStage::qHash(const QRhiShaderStage &v, size_t seed = 0) noexcept
- \relates QRhiShaderStage
+ \return the hash value for \a v, using \a seed to seed the calculation.
*/
-size_t qHash(const QRhiShaderStage &v, size_t seed) noexcept
-{
- return v.type() + qHash(v.shader(), seed) + v.shaderVariant();
-}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QRhiShaderStage &s)
@@ -1557,12 +2230,14 @@ QDebug operator<<(QDebug dbg, const QRhiShaderStage &s)
/*!
\class QRhiColorAttachment
- \internal
\inmodule QtGui
+ \since 6.6
\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.
+ former, i.e. when texture() is set, is used in most cases.
+ QRhiColorAttachment is commonly used in combination with
+ QRhiTextureRenderTargetDescription.
\note texture() and renderBuffer() cannot be both set (be non-null at the
same time).
@@ -1589,10 +2264,15 @@ QDebug operator<<(QDebug dbg, const QRhiShaderStage &s)
\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.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
+ \sa QRhiTextureRenderTargetDescription
*/
/*!
- \fn QRhiColorAttachment::QRhiColorAttachment()
+ \fn QRhiColorAttachment::QRhiColorAttachment() = default
Constructs an empty color attachment description.
*/
@@ -1616,9 +2296,194 @@ QRhiColorAttachment::QRhiColorAttachment(QRhiRenderBuffer *renderBuffer)
}
/*!
+ \fn QRhiTexture *QRhiColorAttachment::texture() const
+
+ \return the texture this attachment description references, or \nullptr if
+ there is none.
+ */
+
+/*!
+ \fn void QRhiColorAttachment::setTexture(QRhiTexture *tex)
+
+ Sets the texture \a tex.
+
+ \note texture() and renderBuffer() cannot be both set (be non-null at the
+ same time).
+ */
+
+/*!
+ \fn QRhiRenderBuffer *QRhiColorAttachment::renderBuffer() const
+
+ \return the renderbuffer this attachment description references, or
+ \nullptr if there is none.
+
+ In practice associating a QRhiRenderBuffer with a QRhiColorAttachment makes
+ the most sense when setting up multisample rendering via a multisample
+ \l{QRhiRenderBuffer::Type}{color} renderbuffer that is then resolved into a
+ non-multisample texture at the end of the render pass.
+ */
+
+/*!
+ \fn void QRhiColorAttachment::setRenderBuffer(QRhiRenderBuffer *rb)
+
+ Sets the renderbuffer \a rb.
+
+ \note texture() and renderBuffer() cannot be both set (be non-null at the
+ same time).
+ */
+
+/*!
+ \fn int QRhiColorAttachment::layer() const
+ \return the layer index (cubemap face or array layer). 0 by default.
+ */
+
+/*!
+ \fn void QRhiColorAttachment::setLayer(int layer)
+ Sets the \a layer index.
+ */
+
+/*!
+ \fn int QRhiColorAttachment::level() const
+ \return the mip level. 0 by default.
+ */
+
+/*!
+ \fn void QRhiColorAttachment::setLevel(int level)
+ Sets the mip \a level.
+ */
+
+/*!
+ \fn QRhiTexture *QRhiColorAttachment::resolveTexture() const
+
+ \return the resolve texture this attachment description references, or
+ \nullptr if there is none.
+
+ Setting a non-null resolve texture is applicable when the attachment
+ references a multisample texture or renderbuffer. The QRhiTexture in the
+ resolveTexture() is then a non-multisample 2D texture (or texture array)
+ with the same size (but a sample count of 1). The multisample content is
+ automatically resolved into this texture at the end of each render pass.
+ */
+
+/*!
+ \fn void QRhiColorAttachment::setResolveTexture(QRhiTexture *tex)
+
+ Sets the resolve texture \a tex.
+
+ \a tex is expected to be a 2D texture or a 2D texture array. In either
+ case, resolving targets a single mip level of a single layer (array
+ element) of \a tex. The mip level and array layer are specified by
+ resolveLevel() and resolveLayer().
+
+ An exception is \l{setMultiViewCount()}{multiview}: when the color
+ attachment is associated with a texture array and multiview is enabled, the
+ resolve texture must also be a texture array with sufficient elements for
+ all views. In this case all elements that correspond to views are resolved
+ automatically; the behavior is similar to the following pseudo-code:
+ \badcode
+ for (i = 0; i < multiViewCount(); ++i)
+ resolve texture's layer() + i into resolveTexture's resolveLayer() + i
+ \endcode
+
+ Setting a non-multisample texture to resolve a multisample texture or
+ renderbuffer automatically at the end of the render pass is often
+ preferable to working with multisample textures (and not setting a resolve
+ texture), because it avoids the need for writing dedicated fragment shaders
+ that work exclusively with multisample textures (\c sampler2DMS, \c
+ texelFetch, etc.), and rather allows using the same shader as one would if
+ the attachment's texture was not multisampled to begin with. This comes at
+ the expense of an additional resource (the non-multisample \a tex).
+ */
+
+/*!
+ \fn int QRhiColorAttachment::resolveLayer() const
+ \return the currently set resolve texture layer. Defaults to 0.
+ */
+
+/*!
+ \fn void QRhiColorAttachment::setResolveLayer(int layer)
+ Sets the resolve texture \a layer to use.
+ */
+
+/*!
+ \fn int QRhiColorAttachment::resolveLevel() const
+ \return the currently set resolve texture mip level. Defaults to 0.
+ */
+
+/*!
+ \fn void QRhiColorAttachment::setResolveLevel(int level)
+ Sets the resolve texture mip \a level to use.
+ */
+
+/*!
+ \fn int QRhiColorAttachment::multiViewCount() const
+
+ \return the currently set number of views. Defaults to 0 which indicates
+ the render target with this color attachment is not going to be used with
+ multiview rendering.
+
+ \since 6.7
+ */
+
+/*!
+ \fn void QRhiColorAttachment::setMultiViewCount(int count)
+
+ Sets the view \a count. Setting a value larger than 1 indicates that the
+ render target with this color attachment is going to be used with multiview
+ rendering. The default value is 0. Values smaller than 2 indicate no
+ multiview rendering.
+
+ When \a count is set to \c 2 or greater, the color attachment must be
+ associated with a 2D texture array. layer() and multiViewCount() together
+ define the range of texture array elements that are targeted during
+ multiview rendering.
+
+ For example, if \c layer is \c 0 and \c multiViewCount is \c 2, the texture
+ array must have 2 (or more) elements, and the multiview rendering will
+ target elements 0 and 1. The \c{gl_ViewIndex} variable in the shaders has a
+ value of \c 0 or \c 1 then, where view \c 0 corresponds to the texture array
+ element \c 0, and view \c 1 to the array element \c 1.
+
+ \note Setting a \a count larger than 1, using a texture array as texture(),
+ and calling \l{QRhiCommandBuffer::beginPass()}{beginPass()} on a
+ QRhiTextureRenderTarget with this color attachment implies multiview
+ rendering for the entire render pass. multiViewCount() should not be set
+ unless multiview rendering is wanted. Multiview cannot be used with texture
+ types other than 2D texture arrays. (although 3D textures may work,
+ depending on the graphics API and backend; applications are nonetheless
+ advised not to rely on that and only use 2D texture arrays as the render
+ targets of multiview rendering)
+
+ See
+ \l{https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview.txt}{GL_OVR_multiview}
+ for more details regarding multiview rendering. Do note that Qt requires
+ \l{https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview2.txt}{GL_OVR_multiview2}
+ as well, when running on OpenGL (ES).
+
+ Multiview rendering is available only when the
+ \l{QRhi::MultiView}{MultiView} feature is reported as supported from
+ \l{QRhi::isFeatureSupported()}{isFeatureSupported()}.
+
+ \note For portability, be aware of limitations that exist for multiview
+ rendering with some of the graphics APIs. It is recommended that multiview
+ render passes do not rely on any of the features that
+ \l{https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview.txt}{GL_OVR_multiview}
+ declares as unsupported. The one exception is shader stage outputs other
+ than \c{gl_Position} depending on \c{gl_ViewIndex}: that can be relied on
+ (even with OpenGL) because QRhi never reports multiview as supported without
+ \c{GL_OVR_multiview2} also being present.
+
+ \note Multiview rendering is not supported in combination with tessellation
+ or geometry shaders, even though some implementations of some graphics APIs
+ may allow this.
+
+ \since 6.7
+ */
+
+/*!
\class QRhiTextureRenderTargetDescription
- \internal
\inmodule QtGui
+ \since 6.6
\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,
@@ -1627,10 +2492,97 @@ QRhiColorAttachment::QRhiColorAttachment(QRhiRenderBuffer *renderBuffer)
\note depthStencilBuffer() and depthTexture() cannot be both set (cannot be
non-null at the same time).
+
+ Let's look at some example usages in combination with
+ QRhiTextureRenderTarget.
+
+ Due to the constructors, the targeting a texture (and no depth/stencil
+ buffer) is simple:
+
+ \code
+ QRhiTexture *texture = rhi->newTexture(QRhiTexture::RGBA8, QSize(256, 256), 1, QRhiTexture::RenderTarget);
+ texture->create();
+ QRhiTextureRenderTarget *rt = rhi->newTextureRenderTarget({ texture }));
+ \endcode
+
+ The following creates a texture render target that is set up to target mip
+ level #2 of a texture:
+
+ \code
+ QRhiTexture *texture = rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 1, QRhiTexture::RenderTarget | QRhiTexture::MipMapped);
+ texture->create();
+ QRhiColorAttachment colorAtt(texture);
+ colorAtt.setLevel(2);
+ QRhiTextureRenderTarget *rt = rhi->newTextureRenderTarget({ colorAtt });
+ \endcode
+
+ Another example, this time to render into a depth texture:
+
+ \code
+ QRhiTexture *shadowMap = rhi->newTexture(QRhiTexture::D32F, QSize(1024, 1024), 1, QRhiTexture::RenderTarget);
+ shadowMap->create();
+ QRhiTextureRenderTargetDescription rtDesc;
+ rtDesc.setDepthTexture(shadowMap);
+ QRhiTextureRenderTarget *rt = rhi->newTextureRenderTarget(rtDesc);
+ \endcode
+
+ A very common case, having a texture as the color attachment and a
+ renderbuffer as depth/stencil to enable depth testing:
+
+ \code
+ QRhiTexture *texture = rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 1. QRhiTexture::RenderTarget);
+ texture->create();
+ QRhiRenderBuffer *depthStencil = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, QSize(512, 512));
+ depthStencil->create();
+ QRhiTextureRenderTargetDescription rtDesc({ texture }, depthStencil);
+ QRhiTextureRenderTarget *rt = rhi->newTextureRenderTarget(rtDesc);
+ \endcode
+
+ Finally, to enable multisample rendering in a portable manner (so also
+ supporting OpenGL ES 3.0), using a QRhiRenderBuffer as the (multisample)
+ color buffer and then resolving into a regular (non-multisample) 2D
+ texture. To enable depth testing, a depth-stencil buffer, which also must
+ use the same sample count, is used as well:
+
+ \code
+ QRhiRenderBuffer *colorBuffer = rhi->newRenderBuffer(QRhiRenderBuffer::Color, QSize(512, 512), 4); // 4x MSAA
+ colorBuffer->create();
+ QRhiRenderBuffer *depthStencil = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, QSize(512, 512), 4);
+ depthStencil->create();
+ QRhiTexture *texture = rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 1, QRhiTexture::RenderTarget);
+ texture->create();
+ QRhiColorAttachment colorAtt(colorBuffer);
+ colorAtt.setResolveTexture(texture);
+ QRhiTextureRenderTarget *rt = rhi->newTextureRenderTarget({ colorAtt, depthStencil });
+ \endcode
+
+ \note when multisample resolving is enabled, the multisample data may not be
+ written out at all. This means that the multisample texture in a color
+ attachment must not be used afterwards with shaders for sampling (or other
+ purposes) whenever a resolve texture is set, since the multisample color
+ buffer is merely an intermediate storage then that gets no data written back
+ on some GPU architectures at all. See
+ \l{QRhiTextureRenderTarget::Flag}{PreserveColorContents} for more details.
+
+ \note When using setDepthTexture(), not setDepthStencilBuffer(), and the
+ depth (stencil) data is not of interest afterwards, set the
+ DoNotStoreDepthStencilContents flag on the QRhiTextureRenderTarget. This
+ allows indicating to the underlying 3D API that the depth/stencil data can
+ be discarded, leading potentially to better performance with tiled GPU
+ architectures. When the depth-stencil buffer is a QRhiRenderBuffer (and also
+ for the multisample color texture, see previous note) this is implicit, but
+ with a depth (stencil) QRhiTexture the intention needs to be declared
+ explicitly. By default QRhi assumes that the data is of interest (e.g., the
+ depth texture is sampled in a shader afterwards).
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
+ \sa QRhiColorAttachment, QRhiTextureRenderTarget
*/
/*!
- \fn QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription()
+ \fn QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription() = default
Constructs an empty texture render target description.
*/
@@ -1672,9 +2624,125 @@ QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRh
}
/*!
+ \fn void QRhiTextureRenderTargetDescription::setColorAttachments(std::initializer_list<QRhiColorAttachment> list)
+ Sets the \a list of color attachments.
+ */
+
+/*!
+ \fn template<typename InputIterator> void QRhiTextureRenderTargetDescription::setColorAttachments(InputIterator first, InputIterator last)
+ Sets the list of color attachments via the iterators \a first and \a last.
+ */
+
+/*!
+ \fn const QRhiColorAttachment *QRhiTextureRenderTargetDescription::cbeginColorAttachments() const
+ \return a const iterator pointing to the first item in the attachment list.
+ */
+
+/*!
+ \fn const QRhiColorAttachment *QRhiTextureRenderTargetDescription::cendColorAttachments() const
+ \return a const iterator pointing just after the last item in the attachment list.
+ */
+
+/*!
+ \fn const QRhiColorAttachment *QRhiTextureRenderTargetDescription::colorAttachmentAt(qsizetype index) const
+ \return the color attachment at the specified \a index.
+ */
+
+/*!
+ \fn qsizetype QRhiTextureRenderTargetDescription::colorAttachmentCount() const
+ \return the number of currently set color attachments.
+ */
+
+/*!
+ \fn QRhiRenderBuffer *QRhiTextureRenderTargetDescription::depthStencilBuffer() const
+ \return the renderbuffer used as depth-stencil buffer, or \nullptr if none was set.
+ */
+
+/*!
+ \fn void QRhiTextureRenderTargetDescription::setDepthStencilBuffer(QRhiRenderBuffer *renderBuffer)
+
+ Sets the \a renderBuffer for depth-stencil. Not mandatory, e.g. when no
+ depth test/write or stencil-related features are used within any graphics
+ pipelines in any of the render passes for this render target, it can be
+ left set to \nullptr.
+
+ \note depthStencilBuffer() and depthTexture() cannot be both set (cannot be
+ non-null at the same time).
+
+ Using a QRhiRenderBuffer over a 2D QRhiTexture as the depth or
+ depth/stencil buffer is very common, and is the recommended approach for
+ applications. Using a QRhiTexture, and so setDepthTexture() becomes
+ relevant if the depth data is meant to be accessed (e.g. sampled in a
+ shader) afterwards, or when
+ \l{QRhiColorAttachment::setMultiViewCount()}{multiview rendering} is
+ involved (because then the depth texture must be a texture array).
+
+ \sa setDepthTexture()
+ */
+
+/*!
+ \fn QRhiTexture *QRhiTextureRenderTargetDescription::depthTexture() const
+ \return the currently referenced depth texture, or \nullptr if none was set.
+ */
+
+/*!
+ \fn void QRhiTextureRenderTargetDescription::setDepthTexture(QRhiTexture *texture)
+
+ Sets the \a texture for depth-stencil. This is an alternative to
+ setDepthStencilBuffer(), where instead of a QRhiRenderBuffer a QRhiTexture
+ with a suitable type (e.g., QRhiTexture::D32F) is provided.
+
+ \note depthStencilBuffer() and depthTexture() cannot be both set (cannot be
+ non-null at the same time).
+
+ \a texture can either be a 2D texture or a 2D texture array (when texture
+ arrays are supported). Specifying a texture array is relevant in particular
+ with
+ \l{QRhiColorAttachment::setMultiViewCount()}{multiview rendering}.
+
+ \note If \a texture is a format with a stencil component, such as
+ \l QRhiTexture::D24S8, it will serve as the stencil buffer as well.
+
+ \sa setDepthStencilBuffer()
+ */
+
+/*!
+ \fn QRhiTexture *QRhiTextureRenderTargetDescription::depthResolveTexture() const
+
+ \return the texture to which a multisample depth (or depth-stencil) texture
+ (or texture array) is resolved to. \nullptr if there is none, which is the
+ most common case.
+
+ \since 6.8
+ \sa QRhiColorAttachment::resolveTexture(), depthTexture()
+ */
+
+/*!
+ \fn void QRhiTextureRenderTargetDescription::setDepthResolveTexture(QRhiTexture *tex)
+
+ Sets the depth (or depth-stencil) resolve texture \a tex.
+
+ \a tex is expected to be a 2D texture or a 2D texture array with a format
+ matching the texture set via setDepthTexture().
+
+ \note Resolving depth (or depth-stencil) data is only functional when the
+ \l ResolveDepthStencil feature is reported as supported at run time. Support
+ for depth-stencil resolve is not universally available among the graphics
+ APIs. Designs assuming unconditional availability of depth-stencil resolve
+ are therefore non-portable, and should be avoided.
+
+ \note As an additional limitation for OpenGL ES in particular, setting a
+ depth resolve texture may only be functional in combination with
+ setDepthTexture(), not with setDepthStencilBuffer().
+
+ \since 6.8
+ \sa QRhiColorAttachment::setResolveTexture(), setDepthTexture()
+ */
+
+/*!
\class QRhiTextureSubresourceUploadDescription
- \internal
\inmodule QtGui
+ \since 6.6
\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
@@ -1731,10 +2799,15 @@ QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRh
in order to be portable across all backends) If this cannot be ensured, the
caller is strongly encouraged to call QImage::detach() on the image before
passing it to uploadTexture().
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
+ \sa QRhiTextureUploadDescription
*/
/*!
- \fn QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription()
+ \fn QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription() = default
Constructs an empty subresource description.
@@ -1767,7 +2840,7 @@ QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription
\a data can safely be destroyed or changed once this function returns.
*/
-QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription(const void *data, int size)
+QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription(const void *data, quint32 size)
: m_data(reinterpret_cast<const char *>(data), size)
{
}
@@ -1782,12 +2855,109 @@ QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription
}
/*!
+ \fn QImage QRhiTextureSubresourceUploadDescription::image() const
+ \return the currently set QImage.
+ */
+
+/*!
+ \fn void QRhiTextureSubresourceUploadDescription::setImage(const QImage &image)
+
+ Sets \a image.
+ Upon textures loading, the image data will be read as is, with no formats conversions.
+
+ \note image() and data() cannot be both set at the same time.
+ */
+
+/*!
+ \fn QByteArray QRhiTextureSubresourceUploadDescription::data() const
+ \return the currently set raw pixel data.
+ */
+
+/*!
+ \fn void QRhiTextureSubresourceUploadDescription::setData(const QByteArray &data)
+
+ Sets \a data.
+
+ \note image() and data() cannot be both set at the same time.
+ */
+
+/*!
+ \fn quint32 QRhiTextureSubresourceUploadDescription::dataStride() const
+ \return the currently set data stride.
+ */
+
+/*!
+ \fn void QRhiTextureSubresourceUploadDescription::setDataStride(quint32 stride)
+
+ Sets the data \a stride in bytes. By default this is 0 and not always
+ relevant. When providing raw data(), and the stride is not specified via
+ setDataStride(), 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. Otherwise, if there is additional space between the
+ lines, set a non-zero \a stride. All this is applicable only when raw image
+ data is provided, and is not necessary when working QImage since that has
+ its own \l{QImage::bytesPerLine()}{stride} value.
+
+ \note Setting the stride via setDataStride() is only functional when
+ QRhi::ImageDataStride is reported as
+ \l{QRhi::isFeatureSupported()}{supported}.
+
+ \note When a QImage is given, the stride returned from
+ QImage::bytesPerLine() is taken into account automatically and therefore
+ there is no need to set the data stride manually.
+ */
+
+/*!
+ \fn QPoint QRhiTextureSubresourceUploadDescription::destinationTopLeft() const
+ \return the currently set destination top-left position. Defaults to (0, 0).
+ */
+
+/*!
+ \fn void QRhiTextureSubresourceUploadDescription::setDestinationTopLeft(const QPoint &p)
+ Sets the destination top-left position \a p.
+ */
+
+/*!
+ \fn QSize QRhiTextureSubresourceUploadDescription::sourceSize() const
+
+ \return the source size in pixels. Defaults to a default-constructed QSize,
+ which indicates the entire subresource.
+ */
+
+/*!
+ \fn void QRhiTextureSubresourceUploadDescription::setSourceSize(const QSize &size)
+
+ Sets the source \a size in pixels.
+
+ \note Setting sourceSize() or sourceTopLeft() may trigger a QImage copy
+ internally, depending on the format and the backend.
+ */
+
+/*!
+ \fn QPoint QRhiTextureSubresourceUploadDescription::sourceTopLeft() const
+ \return the currently set source top-left position. Defaults to (0, 0).
+ */
+
+/*!
+ \fn void QRhiTextureSubresourceUploadDescription::setSourceTopLeft(const QPoint &p)
+
+ Sets the source top-left position \a p.
+
+ \note Setting sourceSize() or sourceTopLeft() may trigger a QImage copy
+ internally, depending on the format and the backend.
+ */
+
+/*!
\class QRhiTextureUploadEntry
- \internal
\inmodule QtGui
+ \since 6.6
\brief Describes one layer (face for cubemaps, slice for 3D textures,
element for texture arrays) in a texture upload operation.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
@@ -1813,18 +2983,61 @@ QRhiTextureUploadEntry::QRhiTextureUploadEntry(int layer, int level,
}
/*!
+ \fn int QRhiTextureUploadEntry::layer() const
+ \return the currently set layer index (cubemap face, array layer). Defaults to 0.
+ */
+
+/*!
+ \fn void QRhiTextureUploadEntry::setLayer(int layer)
+ Sets the \a layer.
+ */
+
+/*!
+ \fn int QRhiTextureUploadEntry::level() const
+ \return the currently set mip level. Defaults to 0.
+ */
+
+/*!
+ \fn void QRhiTextureUploadEntry::setLevel(int level)
+ Sets the mip \a level.
+ */
+
+/*!
+ \fn QRhiTextureSubresourceUploadDescription QRhiTextureUploadEntry::description() const
+ \return the currently set subresource description.
+ */
+
+/*!
+ \fn void QRhiTextureUploadEntry::setDescription(const QRhiTextureSubresourceUploadDescription &desc)
+ Sets the subresource description \a desc.
+ */
+
+/*!
\class QRhiTextureUploadDescription
- \internal
\inmodule QtGui
+ \since 6.6
\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.
+ targeting level 0 for layer 0.
+
+ An example of the the common, simple case of wanting to upload the contents
+ of a QImage to a QRhiTexture with a matching pixel size:
+
+ \code
+ QImage image(256, 256, QImage::Format_RGBA8888);
+ image.fill(Qt::green); // or could use a QPainter targeting image
+ QRhiTexture *texture = rhi->newTexture(QRhiTexture::RGBA8, QSize(256, 256));
+ texture->create();
+ QRhiResourceUpdateBatch *u = rhi->nextResourceUpdateBatch();
+ u->uploadTexture(texture, image);
+ \endcode
+
+ When cubemaps, pre-generated mip images, compressed textures, or partial
+ uploads are involved, applications will have to use this class instead.
QRhiTextureUploadDescription also enables specifying batched uploads, which
are useful for example when generating an atlas or glyph cache texture:
@@ -1840,32 +3053,35 @@ QRhiTextureUploadEntry::QRhiTextureUploadEntry(int layer, int level,
For example, specifying the faces of a cubemap could look like the following:
- \badcode
+ \code
QImage faces[6];
- ...
- QList<QRhiTextureUploadEntry> entries;
+ // ...
+ QVarLengthArray<QRhiTextureUploadEntry, 6> entries;
for (int i = 0; i < 6; ++i)
entries.append(QRhiTextureUploadEntry(i, 0, faces[i]));
- QRhiTextureUploadDescription desc(entries);
+ QRhiTextureUploadDescription desc;
+ desc.setEntries(entries.cbegin(), entries.cend());
resourceUpdates->uploadTexture(texture, desc);
\endcode
Another example that specifies mip images for a compressed texture:
- \badcode
- QRhiTextureUploadDescription desc;
+ \code
+ QList<QRhiTextureUploadEntry> entries;
const int mipCount = rhi->mipLevelsForSize(compressedTexture->pixelSize());
for (int level = 0; level < mipCount; ++level) {
const QByteArray compressedDataForLevel = ..
- desc.append(QRhiTextureUploadEntry(0, level, compressedDataForLevel));
+ entries.append(QRhiTextureUploadEntry(0, level, compressedDataForLevel));
}
+ QRhiTextureUploadDescription desc;
+ desc.setEntries(entries.cbegin(), entries.cend());
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
+ \code
QRhiTextureSubresourceUploadDescription subresDesc(image);
subresDesc.setSourceSize(QSize(10, 10));
subResDesc.setDestinationTopLeft(QPoint(50, 40));
@@ -1879,6 +3095,11 @@ QRhiTextureUploadEntry::QRhiTextureUploadEntry(int layer, int level,
QRhiTextureUploadDescription desc({ entry, entry2});
resourceUpdates->uploadTexture(texture, desc);
\endcode
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
+ \sa QRhiResourceUpdateBatch
*/
/*!
@@ -1911,9 +3132,39 @@ QRhiTextureUploadDescription::QRhiTextureUploadDescription(std::initializer_list
}
/*!
+ \fn void QRhiTextureUploadDescription::setEntries(std::initializer_list<QRhiTextureUploadEntry> list)
+ Sets the \a list of entries.
+ */
+
+/*!
+ \fn template<typename InputIterator> void QRhiTextureUploadDescription::setEntries(InputIterator first, InputIterator last)
+ Sets the list of entries using the iterators \a first and \a last.
+ */
+
+/*!
+ \fn const QRhiTextureUploadEntry *QRhiTextureUploadDescription::cbeginEntries() const
+ \return a const iterator pointing to the first item in the entry list.
+ */
+
+/*!
+ \fn const QRhiTextureUploadEntry *QRhiTextureUploadDescription::cendEntries() const
+ \return a const iterator pointing just after the last item in the entry list.
+ */
+
+/*!
+ \fn const QRhiTextureUploadEntry *QRhiTextureUploadDescription::entryAt(qsizetype index) const
+ \return the entry at \a index.
+ */
+
+/*!
+ \fn qsizetype QRhiTextureUploadDescription::entryCount() const
+ \return the number of entries.
+ */
+
+/*!
\class QRhiTextureCopyDescription
- \internal
\inmodule QtGui
+ \since 6.6
\brief Describes a texture-to-texture copy operation.
An empty pixelSize() indicates that the entire subresource is to be copied.
@@ -1933,6 +3184,9 @@ QRhiTextureUploadDescription::QRhiTextureUploadDescription(std::initializer_list
copied at a time. The source and destination layer and mip level indices can
differ, but the size and position must be carefully controlled to avoid out
of bounds copies, in which case the behavior is undefined.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
@@ -1942,9 +3196,83 @@ QRhiTextureUploadDescription::QRhiTextureUploadDescription(std::initializer_list
*/
/*!
+ \fn QSize QRhiTextureCopyDescription::pixelSize() const
+ \return the size of the region to copy.
+
+ \note 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.
+ */
+
+/*!
+ \fn void QRhiTextureCopyDescription::setPixelSize(const QSize &sz)
+ Sets the size of the region to copy to \a sz.
+ */
+
+/*!
+ \fn int QRhiTextureCopyDescription::sourceLayer() const
+ \return the source array layer (cubemap face or array layer index). Defaults to 0.
+ */
+
+/*!
+ \fn void QRhiTextureCopyDescription::setSourceLayer(int layer)
+ Sets the source array \a layer.
+ */
+
+/*!
+ \fn int QRhiTextureCopyDescription::sourceLevel() const
+ \return the source mip level. Defaults to 0.
+ */
+
+/*!
+ \fn void QRhiTextureCopyDescription::setSourceLevel(int level)
+ Sets the source mip \a level.
+ */
+
+/*!
+ \fn QPoint QRhiTextureCopyDescription::sourceTopLeft() const
+ \return the source top-left position (in pixels). Defaults to (0, 0).
+ */
+
+/*!
+ \fn void QRhiTextureCopyDescription::setSourceTopLeft(const QPoint &p)
+ Sets the source top-left position to \a p.
+ */
+
+/*!
+ \fn int QRhiTextureCopyDescription::destinationLayer() const
+ \return the destination array layer (cubemap face or array layer index). Default to 0.
+ */
+
+/*!
+ \fn void QRhiTextureCopyDescription::setDestinationLayer(int layer)
+ Sets the destination array \a layer.
+ */
+
+/*!
+ \fn int QRhiTextureCopyDescription::destinationLevel() const
+ \return the destionation mip level. Defaults to 0.
+ */
+
+/*!
+ \fn void QRhiTextureCopyDescription::setDestinationLevel(int level)
+ Sets the destination mip \a level.
+ */
+
+/*!
+ \fn QPoint QRhiTextureCopyDescription::destinationTopLeft() const
+ \return the destionation top-left position in pixels. Defaults to (0, 0).
+ */
+
+/*!
+ \fn void QRhiTextureCopyDescription::setDestinationTopLeft(const QPoint &p)
+ Sets the destination top-left position \a p.
+ */
+
+/*!
\class QRhiReadbackDescription
- \internal
\inmodule QtGui
+ \since 6.6
\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
@@ -1962,10 +3290,13 @@ QRhiTextureUploadDescription::QRhiTextureUploadDescription(std::initializer_list
\note Multisample textures cannot be read back. Readbacks are supported for
multisample swapchain buffers however.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
- \fn QRhiReadbackDescription::QRhiReadbackDescription()
+ \fn QRhiReadbackDescription::QRhiReadbackDescription() = default
Constructs an empty texture readback description.
@@ -1989,32 +3320,140 @@ QRhiReadbackDescription::QRhiReadbackDescription(QRhiTexture *texture)
}
/*!
+ \fn QRhiTexture *QRhiReadbackDescription::texture() const
+
+ \return the QRhiTexture that is read back. Can be left set to \nullptr
+ which indicates that the backbuffer of the current swapchain is to be used
+ instead.
+ */
+
+/*!
+ \fn void QRhiReadbackDescription::setTexture(QRhiTexture *tex)
+
+ Sets the texture \a tex as the source of the readback operation.
+
+ Setting \nullptr is valid too, in which case the current swapchain's
+ current backbuffer is used. (but then the readback cannot be issued in a
+ non-swapchain-based frame)
+
+ \note Multisample textures cannot be read back. Readbacks are supported for
+ multisample swapchain buffers however.
+
+ \note Textures used in readbacks must be created with
+ QRhiTexture::UsedAsTransferSource.
+
+ \note Swapchains used in readbacks must be created with
+ QRhiSwapChain::UsedAsTransferSource.
+ */
+
+/*!
+ \fn int QRhiReadbackDescription::layer() const
+
+ \return the currently set array layer (cubemap face, array index). Defaults to 0.
+
+ Applicable only when the source of the readback is a QRhiTexture.
+ */
+
+/*!
+ \fn void QRhiReadbackDescription::setLayer(int layer)
+ Sets the array \a layer to read back.
+ */
+
+/*!
+ \fn int QRhiReadbackDescription::level() const
+
+ \return the currently set mip level. Defaults to 0.
+
+ Applicable only when the source of the readback is a QRhiTexture.
+ */
+
+/*!
+ \fn void QRhiReadbackDescription::setLevel(int level)
+ Sets the mip \a level to read back.
+ */
+
+/*!
\class QRhiReadbackResult
- \internal
\inmodule QtGui
- \brief Describes the results of a potentially asynchronous readback operation.
+ \since 6.6
+ \brief Describes the results of a potentially asynchronous buffer or texture 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.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+ */
+
+/*!
+ \variable QRhiReadbackResult::completed
+
+ Callback that is invoked upon completion, on the thread the QRhi operates
+ on. Can be left set to \nullptr, in which case no callback is invoked.
+ */
+
+/*!
+ \variable QRhiReadbackResult::format
+
+ Valid only for textures, the texture format.
*/
/*!
+ \variable QRhiReadbackResult::pixelSize
+
+ Valid only for textures, the size in pixels.
+ */
+
+/*!
+ \variable QRhiReadbackResult::data
+
+ The buffer or image data.
+
+ \sa QRhiResourceUpdateBatch::readBackTexture(), QRhiResourceUpdateBatch::readBackBuffer()
+ */
+
+
+/*!
\class QRhiNativeHandles
- \internal
\inmodule QtGui
+ \since 6.6
\brief Base class for classes exposing backend-specific collections of native resource objects.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
\class QRhiResource
- \internal
\inmodule QtGui
+ \since 6.6
\brief Base class for classes encapsulating native resource objects.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
- \fn QRhiResource::Type QRhiResource::resourceType() const
+ \enum QRhiResource::Type
+ Specifies type of the resource.
+
+ \value Buffer
+ \value Texture
+ \value Sampler
+ \value RenderBuffer
+ \value RenderPassDescriptor
+ \value SwapChainRenderTarget
+ \value TextureRenderTarget
+ \value ShaderResourceBindings
+ \value GraphicsPipeline
+ \value SwapChain
+ \value ComputePipeline
+ \value CommandBuffer
+ */
+
+/*!
+ \fn virtual QRhiResource::Type QRhiResource::resourceType() const = 0
\return the type of the resource.
*/
@@ -2046,7 +3485,7 @@ QRhiResource::~QRhiResource()
}
/*!
- \fn void QRhiResource::destroy()
+ \fn virtual void QRhiResource::destroy() = 0
Releases (or requests deferred releasing of) the underlying native graphics
resources. Safe to call multiple times, subsequent invocations will be a
@@ -2060,7 +3499,7 @@ QRhiResource::~QRhiResource()
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.
+ function is not necessary before deleting a QRhiResource.
\sa deleteLater()
*/
@@ -2073,11 +3512,42 @@ QRhiResource::~QRhiResource()
requirement of not altering QRhiResource objects that are referenced by the
frame being recorded.
+ If the QRhi that created this object is already destroyed, the object is
+ deleted immediately.
+
+ Using deleteLater() can be a useful convenience in many cases, and it
+ complements the low-level guarantee (that the underlying native graphics
+ objects are never destroyed until it is safe to do so and it is known for
+ sure that they are not used by the GPU in an still in-flight frame), by
+ offering a way to make sure the C++ object instances (of QRhiBuffer,
+ QRhiTexture, etc.) themselves also stay valid until the end of the current
+ frame.
+
+ The following example shows a convenient way of creating a throwaway buffer
+ that is only used in one frame and gets automatically released in
+ endFrame(). (when it comes to the underlying native buffer(s), the usual
+ guarantee applies: the QRhi backend defers the releasing of those until it
+ is guaranteed that the frame in which the buffer is accessed by the GPU has
+ completed)
+
+ \code
+ rhi->beginFrame(swapchain);
+ QRhiBuffer *buf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, 256);
+ buf->deleteLater(); // !
+ u = rhi->nextResourceUpdateBatch();
+ u->uploadStaticBuffer(buf, data);
+ // ... draw with buf
+ rhi->endFrame();
+ \endcode
+
\sa destroy()
*/
void QRhiResource::deleteLater()
{
- m_rhi->addDeleteLater(this);
+ if (m_rhi)
+ m_rhi->addDeleteLater(this);
+ else
+ delete this;
}
/*!
@@ -2091,7 +3561,7 @@ QByteArray QRhiResource::name() const
/*!
Sets a \a name for the object.
- This has two uses: to get descriptive names for the native graphics
+ This allows getting 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}.
@@ -2125,10 +3595,141 @@ quint64 QRhiResource::globalResourceId() const
}
/*!
+ \return the QRhi that created this resource.
+
+ If the QRhi that created this object is already destroyed, the result is
+ \nullptr.
+ */
+QRhi *QRhiResource::rhi() const
+{
+ return m_rhi ? m_rhi->q : nullptr;
+}
+
+/*!
\class QRhiBuffer
- \internal
\inmodule QtGui
+ \since 6.6
\brief Vertex, index, or uniform (constant) buffer resource.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
+ A QRhiBuffer encapsulates zero, one, or more native buffer objects (such as
+ a \c VkBuffer or \c MTLBuffer). With some graphics APIs and backends
+ certain types of buffers may not use a native buffer object at all (e.g.
+ OpenGL if uniform buffer objects are not used), but this is transparent to
+ the user of the QRhiBuffer API. Similarly, the fact that some types of
+ buffers may use two or three native buffers underneath, in order to allow
+ efficient per-frame content update without stalling the GPU pipeline, is
+ mostly invisible to the applications and libraries.
+
+ A QRhiBuffer instance is always created by calling
+ \l{QRhi::newBuffer()}{the QRhi's newBuffer() function}. This creates no
+ native graphics resources. To do that, call create() after setting the
+ appropriate options, such as the type, usage flags, size, although in most cases these
+ are already set based on the arguments passed to
+ \l{QRhi::newBuffer()}{newBuffer()}.
+
+ \section2 Example usage
+
+ To create a uniform buffer for a shader where the GLSL uniform block
+ contains a single \c mat4 member, and update the contents:
+
+ \code
+ QRhiBuffer *ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64);
+ if (!ubuf->create()) { error(); }
+ QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
+ QMatrix4x4 mvp;
+ // ... set up the modelview-projection matrix
+ batch->updateDynamicBuffer(ubuf, 0, 64, mvp.constData());
+ // ...
+ commandBuffer->resourceUpdate(batch); // or, alternatively, pass 'batch' to a beginPass() call
+ \endcode
+
+ An example of creating a buffer with vertex data:
+
+ \code
+ const float vertices[] = { -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 1.0f };
+ QRhiBuffer *vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices));
+ if (!vbuf->create()) { error(); }
+ QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
+ batch->uploadStaticBuffer(vbuf, vertices);
+ // ...
+ commandBuffer->resourceUpdate(batch); // or, alternatively, pass 'batch' to a beginPass() call
+ \endcode
+
+ An index buffer:
+
+ \code
+ static const quint16 indices[] = { 0, 1, 2 };
+ QRhiBuffer *ibuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indices));
+ if (!ibuf->create()) { error(); }
+ QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
+ batch->uploadStaticBuffer(ibuf, indices);
+ // ...
+ commandBuffer->resourceUpdate(batch); // or, alternatively, pass 'batch' to a beginPass() call
+ \endcode
+
+ \section2 Common patterns
+
+ A call to create() destroys any existing native resources if create() was
+ successfully called before. If those native resources are still in use by
+ an in-flight frame (i.e., there's a chance they are still read by the GPU),
+ the destroying of those resources is deferred automatically. Thus a very
+ common and convenient pattern to safely increase the size of an already
+ initialized buffer is the following. In practice this drops and creates a
+ whole new set of native resources underneath, so it is not necessarily a
+ cheap operation, but is more convenient and still faster than the
+ alternatives, because by not destroying the \c buf object itself, all
+ references to it stay valid in other data structures (e.g., in any
+ QRhiShaderResourceBinding the QRhiBuffer is referenced from).
+
+ \code
+ if (buf->size() < newSize) {
+ buf->setSize(newSize);
+ if (!buf->create()) { error(); }
+ }
+ // continue using buf, fill it with new data
+ \endcode
+
+ When working with uniform buffers, it will sometimes be necessary to
+ combine data for multiple draw calls into a single buffer for efficiency
+ reasons. Be aware of the aligment requirements: with some graphics APIs
+ offsets for a uniform buffer must be aligned to 256 bytes. This applies
+ both to QRhiShaderResourceBinding and to the dynamic offsets passed to
+ \l{QRhiCommandBuffer::setShaderResources()}{setShaderResources()}. Use the
+ \l{QRhi::ubufAlignment()}{ubufAlignment()} and
+ \l{QRhi::ubufAligned()}{ubufAligned()} functions to create portable code.
+ As an example, the following is an outline for issuing multiple (\c N) draw
+ calls with the same pipeline and geometry, but with a different data in the
+ uniform buffers exposed at binding point 0. This assumes the buffer is
+ exposed via
+ \l{QRhiShaderResourceBinding::uniformBufferWithDynamicOffset()}{uniformBufferWithDynamicOffset()}
+ which allows passing a QRhiCommandBuffer::DynamicOffset list to
+ \l{QRhiCommandBuffer::setShaderResources()}{setShaderResources()}.
+
+ \code
+ const int N = 2;
+ const int UB_SIZE = 64 + 4; // assuming a uniform block with { mat4 matrix; float opacity; }
+ const int ONE_UBUF_SIZE = rhi->ubufAligned(UB_SIZE);
+ const int TOTAL_UBUF_SIZE = N * ONE_UBUF_SIZE;
+ QRhiBuffer *ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, TOTAL_UBUF_SIZE);
+ if (!ubuf->create()) { error(); }
+ QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
+ for (int i = 0; i < N; ++i) {
+ batch->updateDynamicBuffer(ubuf, i * ONE_UBUF_SIZE, 64, matrix.constData());
+ batch->updateDynamicBuffer(ubuf, i * ONE_UBUF_SIZE + 64, 4, &opacity);
+ }
+ // ...
+ // beginPass(), set pipeline, etc., and then:
+ for (int i = 0; i < N; ++i) {
+ QRhiCommandBuffer::DynamicOffset dynOfs[] = { { 0, i * ONE_UBUF_SIZE } };
+ cb->setShaderResources(srb, 1, dynOfs);
+ cb->draw(36);
+ }
+ \endcode
+
+ \sa QRhiResourceUpdateBatch, QRhi, QRhiCommandBuffer
*/
/*!
@@ -2164,42 +3765,29 @@ quint64 QRhiResource::globalResourceId() const
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()}.
+ \l{QRhiCommandBuffer::setVertexInput()}{setVertexInput()}.
\value IndexBuffer Index buffer. This allows the QRhiBuffer to be used in
- \l{setVertexInput()}{QRhiCommandBuffer::setVertexInput()}.
+ \l{QRhiCommandBuffer::setVertexInput()}{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{QRhiShaderResourceBinding::UniformBuffer}{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
+ in combination with \l{QRhiShaderResourceBinding::BufferLoad}{BufferLoad},
+ \l{QRhiShaderResourceBinding::BufferStore}{BufferStore}, or
+ \l{QRhiShaderResourceBinding::BufferLoadStore}{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
- create(), and for already created 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.
- */
-
-/*!
\class QRhiBuffer::NativeBuffer
+ \inmodule QtGui
\brief Contains information about the underlying native resources of a buffer.
*/
@@ -2211,10 +3799,13 @@ quint64 QRhiResource::globalResourceId() const
objects array are pointers to a GLuint. With Vulkan, the native handle is a
VkBuffer, so the elements of the array are pointers to a VkBuffer. With
Direct3D 11 and Metal the elements are pointers to a ID3D11Buffer or
- MTLBuffer pointer, respectively.
+ MTLBuffer pointer, respectively. With Direct3D 12, the elements are
+ pointers to a ID3D12Resource.
\note Pay attention to the fact that the elements are always pointers to
the native buffer handle type, even if the native type itself is a pointer.
+ (so the elements are \c{VkBuffer *} on Vulkan, even though VkBuffer itself
+ is a pointer on 64-bit architectures).
*/
/*!
@@ -2235,7 +3826,7 @@ quint64 QRhiResource::globalResourceId() const
/*!
\internal
*/
-QRhiBuffer::QRhiBuffer(QRhiImplementation *rhi, Type type_, UsageFlags usage_, int size_)
+QRhiBuffer::QRhiBuffer(QRhiImplementation *rhi, Type type_, UsageFlags usage_, quint32 size_)
: QRhiResource(rhi),
m_type(type_), m_usage(usage_), m_size(size_)
{
@@ -2250,7 +3841,7 @@ QRhiResource::Type QRhiBuffer::resourceType() const
}
/*!
- \fn bool QRhiBuffer::create()
+ \fn virtual bool QRhiBuffer::create() = 0
Creates the corresponding native graphics resources. If there are already
resources present due to an earlier create() with no corresponding
@@ -2261,6 +3852,50 @@ QRhiResource::Type QRhiBuffer::resourceType() const
*/
/*!
+ \fn QRhiBuffer::Type QRhiBuffer::type() const
+ \return the buffer type.
+ */
+
+/*!
+ \fn void QRhiBuffer::setType(Type t)
+ Sets the buffer's type to \a t.
+ */
+
+/*!
+ \fn QRhiBuffer::UsageFlags QRhiBuffer::usage() const
+ \return the buffer's usage flags.
+ */
+
+/*!
+ \fn void QRhiBuffer::setUsage(UsageFlags u)
+ Sets the buffer's usage flags to \a u.
+ */
+
+/*!
+ \fn quint32 QRhiBuffer::size() const
+
+ \return the buffer's size in bytes.
+
+ This is always the value that was passed to setSize() or QRhi::newBuffer().
+ Internally, the native buffers may be bigger if that is required by the
+ underlying graphics API.
+ */
+
+/*!
+ \fn void QRhiBuffer::setSize(quint32 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
+ create(), and for already created 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.
+ */
+
+/*!
\return the underlying native resources for this buffer. The returned value
will be empty if exposing the underlying native resources is not supported by
the backend.
@@ -2348,18 +3983,18 @@ void QRhiBuffer::endFullDynamicBufferUpdateForCurrentFrame()
/*!
\class QRhiRenderBuffer
- \internal
\inmodule QtGui
+ \since 6.6
\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
+ A \l 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
+ \l 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
@@ -2373,6 +4008,9 @@ void QRhiBuffer::endFullDynamicBufferUpdateForCurrentFrame()
QRhi provides automatic sizing behavior to match the color buffers, which
means calling setPixelSize() and create() are not necessary for such
renderbuffers.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
@@ -2384,6 +4022,22 @@ void QRhiBuffer::endFullDynamicBufferUpdateForCurrentFrame()
*/
/*!
+ \struct QRhiRenderBuffer::NativeRenderBuffer
+ \inmodule QtGui
+ \brief Wraps a native renderbuffer object.
+ */
+
+/*!
+ \variable QRhiRenderBuffer::NativeRenderBuffer::object
+ \brief 64-bit integer containing the native object handle.
+
+ Used with QRhiRenderBuffer::createFrom().
+
+ With OpenGL the native handle is a GLuint value. \c object is expected to
+ be a valid OpenGL renderbuffer object ID.
+ */
+
+/*!
\enum QRhiRenderBuffer::Flag
Flag values for flags() and setFlags()
@@ -2420,7 +4074,7 @@ QRhiResource::Type QRhiRenderBuffer::resourceType() const
}
/*!
- \fn bool QRhiRenderBuffer::create()
+ \fn virtual bool QRhiRenderBuffer::create() = 0
Creates the corresponding native graphics resources. If there are already
resources present due to an earlier create() with no corresponding
@@ -2469,16 +4123,121 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src)
}
/*!
- \fn QRhiTexture::Format QRhiRenderBuffer::backingFormat() const
+ \fn QRhiRenderBuffer::Type QRhiRenderBuffer::type() const
+ \return the renderbuffer type.
+ */
+
+/*!
+ \fn void QRhiRenderBuffer::setType(Type t)
+ Sets the type to \a t.
+ */
+
+/*!
+ \fn QSize QRhiRenderBuffer::pixelSize() const
+ \return the pixel size.
+ */
+
+/*!
+ \fn void QRhiRenderBuffer::setPixelSize(const QSize &sz)
+ Sets the size (in pixels) to \a sz.
+ */
+
+/*!
+ \fn int QRhiRenderBuffer::sampleCount() const
+ \return the sample count. 1 means no multisample antialiasing.
+ */
+
+/*!
+ \fn void QRhiRenderBuffer::setSampleCount(int s)
+ Sets the sample count to \a s.
+ */
+
+/*!
+ \fn QRhiRenderBuffer::Flags QRhiRenderBuffer::flags() const
+ \return the flags.
+ */
+
+/*!
+ \fn void QRhiRenderBuffer::setFlags(Flags f)
+ Sets the flags to \a f.
+ */
+
+/*!
+ \fn virtual QRhiTexture::Format QRhiRenderBuffer::backingFormat() const = 0
\internal
*/
/*!
\class QRhiTexture
- \internal
\inmodule QtGui
+ \since 6.6
\brief Texture resource.
+
+ A QRhiTexture encapsulates a native texture object, such as a \c VkImage or
+ \c MTLTexture.
+
+ A QRhiTexture instance is always created by calling
+ \l{QRhi::newTexture()}{the QRhi's newTexture() function}. This creates no
+ native graphics resources. To do that, call create() after setting the
+ appropriate options, such as the format and size, although in most cases
+ these are already set based on the arguments passed to
+ \l{QRhi::newTexture()}{newTexture()}.
+
+ Setting the \l{QRhiTexture::Flags}{flags} correctly is essential, otherwise
+ various errors can occur depending on the underlying QRhi backend and
+ graphics API. For example, when a texture will be rendered into from a
+ render pass via QRhiTextureRenderTarget, the texture must be created with
+ the \l RenderTarget flag set. Similarly, when the texture is going to be
+ \l{QRhiResourceUpdateBatch::readBackTexture()}{read back}, the \l
+ UsedAsTransferSource flag must be set upfront. Mipmapped textures must have
+ the MipMapped flag set. And so on. It is not possible to change the flags
+ once create() has succeeded. To release the existing and create a new
+ native texture object with the changed settings, call the setters and call
+ create() again. This then might be a potentially expensive operation.
+
+ \section2 Example usage
+
+ To create a 2D texture with a size of 512x512 pixels and set its contents to all green:
+
+ \code
+ QRhiTexture *texture = rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 512));
+ if (!texture->create()) { error(); }
+ QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
+ QImage image(512, 512, QImage::Format_RGBA8888);
+ image.fill(Qt::green);
+ batch->uploadTexture(texture, image);
+ // ...
+ commandBuffer->resourceUpdate(batch); // or, alternatively, pass 'batch' to a beginPass() call
+ \endcode
+
+ \section2 Common patterns
+
+ A call to create() destroys any existing native resources if create() was
+ successfully called before. If those native resources are still in use by
+ an in-flight frame (i.e., there's a chance they are still read by the GPU),
+ the destroying of those resources is deferred automatically. Thus a very
+ common and convenient pattern to safely change the size of an already
+ existing texture is the following. In practice this drops and creates a
+ whole new native texture resource underneath, so it is not necessarily a
+ cheap operation, but is more convenient and still faster than the
+ alternatives, because by not destroying the \c texture object itself, all
+ references to it stay valid in other data structures (e.g., in any
+ QShaderResourceBinding the QRhiTexture is referenced from).
+
+ \code
+ // determine newSize, e.g. based on the swapchain's output size or other factors
+ if (texture->pixelSize() != newSize) {
+ texture->setPixelSize(newSize);
+ if (!texture->create()) { error(); }
+ }
+ // continue using texture, fill it with new data
+ \endcode
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
+ \sa QRhiResourceUpdateBatch, QRhi, QRhiTextureRenderTarget
*/
/*!
@@ -2545,6 +4304,14 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src)
QRhi::TextureArrays feature. When rendering into, or uploading data to a
texture array, the \c layer specified in the render target's color
attachment or the upload description selects a single element in the array.
+
+ \value OneDimensional The texture is a 1D texture. Such textures can be
+ created by passing a 0 height and depth to QRhi::newTexture(). Note that
+ there can be limitations on one dimensional textures depending on the
+ underlying graphics API. For example, rendering to them or using them with
+ mipmap-based filtering may be unsupported. This is indicated by the
+ QRhi::OneDimensionalTextures and QRhi::OneDimensionalTextureMipmaps
+ feature flags.
*/
/*!
@@ -2578,7 +4345,7 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src)
\value R32F One component, 32-bit float.
- \value RGBA10A2 Four components, unsigned normalized 10 bit R, G, and B,
+ \value RGB10A2 Four components, unsigned normalized 10 bit R, G, and B,
2-bit alpha. This is a packed format so native endianness applies. Note
that there is no BGR10A2. This is because RGB10A2 maps to
DXGI_FORMAT_R10G10B10A2_UNORM with D3D, MTLPixelFormatRGB10A2Unorm with
@@ -2624,7 +4391,8 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src)
*/
/*!
- \class QRhiTexture::NativeTexture
+ \struct QRhiTexture::NativeTexture
+ \inmodule QtGui
\brief Contains information about the underlying native resources of a texture.
*/
@@ -2633,9 +4401,10 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src)
\brief 64-bit integer containing the native object handle.
With OpenGL, the native handle is a GLuint value, so \c object can then be
- cast to a GLuint. With Vulkan, the native handle is a VkImage, so \c
- object can be cast to a VkImage. With Direct3D 11 and Metal \c
- object contains a ID3D11Texture2D or MTLTexture pointer, respectively.
+ cast to a GLuint. With Vulkan, the native handle is a VkImage, so \c object
+ can be cast to a VkImage. With Direct3D 11 and Metal \c object contains a
+ ID3D11Texture2D or MTLTexture pointer, respectively. With Direct3D 12
+ \c object contains a ID3D12Resource pointer.
*/
/*!
@@ -2665,7 +4434,7 @@ QRhiResource::Type QRhiTexture::resourceType() const
}
/*!
- \fn bool QRhiTexture::create()
+ \fn virtual bool QRhiTexture::create() = 0
Creates the corresponding native graphics resources. If there are already
resources present due to an earlier create() with no corresponding
@@ -2688,13 +4457,16 @@ QRhiTexture::NativeTexture QRhiTexture::nativeTexture()
}
/*!
- Similar to create() except that no new native textures are created. Instead,
- the native texture resources specified by \a src is used.
+ Similar to create(), except that no new native textures are created.
+ Instead, the native texture resources specified by \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.
+ \return true if the specified existing native texture object has been
+ successfully wrapped as a non-owning QRhiTexture.
+
\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 createFrom() expecting that the native texture
@@ -2721,7 +4493,7 @@ bool QRhiTexture::createFrom(QRhiTexture::NativeTexture src)
/*!
With some graphics APIs, such as Vulkan, integrating custom rendering code
that uses the graphics API directly needs special care when it comes to
- image layouts. This function allows communicating the expected layout the
+ image layouts. This function allows communicating the expected \a layout the
image backing the QRhiTexture is in after the native rendering commands.
For example, consider rendering into a QRhiTexture's VkImage directly with
@@ -2737,6 +4509,9 @@ bool QRhiTexture::createFrom(QRhiTexture::NativeTexture src)
This function has no effect with QRhi backends where the underlying
graphics API does not expose a concept of image layouts.
+
+ \note With Vulkan \a layout is a \c VkImageLayout. With Direct 3D 12 \a
+ layout is a value composed of the bits from \c D3D12_RESOURCE_STATES.
*/
void QRhiTexture::setNativeLayout(int layout)
{
@@ -2744,10 +4519,196 @@ void QRhiTexture::setNativeLayout(int layout)
}
/*!
+ \fn QRhiTexture::Format QRhiTexture::format() const
+ \return the texture format.
+ */
+
+/*!
+ \fn void QRhiTexture::setFormat(QRhiTexture::Format fmt)
+
+ Sets the requested texture format to \a fmt.
+
+ \note The value set is only taken into account upon the next call to
+ create(), i.e. when the underlying graphics resource are (re)created.
+ Setting a new value is futile otherwise and must be avoided since it can
+ lead to inconsistent state.
+ */
+
+/*!
+ \fn QSize QRhiTexture::pixelSize() const
+ \return the size in pixels.
+ */
+
+/*!
+ \fn void QRhiTexture::setPixelSize(const QSize &sz)
+
+ Sets the texture size, specified in pixels, to \a sz.
+
+ \note The value set is only taken into account upon the next call to
+ create(), i.e. when the underlying graphics resource are (re)created.
+ Setting a new value is futile otherwise and must be avoided since it can
+ lead to inconsistent state. The same applies to all other setters as well.
+ */
+
+/*!
+ \fn int QRhiTexture::depth() const
+ \return the depth for 3D textures.
+ */
+
+/*!
+ \fn void QRhiTexture::setDepth(int depth)
+ Sets the \a depth for a 3D texture.
+ */
+
+/*!
+ \fn int QRhiTexture::arraySize() const
+ \return the texture array size.
+ */
+
+/*!
+ \fn void QRhiTexture::setArraySize(int arraySize)
+ Sets the texture \a arraySize.
+ */
+
+/*!
+ \fn int QRhiTexture::arrayRangeStart() const
+
+ \return the first array layer when setArrayRange() was called.
+
+ \sa setArrayRange()
+ */
+
+/*!
+ \fn int QRhiTexture::arrayRangeLength() const
+
+ \return the exposed array range size when setArrayRange() was called.
+
+ \sa setArrayRange()
+*/
+
+/*!
+ \fn void QRhiTexture::setArrayRange(int startIndex, int count)
+
+ Normally all array layers are exposed and it is up to the shader to select
+ the layer via the third coordinate passed to the \c{texture()} GLSL
+ function when sampling the \c sampler2DArray. When QRhi::TextureArrayRange
+ is reported as supported, calling setArrayRange() before create() or
+ createFrom() requests selecting only the specified range, \a count elements
+ starting from \a startIndex. The shader logic can then be written with this
+ in mind.
+
+ \sa QRhi::TextureArrayRange
+ */
+
+/*!
+ \fn Flags QRhiTexture::flags() const
+ \return the texture flags.
+ */
+
+/*!
+ \fn void QRhiTexture::setFlags(Flags f)
+ Sets the texture flags to \a f.
+ */
+
+/*!
+ \fn int QRhiTexture::sampleCount() const
+ \return the sample count. 1 means no multisample antialiasing.
+ */
+
+/*!
+ \fn void QRhiTexture::setSampleCount(int s)
+ Sets the sample count to \a s.
+ */
+
+/*!
+ \struct QRhiTexture::ViewFormat
+ \inmodule QtGui
+ \since 6.8
+ \brief Specifies the view format for reading or writing from or to the texture.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+ */
+
+/*!
+ \variable QRhiTexture::ViewFormat::format
+ */
+
+/*!
+ \variable QRhiTexture::ViewFormat::srgb
+ */
+
+/*!
+ \fn QRhiTexture::ViewFormat QRhiTexture::readViewFormat() const
+ \since 6.8
+ \return the view format used when sampling the texture. When not called, the view
+ format is assumed to be the same as format().
+ */
+
+/*!
+ \fn void QRhiTexture::setReadViewFormat(const ViewFormat &fmt)
+ \since 6.8
+
+ Sets the shader resource view format (or the format of the view used for
+ sampling the texture) to \a fmt. By default the same format (and sRGB-ness)
+ is used as the texture itself, and in most cases this function does not need
+ to be called.
+
+ This setting is only taken into account when the \l TextureViewFormat
+ feature is reported as supported.
+
+ \note This functionality is provided to allow "casting" between
+ non-sRGB and sRGB in order to get the shader reads perform, or not perform,
+ the implicit sRGB conversions. Other types of casting may or may not be
+ functional.
+ */
+
+/*!
+ \fn QRhiTexture::ViewFormat QRhiTexture::writeViewFormat() const
+ \since 6.8
+ \return the view format used when writing to the texture and when using it
+ with image load/store. When not called, the view format is assumed to be the
+ same as format().
+ */
+
+/*!
+ \fn void QRhiTexture::setWriteViewFormat(const ViewFormat &fmt)
+ \since 6.8
+
+ Sets the render target view format to \a fmt. By default the same format
+ (and sRGB-ness) is used as the texture itself, and in most cases this
+ function does not need to be called.
+
+ One common use case for providing a write view format is working with
+ externally provided textures that, outside of our control, use an sRGB
+ format with 3D APIs such as Vulkan or Direct 3D, but the rendering engine is
+ already prepared to handle linearization and conversion to sRGB at the end
+ of its shading pipeline. In this case what is wanted when rendering into
+ such a texture is a render target view (e.g. VkImageView) that has the same,
+ but non-sRGB format. (if e.g. from an OpenXR implementation one gets a
+ VK_FORMAT_R8G8B8A8_SRGB texture, it is likely that rendering into it should
+ be done using a VK_FORMAT_R8G8B8A8_UNORM view, if that is what the rendering
+ engine's pipeline requires; in this example one would call this function
+ with a ViewFormat that has a format of QRhiTexture::RGBA8 and \c srgb set to
+ \c false).
+
+ This setting is only taken into account when the \l TextureViewFormat
+ feature is reported as supported.
+
+ \note This functionality is provided to allow "casting" between
+ non-sRGB and sRGB in order to get the shader write not perform, or perform,
+ the implicit sRGB conversions. Other types of casting may or may not be
+ functional.
+ */
+
+/*!
\class QRhiSampler
- \internal
\inmodule QtGui
+ \since 6.6
\brief Sampler resource.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
@@ -2804,14 +4765,91 @@ QRhiResource::Type QRhiSampler::resourceType() const
}
/*!
+ \fn QRhiSampler::Filter QRhiSampler::magFilter() const
+ \return the magnification filter mode.
+ */
+
+/*!
+ \fn void QRhiSampler::setMagFilter(Filter f)
+ Sets the magnification filter mode to \a f.
+ */
+
+/*!
+ \fn QRhiSampler::Filter QRhiSampler::minFilter() const
+ \return the minification filter mode.
+ */
+
+/*!
+ \fn void QRhiSampler::setMinFilter(Filter f)
+ Sets the minification filter mode to \a f.
+ */
+
+/*!
+ \fn QRhiSampler::Filter QRhiSampler::mipmapMode() const
+ \return the mipmap filter mode.
+ */
+
+/*!
+ \fn void QRhiSampler::setMipmapMode(Filter f)
+
+ Sets the mipmap filter mode to \a f.
+
+ Leave this set to None when the texture has no mip levels, or when the mip
+ levels are not to be taken into account.
+ */
+
+/*!
+ \fn QRhiSampler::AddressMode QRhiSampler::addressU() const
+ \return the horizontal wrap mode.
+ */
+
+/*!
+ \fn void QRhiSampler::setAddressU(AddressMode mode)
+ Sets the horizontal wrap \a mode.
+ */
+
+/*!
+ \fn QRhiSampler::AddressMode QRhiSampler::addressV() const
+ \return the vertical wrap mode.
+ */
+
+/*!
+ \fn void QRhiSampler::setAddressV(AddressMode mode)
+ Sets the vertical wrap \a mode.
+ */
+
+/*!
+ \fn QRhiSampler::AddressMode QRhiSampler::addressW() const
+ \return the depth wrap mode.
+ */
+
+/*!
+ \fn void QRhiSampler::setAddressW(AddressMode mode)
+ Sets the depth wrap \a mode.
+ */
+
+/*!
+ \fn QRhiSampler::CompareOp QRhiSampler::textureCompareOp() const
+ \return the texture comparison function.
+ */
+
+/*!
+ \fn void QRhiSampler::setTextureCompareOp(CompareOp op)
+ Sets the texture comparison function \a op.
+ */
+
+/*!
\class QRhiRenderPassDescriptor
- \internal
\inmodule QtGui
+ \since 6.6
\brief Render pass resource.
A render pass, if such a concept exists in the underlying graphics API, is
a collection of attachments (color, depth, stencil) and describes how those
attachments are used.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
@@ -2831,7 +4869,7 @@ QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
}
/*!
- \fn bool QRhiRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
+ \fn virtual bool QRhiRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const = 0
\return true if the \a other QRhiRenderPassDescriptor is compatible with
this one, meaning \c this and \a other can be used interchangebly in
@@ -2866,7 +4904,7 @@ QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
*/
/*!
- \fn QRhiRenderPassDescriptor *QRhiRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
+ \fn virtual QRhiRenderPassDescriptor *QRhiRenderPassDescriptor::newCompatibleRenderPassDescriptor() const = 0
\return a new QRhiRenderPassDescriptor that is
\l{isCompatible()}{compatible} with this one.
@@ -2887,7 +4925,7 @@ QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
*/
/*!
- \fn QVector<quint32> QRhiRenderPassDescriptor::serializedFormat() const
+ \fn virtual QVector<quint32> QRhiRenderPassDescriptor::serializedFormat() const = 0
\return a vector of integers containing an opaque blob describing the data
relevant for \l{isCompatible()}{compatibility}.
@@ -2918,10 +4956,20 @@ const QRhiNativeHandles *QRhiRenderPassDescriptor::nativeHandles()
/*!
\class QRhiRenderTarget
- \internal
\inmodule QtGui
+ \since 6.6
\brief Represents an onscreen (swapchain) or offscreen (texture) render target.
+ Applications do not create an instance of this class directly. Rather, it
+ is the subclass QRhiTextureRenderTarget that is instantiable by clients of
+ the API via \l{QRhi::newTextureRenderTarget()}{newTextureRenderTarget()}.
+ The other subclass is QRhiSwapChainRenderTarget, which is the type
+ QRhiSwapChain returns when calling
+ \l{QRhiSwapChain::currentFrameRenderTarget()}{currentFrameRenderTarget()}.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
\sa QRhiSwapChainRenderTarget, QRhiTextureRenderTarget
*/
@@ -2934,7 +4982,7 @@ QRhiRenderTarget::QRhiRenderTarget(QRhiImplementation *rhi)
}
/*!
- \fn QSize QRhiRenderTarget::pixelSize() const
+ \fn virtual QSize QRhiRenderTarget::pixelSize() const = 0
\return the size in pixels.
@@ -2952,7 +5000,7 @@ QRhiRenderTarget::QRhiRenderTarget(QRhiImplementation *rhi)
*/
/*!
- \fn float QRhiRenderTarget::devicePixelRatio() const
+ \fn virtual float QRhiRenderTarget::devicePixelRatio() const = 0
\return the device pixel ratio. For QRhiTextureRenderTarget this is always
1. For targets retrieved from a QRhiSwapChain the value reflects the
@@ -2961,6 +5009,25 @@ QRhiRenderTarget::QRhiRenderTarget(QRhiImplementation *rhi)
*/
/*!
+ \fn virtual int QRhiRenderTarget::sampleCount() const = 0
+
+ \return the sample count or 1 if multisample antialiasing is not relevant for
+ this render target.
+ */
+
+/*!
+ \fn QRhiRenderPassDescriptor *QRhiRenderTarget::renderPassDescriptor() const
+
+ \return the associated QRhiRenderPassDescriptor.
+ */
+
+/*!
+ \fn void QRhiRenderTarget::setRenderPassDescriptor(QRhiRenderPassDescriptor *desc)
+
+ Sets the QRhiRenderPassDescriptor \a desc for use with this render target.
+ */
+
+/*!
\internal
*/
QRhiSwapChainRenderTarget::QRhiSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain_)
@@ -2971,14 +5038,17 @@ QRhiSwapChainRenderTarget::QRhiSwapChainRenderTarget(QRhiImplementation *rhi, QR
/*!
\class QRhiSwapChainRenderTarget
- \internal
\inmodule QtGui
+ \since 6.6
\brief Swapchain render target resource.
When targeting the color buffers of a swapchain, active render target is a
QRhiSwapChainRenderTarget. This is what
QRhiSwapChain::currentFrameRenderTarget() returns.
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
\sa QRhiSwapChain
*/
@@ -2998,8 +5068,8 @@ QRhiResource::Type QRhiSwapChainRenderTarget::resourceType() const
/*!
\class QRhiTextureRenderTarget
- \internal
\inmodule QtGui
+ \since 6.6
\brief Texture render target resource.
A texture render target allows rendering into one or more textures,
@@ -3015,15 +5085,18 @@ QRhiResource::Type QRhiSwapChainRenderTarget::resourceType() const
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);
+ \code
+ QRhiTexture *texture = rhi->newTexture(QRhiTexture::RGBA8, size, 1, QRhiTexture::RenderTarget);
texture->create();
- rt = rhi->newTextureRenderTarget({ texture });
+ QRhiTextureRenderTarget *rt = rhi->newTextureRenderTarget({ texture });
rp = rt->newCompatibleRenderPassDescriptor();
rt->setRenderPassDescriptor(rt);
rt->create();
// rt can now be used with beginPass()
\endcode
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
@@ -3038,7 +5111,19 @@ QRhiResource::Type QRhiSwapChainRenderTarget::resourceType() const
\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.
+ GPUs, but allows preserving the existing contents between passes. When doing
+ multisample rendering with a resolve texture set, setting this flag also
+ requests the multisample color data to be stored (written out) to the
+ multisample texture or render buffer. (for non-multisample rendering the
+ color data is always stored, but for MSAA storing the multisample data
+ decreases efficiency for certain GPU architectures, hence defaulting to not
+ writing it out) Note however that this is non-portable: in some cases there
+ is no intermediate multisample texture on the graphics API level, e.g. when
+ using OpenGL ES's \c{GL_EXT_multisampled_render_to_texture} as it is all
+ implicit, handled by the OpenGL ES implementation. In that case,
+ PreserveColorContents will likely have no effect. Therefore, avoid relying
+ on this flag when using multisample rendering and the color attachment is
+ using a multisample QRhiTexture (not QRhiRenderBuffer).
\value PreserveDepthStencilContents Indicates that the contents of the
depth texture is to be loaded when starting a render pass, instead
@@ -3046,6 +5131,13 @@ QRhiResource::Type QRhiSwapChainRenderTarget::resourceType() const
(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.
+
+ \value DoNotStoreDepthStencilContents Indicates that the contents of the
+ depth texture does not need to be written out. Relevant only when a
+ QRhiTexture, not QRhiRenderBuffer, is used as the depth-stencil buffer,
+ because for QRhiRenderBuffer this is implicit. When a depthResolveTexture is
+ set, the flag is not relevant, because the behavior is then as if the flag
+ was set. This enum value is introduced in Qt 6.8.
*/
/*!
@@ -3069,7 +5161,7 @@ QRhiResource::Type QRhiTextureRenderTarget::resourceType() const
}
/*!
- \fn QRhiRenderPassDescriptor *QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor()
+ \fn virtual QRhiRenderPassDescriptor *QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor() = 0
\return a new QRhiRenderPassDescriptor that is compatible with this render
target.
@@ -3079,7 +5171,8 @@ QRhiResource::Type QRhiTextureRenderTarget::resourceType() const
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
+ be used in combination with a render target that has a
+ \l{QRhiRenderPassDescriptor::isCompatible()}{compatible}
QRhiRenderPassDescriptor set.
Two QRhiTextureRenderTarget instances can share the same render pass
@@ -3095,7 +5188,7 @@ QRhiResource::Type QRhiTextureRenderTarget::resourceType() const
*/
/*!
- \fn bool QRhiTextureRenderTarget::create()
+ \fn virtual bool QRhiTextureRenderTarget::create() = 0
Creates the corresponding native graphics resources. If there are already
resources present due to an earlier create() with no corresponding
@@ -3119,9 +5212,29 @@ QRhiResource::Type QRhiTextureRenderTarget::resourceType() const
*/
/*!
+ \fn QRhiTextureRenderTargetDescription QRhiTextureRenderTarget::description() const
+ \return the render target description.
+ */
+
+/*!
+ \fn void QRhiTextureRenderTarget::setDescription(const QRhiTextureRenderTargetDescription &desc)
+ Sets the render target description \a desc.
+ */
+
+/*!
+ \fn QRhiTextureRenderTarget::Flags QRhiTextureRenderTarget::flags() const
+ \return the currently set flags.
+ */
+
+/*!
+ \fn void QRhiTextureRenderTarget::setFlags(Flags f)
+ Sets the flags to \a f.
+ */
+
+/*!
\class QRhiShaderResourceBindings
- \internal
\inmodule QtGui
+ \since 6.6
\brief Encapsulates resources for making buffer, texture, sampler resources visible to shaders.
A QRhiShaderResourceBindings is a collection of QRhiShaderResourceBinding
@@ -3142,19 +5255,19 @@ QRhiResource::Type QRhiTextureRenderTarget::resourceType() const
QRhiShaderResourceBindings could be created and then passed to
QRhiGraphicsPipeline::setShaderResourceBindings():
- \badcode
- srb = rhi->newShaderResourceBindings();
+ \code
+ QRhiShaderResourceBindings *srb = rhi->newShaderResourceBindings();
srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, sampler)
});
srb->create();
- ...
- ps = rhi->newGraphicsPipeline();
- ...
+ // ...
+ QRhiGraphicsPipeline *ps = rhi->newGraphicsPipeline();
+ // ...
ps->setShaderResourceBindings(srb);
ps->create();
- ...
+ // ...
cb->setGraphicsPipeline(ps);
cb->setShaderResources(); // binds srb
\endcode
@@ -3173,17 +5286,28 @@ QRhiResource::Type QRhiTextureRenderTarget::resourceType() const
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 created with one of
- them in the first place.
+ them in the first place. See isLayoutCompatible() for more details.
- \badcode
- srb2 = rhi->newShaderResourceBindings();
- ...
+ \code
+ QRhiShaderResourceBindings *srb2 = rhi->newShaderResourceBindings();
+ // ...
cb->setGraphicsPipeline(ps);
cb->setShaderResources(srb2); // binds srb2
\endcode
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
+ \typedef QRhiShaderResourceBindingSet
+ \relates QRhi
+ \since 6.7
+
+ Synonym for QRhiShaderResourceBindings.
+*/
+
+/*!
\internal
*/
QRhiShaderResourceBindings::QRhiShaderResourceBindings(QRhiImplementation *rhi)
@@ -3246,7 +5370,7 @@ bool QRhiShaderResourceBindings::isLayoutCompatible(const QRhiShaderResourceBind
\l{isLayoutCompatible()}{layout compatibility tests}.
Given two objects \c srb1 and \c srb2, if the data returned from this
- function is identical, then \c{srb1->isLayoutCompatible(srb2), and vice
+ function is identical, then \c{srb1->isLayoutCompatible(srb2)}, and vice
versa hold true as well.
\note The returned data is meant to be used for storing in memory and
@@ -3262,8 +5386,8 @@ void QRhiImplementation::updateLayoutDesc(QRhiShaderResourceBindings *srb)
srb->m_layoutDescHash = 0;
srb->m_layoutDesc.clear();
auto layoutDescAppender = std::back_inserter(srb->m_layoutDesc);
- for (const QRhiShaderResourceBinding &b : qAsConst(srb->m_bindings)) {
- const QRhiShaderResourceBinding::Data *d = b.data();
+ for (const QRhiShaderResourceBinding &b : std::as_const(srb->m_bindings)) {
+ const QRhiShaderResourceBinding::Data *d = &b.d;
srb->m_layoutDescHash ^= uint(d->binding) ^ uint(d->stage) ^ uint(d->type)
^ uint(d->arraySize());
layoutDescAppender = d->serialize(layoutDescAppender);
@@ -3271,14 +5395,47 @@ void QRhiImplementation::updateLayoutDesc(QRhiShaderResourceBindings *srb)
}
/*!
+ \fn void QRhiShaderResourceBindings::setBindings(std::initializer_list<QRhiShaderResourceBinding> list)
+ Sets the \a list of bindings.
+ */
+
+/*!
+ \fn template<typename InputIterator> void QRhiShaderResourceBindings::setBindings(InputIterator first, InputIterator last)
+ Sets the list of bindings from the iterators \a first and \a last.
+ */
+
+/*!
+ \fn const QRhiShaderResourceBinding *QRhiShaderResourceBindings::cbeginBindings() const
+ \return a const iterator pointing to the first item in the binding list.
+ */
+
+/*!
+ \fn const QRhiShaderResourceBinding *QRhiShaderResourceBindings::cendBindings() const
+ \return a const iterator pointing just after the last item in the binding list.
+ */
+
+/*!
+ \fn const QRhiShaderResourceBinding *QRhiShaderResourceBindings::bindingAt(qsizetype index) const
+ \return the binding at the specified \a index.
+ */
+
+/*!
+ \fn qsizetype QRhiShaderResourceBindings::bindingCount() const
+ \return the number of bindings.
+ */
+
+/*!
\class QRhiShaderResourceBinding
- \internal
\inmodule QtGui
+ \since 6.6
\brief Describes the shader resource for a single binding point.
A QRhiShaderResourceBinding cannot be constructed directly. Instead, use the
static functions such as uniformBuffer() or sampledTexture() to get an
instance.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
@@ -3287,7 +5444,15 @@ void QRhiImplementation::updateLayoutDesc(QRhiShaderResourceBindings *srb)
\value UniformBuffer Uniform buffer
- \value SampledTexture Combined image sampler
+ \value SampledTexture Combined image sampler (a texture and sampler pair).
+ Even when the shading language associated with the underlying 3D API has no
+ support for this concept (e.g. D3D and HLSL), this is still supported
+ because the shader translation layer takes care of the appropriate
+ translation and remapping of binding points or shader registers.
+
+ \value Texture Texture (separate)
+
+ \value Sampler Sampler (separate)
\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
@@ -3327,7 +5492,7 @@ void QRhiImplementation::updateLayoutDesc(QRhiShaderResourceBindings *srb)
For example, \c a and \c b below are not equal, but are compatible layout-wise:
- \badcode
+ \code
auto a = QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buffer);
auto b = QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, someOtherBuffer, 256);
\endcode
@@ -3396,7 +5561,7 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer(
unexpected errors may occur.
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer(
- int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size)
+ int binding, StageFlags stage, QRhiBuffer *buf, quint32 offset, quint32 size)
{
Q_ASSERT(size > 0);
QRhiShaderResourceBinding b;
@@ -3433,7 +5598,7 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer(
unexpected errors may occur.
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(
- int binding, StageFlags stage, QRhiBuffer *buf, int size)
+ int binding, StageFlags stage, QRhiBuffer *buf, quint32 size)
{
Q_ASSERT(size > 0);
QRhiShaderResourceBinding b;
@@ -3462,6 +5627,14 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBufferWithDynamicOff
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
+ \note A shader may not be able to consume more than 16 textures/samplers,
+ depending on the underlying graphics API. This hard limit must be kept in
+ mind in renderer design. This does not apply to texture arrays which
+ consume a single binding point (shader register) and can contain 256-2048
+ textures, depending on the underlying graphics API. Arrays of textures (see
+ sampledTextures()) are however no different in this regard than using the
+ same number of individual textures.
+
\sa sampledTextures()
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTexture(
@@ -3547,6 +5720,14 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTextures(
Vulkan-compatible GLSL code separate textures are declared as \c texture2D
as opposed to \c sampler2D: \c{layout(binding = 1) uniform texture2D tex;}
+ \note A shader may not be able to consume more than 16 textures, depending
+ on the underlying graphics API. This hard limit must be kept in mind in
+ renderer design. This does not apply to texture arrays which consume a
+ single binding point (shader register) and can contain 256-2048 textures,
+ depending on the underlying graphics API. Arrays of textures (see
+ sampledTextures()) are however no different in this regard than using the
+ same number of individual textures.
+
\sa textures(), sampler()
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::texture(int binding, StageFlags stage, QRhiTexture *tex)
@@ -3619,6 +5800,10 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::textures(int binding, Stage
to sample the texture: \c{fragColor = texture(sampler2D(tex, samp),
texcoord);}.
+ \note A shader may not be able to consume more than 16 samplers, depending
+ on the underlying graphics API. This hard limit must be kept in mind in
+ renderer design.
+
\sa texture()
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::sampler(int binding, StageFlags stage, QRhiSampler *sampler)
@@ -3647,6 +5832,13 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::sampler(int binding, StageF
pipelines. Such a pipeline must then always be used together with another,
layout compatible QRhiShaderResourceBindings with resources present passed
to QRhiCommandBuffer::setShaderResources().
+
+ \note Image load/store is only guaranteed to be available within a compute
+ pipeline. While some backends may support using these resources in a
+ graphics pipeline as well, this is not universally supported, and even when
+ it is, unexpected problems may arise when it comes to barriers and
+ synchronization. Therefore, avoid using such resources with shaders other
+ than compute.
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::imageLoad(
int binding, StageFlags stage, QRhiTexture *tex, int level)
@@ -3675,6 +5867,13 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::imageLoad(
pipelines. Such a pipeline must then always be used together with another,
layout compatible QRhiShaderResourceBindings with resources present passed
to QRhiCommandBuffer::setShaderResources().
+
+ \note Image load/store is only guaranteed to be available within a compute
+ pipeline. While some backends may support using these resources in a
+ graphics pipeline as well, this is not universally supported, and even when
+ it is, unexpected problems may arise when it comes to barriers and
+ synchronization. Therefore, avoid using such resources with shaders other
+ than compute.
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::imageStore(
int binding, StageFlags stage, QRhiTexture *tex, int level)
@@ -3703,6 +5902,13 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::imageStore(
pipelines. Such a pipeline must then always be used together with another,
layout compatible QRhiShaderResourceBindings with resources present passed
to QRhiCommandBuffer::setShaderResources().
+
+ \note Image load/store is only guaranteed to be available within a compute
+ pipeline. While some backends may support using these resources in a
+ graphics pipeline as well, this is not universally supported, and even when
+ it is, unexpected problems may arise when it comes to barriers and
+ synchronization. Therefore, avoid using such resources with shaders other
+ than compute.
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::imageLoadStore(
int binding, StageFlags stage, QRhiTexture *tex, int level)
@@ -3729,6 +5935,13 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::imageLoadStore(
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
+
+ \note Buffer load/store is only guaranteed to be available within a compute
+ pipeline. While some backends may support using these resources in a
+ graphics pipeline as well, this is not universally supported, and even when
+ it is, unexpected problems may arise when it comes to barriers and
+ synchronization. Therefore, avoid using such resources with shaders other
+ than compute.
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoad(
int binding, StageFlags stage, QRhiBuffer *buf)
@@ -3757,9 +5970,16 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoad(
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
+
+ \note Buffer load/store is only guaranteed to be available within a compute
+ pipeline. While some backends may support using these resources in a
+ graphics pipeline as well, this is not universally supported, and even when
+ it is, unexpected problems may arise when it comes to barriers and
+ synchronization. Therefore, avoid using such resources with shaders other
+ than compute.
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoad(
- int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size)
+ int binding, StageFlags stage, QRhiBuffer *buf, quint32 offset, quint32 size)
{
Q_ASSERT(size > 0);
QRhiShaderResourceBinding b;
@@ -3785,6 +6005,13 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoad(
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
+
+ \note Buffer load/store is only guaranteed to be available within a compute
+ pipeline. While some backends may support using these resources in a
+ graphics pipeline as well, this is not universally supported, and even when
+ it is, unexpected problems may arise when it comes to barriers and
+ synchronization. Therefore, avoid using such resources with shaders other
+ than compute.
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferStore(
int binding, StageFlags stage, QRhiBuffer *buf)
@@ -3813,9 +6040,16 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferStore(
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
+
+ \note Buffer load/store is only guaranteed to be available within a compute
+ pipeline. While some backends may support using these resources in a
+ graphics pipeline as well, this is not universally supported, and even when
+ it is, unexpected problems may arise when it comes to barriers and
+ synchronization. Therefore, avoid using such resources with shaders other
+ than compute.
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferStore(
- int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size)
+ int binding, StageFlags stage, QRhiBuffer *buf, quint32 offset, quint32 size)
{
Q_ASSERT(size > 0);
QRhiShaderResourceBinding b;
@@ -3841,6 +6075,13 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferStore(
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
+
+ \note Buffer load/store is only guaranteed to be available within a compute
+ pipeline. While some backends may support using these resources in a
+ graphics pipeline as well, this is not universally supported, and even when
+ it is, unexpected problems may arise when it comes to barriers and
+ synchronization. Therefore, avoid using such resources with shaders other
+ than compute.
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoadStore(
int binding, StageFlags stage, QRhiBuffer *buf)
@@ -3869,9 +6110,16 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoadStore(
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
+
+ \note Buffer load/store is only guaranteed to be available within a compute
+ pipeline. While some backends may support using these resources in a
+ graphics pipeline as well, this is not universally supported, and even when
+ it is, unexpected problems may arise when it comes to barriers and
+ synchronization. Therefore, avoid using such resources with shaders other
+ than compute.
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoadStore(
- int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size)
+ int binding, StageFlags stage, QRhiBuffer *buf, quint32 offset, quint32 size)
{
Q_ASSERT(size > 0);
QRhiShaderResourceBinding b;
@@ -3896,8 +6144,8 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoadStore(
*/
bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) noexcept
{
- const QRhiShaderResourceBinding::Data *da = a.data();
- const QRhiShaderResourceBinding::Data *db = b.data();
+ const QRhiShaderResourceBinding::Data *da = QRhiImplementation::shaderResourceBindingData(a);
+ const QRhiShaderResourceBinding::Data *db = QRhiImplementation::shaderResourceBindingData(b);
if (da == db)
return true;
@@ -3962,8 +6210,7 @@ bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBind
}
break;
default:
- Q_UNREACHABLE();
- return false;
+ Q_UNREACHABLE_RETURN(false);
}
return true;
@@ -3987,41 +6234,44 @@ bool operator!=(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBind
*/
size_t qHash(const QRhiShaderResourceBinding &b, size_t seed) noexcept
{
- const QRhiShaderResourceBinding::Data *d = b.data();
- size_t h = uint(d->binding) ^ uint(d->stage) ^ uint(d->type) ^ seed;
+ const QRhiShaderResourceBinding::Data *d = QRhiImplementation::shaderResourceBindingData(b);
+ QtPrivate::QHashCombine hash;
+ seed = hash(seed, d->binding);
+ seed = hash(seed, d->stage);
+ seed = hash(seed, d->type);
switch (d->type) {
case QRhiShaderResourceBinding::UniformBuffer:
- h ^= qHash(reinterpret_cast<quintptr>(d->u.ubuf.buf));
+ seed = hash(seed, reinterpret_cast<quintptr>(d->u.ubuf.buf));
break;
case QRhiShaderResourceBinding::SampledTexture:
- h ^= qHash(reinterpret_cast<quintptr>(d->u.stex.texSamplers[0].tex));
- h ^= qHash(reinterpret_cast<quintptr>(d->u.stex.texSamplers[0].sampler));
+ seed = hash(seed, reinterpret_cast<quintptr>(d->u.stex.texSamplers[0].tex));
+ seed = hash(seed, reinterpret_cast<quintptr>(d->u.stex.texSamplers[0].sampler));
break;
case QRhiShaderResourceBinding::Texture:
- h ^= qHash(reinterpret_cast<quintptr>(d->u.stex.texSamplers[0].tex));
+ seed = hash(seed, reinterpret_cast<quintptr>(d->u.stex.texSamplers[0].tex));
break;
case QRhiShaderResourceBinding::Sampler:
- h ^= qHash(reinterpret_cast<quintptr>(d->u.stex.texSamplers[0].sampler));
+ seed = hash(seed, reinterpret_cast<quintptr>(d->u.stex.texSamplers[0].sampler));
break;
case QRhiShaderResourceBinding::ImageLoad:
case QRhiShaderResourceBinding::ImageStore:
case QRhiShaderResourceBinding::ImageLoadStore:
- h ^= qHash(reinterpret_cast<quintptr>(d->u.simage.tex));
+ seed = hash(seed, reinterpret_cast<quintptr>(d->u.simage.tex));
break;
case QRhiShaderResourceBinding::BufferLoad:
case QRhiShaderResourceBinding::BufferStore:
case QRhiShaderResourceBinding::BufferLoadStore:
- h ^= qHash(reinterpret_cast<quintptr>(d->u.sbuf.buf));
+ seed = hash(seed, reinterpret_cast<quintptr>(d->u.sbuf.buf));
break;
}
- return h;
+ return seed;
}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QRhiShaderResourceBinding &b)
{
QDebugStateSaver saver(dbg);
- const QRhiShaderResourceBinding::Data *d = b.data();
+ const QRhiShaderResourceBinding::Data *d = QRhiImplementation::shaderResourceBindingData(b);
dbg.nospace() << "QRhiShaderResourceBinding("
<< "binding=" << d->binding
<< " stage=" << d->stage
@@ -4116,18 +6366,44 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
/*!
\class QRhiGraphicsPipeline
- \internal
\inmodule QtGui
+ \since 6.6
\brief Graphics pipeline state resource.
+ Represents a graphics pipeline. What exactly this map to in the underlying
+ native graphics API, varies. Where there is a concept of pipeline objects,
+ for example with Vulkan, the QRhi backend will create such an object upon
+ calling create(). Elsewhere, for example with OpenGL, the
+ QRhiGraphicsPipeline may merely collect the various state, and create()'s
+ main task is to set up the corresponding shader program, but deferring
+ looking at any of the requested state to a later point.
+
+ As with all QRhiResource subclasses, the two-phased initialization pattern
+ applies: setting any values via the setters, for example setDepthTest(), is
+ only effective after calling create(). Avoid changing any values once the
+ QRhiGraphicsPipeline has been initialized via create(). To change some
+ state, set the new value and call create() again. However, that will
+ effectively release all underlying native resources and create new ones. As
+ a result, it may be a heavy, expensive operation. Rather, prefer creating
+ multiple pipelines with the different states, and
+ \l{QRhiCommandBuffer::setGraphicsPipeline()}{switch between them} when
+ recording the render pass.
+
\note Setting the shader stages is mandatory. There must be at least one
stage, and there must be a vertex stage.
\note Setting the shader resource bindings is mandatory. The referenced
QRhiShaderResourceBindings must already have create() called on it by the
time create() is called. Associating with a QRhiShaderResourceBindings that
- has no bindings is also valid, as long as no shader in any stage expects
- any resources.
+ has no bindings is also valid, as long as no shader in any stage expects any
+ resources. Using a QRhiShaderResourceBindings object that does not specify
+ any actual resources (i.e., the buffers, textures, etc. for the binding
+ points are set to \nullptr) is valid as well, as long as a
+ \l{QRhiShaderResourceBindings::isLayoutCompatible()}{layout-compatible}
+ QRhiShaderResourceBindings, that specifies resources for all the bindings,
+ is going to be set via
+ \l{QRhiCommandBuffer::setShaderResources()}{setShaderResources()} when
+ recording the render pass.
\note Setting the render pass descriptor is mandatory. To obtain a
QRhiRenderPassDescriptor that can be passed to setRenderPassDescriptor(),
@@ -4140,20 +6416,49 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
render target's color and depth stencil attachments.
\note The depth test, depth write, and stencil test are disabled by
- default.
+ default. The face culling mode defaults to no culling.
\note stencilReadMask() and stencilWriteMask() apply to both faces. They
both default to 0xFF.
- */
-/*!
- \fn void QRhiGraphicsPipeline::setTargetBlends(const QList<TargetBlend> &blends)
+ \section2 Example usage
+
+ All settings of a graphics pipeline have defaults which might be suitable
+ to many applications. Therefore a minimal example of creating a graphics
+ pipeline could be the following. This assumes that the vertex shader takes
+ a single \c{vec3 position} input at the input location 0. With the
+ QRhiShaderResourceBindings and QRhiRenderPassDescriptor objects, plus the
+ QShader collections for the vertex and fragment stages, a pipeline could be
+ created like this:
- Sets the blend specification for color attachments. Each element in \a
- blends corresponds to a color attachment of the render target.
+ \code
+ QRhiShaderResourceBindings *srb;
+ QRhiRenderPassDescriptor *rpDesc;
+ QShader vs, fs;
+ // ...
+
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({ { 3 * sizeof(float) } });
+ inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 } });
+
+ QRhiGraphicsPipeline *ps = rhi->newGraphicsPipeline();
+ ps->setShaderStages({ { QRhiShaderStage::Vertex, vs }, { QRhiShaderStage::Fragment, fs } });
+ ps->setVertexInputLayout(inputLayout);
+ ps->setShaderResourceBindings(srb);
+ ps->setRenderPassDescriptor(rpDesc);
+ if (!ps->create()) { error(); }
+ \endcode
- By default no blends are set, which is a shortcut to disabling blending and
- enabling color write for all four channels.
+ The above code creates a pipeline object that uses the defaults for many
+ settings and states. For example, it will use a \l Triangles topology, no
+ backface culling, blending is disabled but color write is enabled for all
+ four channels, depth test/write are disabled, stencil operations are
+ disabled.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
+ \sa QRhiCommandBuffer, QRhi
*/
/*!
@@ -4312,21 +6617,85 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
*/
/*!
- \class QRhiGraphicsPipeline::TargetBlend
- \internal
+ \struct QRhiGraphicsPipeline::TargetBlend
\inmodule QtGui
+ \since 6.6
\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.
+ OneMinusSrcAlpha) by default. This means that to get the alpha blending
+ mode Qt Quick uses, it is enough to set the \c enable flag to true while
+ leaving other values at their defaults.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
- \class QRhiGraphicsPipeline::StencilOpState
- \internal
+ \variable QRhiGraphicsPipeline::TargetBlend::colorWrite
+ */
+
+/*!
+ \variable QRhiGraphicsPipeline::TargetBlend::enable
+ */
+
+/*!
+ \variable QRhiGraphicsPipeline::TargetBlend::srcColor
+ */
+
+/*!
+ \variable QRhiGraphicsPipeline::TargetBlend::dstColor
+ */
+
+/*!
+ \variable QRhiGraphicsPipeline::TargetBlend::opColor
+ */
+
+/*!
+ \variable QRhiGraphicsPipeline::TargetBlend::srcAlpha
+ */
+
+/*!
+ \variable QRhiGraphicsPipeline::TargetBlend::dstAlpha
+ */
+
+/*!
+ \variable QRhiGraphicsPipeline::TargetBlend::opAlpha
+ */
+
+/*!
+ \struct QRhiGraphicsPipeline::StencilOpState
\inmodule QtGui
+ \since 6.6
\brief Describes the stencil operation state.
+
+ The default-constructed StencilOpState has the following set:
+ \list
+ \li failOp - \l Keep
+ \li depthFailOp - \l Keep
+ \li passOp - \l Keep
+ \li compareOp \l Always
+ \endlist
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+ */
+
+/*!
+ \variable QRhiGraphicsPipeline::StencilOpState::failOp
+ */
+
+/*!
+ \variable QRhiGraphicsPipeline::StencilOpState::depthFailOp
+ */
+
+/*!
+ \variable QRhiGraphicsPipeline::StencilOpState::passOp
+ */
+
+/*!
+ \variable QRhiGraphicsPipeline::StencilOpState::compareOp
*/
/*!
@@ -4346,7 +6715,7 @@ QRhiResource::Type QRhiGraphicsPipeline::resourceType() const
}
/*!
- \fn bool QRhiGraphicsPipeline::create()
+ \fn virtual bool QRhiGraphicsPipeline::create() = 0
Creates the corresponding native graphics resources. If there are already
resources present due to an earlier create() with no corresponding
@@ -4354,23 +6723,132 @@ QRhiResource::Type QRhiGraphicsPipeline::resourceType() const
\return \c true when successful, \c false when a graphics operation failed.
Regardless of the return value, calling destroy() is always safe.
+
+ \note This may be, depending on the underlying graphics API, an expensive
+ operation, especially when shaders get compiled/optimized from source or
+ from an intermediate bytecode format to the GPU's own instruction set.
+ Where applicable, the QRhi backend automatically sets up the relevant
+ non-persistent facilities to accelerate this, for example the Vulkan
+ backend automatically creates a \c VkPipelineCache to improve data reuse
+ during the lifetime of the application.
+
+ \note Drivers may also employ various persistent (disk-based) caching
+ strategies for shader and pipeline data, which is hidden to and is outside
+ of Qt's control. In some cases, depending on the graphics API and the QRhi
+ backend, there are facilities within QRhi for manually managing such a
+ cache, allowing the retrieval of a serializable blob that can then be
+ reloaded in the future runs of the application to ensure faster pipeline
+ creation times. See QRhi::pipelineCacheData() and
+ QRhi::setPipelineCacheData() for details. Note also that when working with
+ a QRhi instance managed by a higher level Qt framework, such as Qt Quick,
+ it is possible that such disk-based caching is taken care of automatically,
+ for example QQuickWindow uses a disk-based pipeline cache by default (which
+ comes in addition to any driver-level caching).
+ */
+
+/*!
+ \fn QRhiGraphicsPipeline::Flags QRhiGraphicsPipeline::flags() const
+ \return the currently set flags.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setFlags(Flags f)
+ Sets the flags \a f.
+ */
+
+/*!
+ \fn QRhiGraphicsPipeline::Topology QRhiGraphicsPipeline::topology() const
+ \return the currently set primitive topology.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setTopology(Topology t)
+ Sets the primitive topology \a t.
+ */
+
+/*!
+ \fn QRhiGraphicsPipeline::CullMode QRhiGraphicsPipeline::cullMode() const
+ \return the currently set face culling mode.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setCullMode(CullMode mode)
+ Sets the specified face culling \a mode.
+ */
+
+/*!
+ \fn QRhiGraphicsPipeline::FrontFace QRhiGraphicsPipeline::frontFace() const
+ \return the currently set front face mode.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setFrontFace(FrontFace f)
+ Sets the front face mode \a f.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setTargetBlends(std::initializer_list<TargetBlend> list)
+
+ Sets the \a list of render target blend settings. This is a list because
+ when multiple render targets are used (i.e., a QRhiTextureRenderTarget with
+ more than one QRhiColorAttachment), there needs to be a TargetBlend
+ structure per render target (color attachment).
+
+ By default there is one default-constructed TargetBlend set.
+
+ \sa QRhi::MaxColorAttachments
+ */
+
+/*!
+ \fn template<typename InputIterator> void QRhiGraphicsPipeline::setTargetBlends(InputIterator first, InputIterator last)
+ Sets the list of render target blend settings from the iterators \a first and \a last.
+ */
+
+/*!
+ \fn const QRhiGraphicsPipeline::TargetBlend *QRhiGraphicsPipeline::cbeginTargetBlends() const
+ \return a const iterator pointing to the first item in the render target blend setting list.
+ */
+
+/*!
+ \fn const QRhiGraphicsPipeline::TargetBlend *QRhiGraphicsPipeline::cendTargetBlends() const
+ \return a const iterator pointing just after the last item in the render target blend setting list.
+ */
+
+/*!
+ \fn const QRhiGraphicsPipeline::TargetBlend *QRhiGraphicsPipeline::targetBlendAt(qsizetype index) const
+ \return the render target blend setting at the specified \a index.
+ */
+
+/*!
+ \fn qsizetype QRhiGraphicsPipeline::targetBlendCount() const
+ \return the number of render target blend settings.
+ */
+
+/*!
+ \fn bool QRhiGraphicsPipeline::hasDepthTest() const
+ \return true if depth testing is enabled.
*/
/*!
\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.
+ Enables or disables depth testing based on \a enable. Both depth test and
+ the writing out of depth data are disabled by default.
\sa setDepthWrite()
*/
/*!
+ \fn bool QRhiGraphicsPipeline::hasDepthWrite() const
+ \return true if depth write is enabled.
+ */
+
+/*!
\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.
+ Controls the writing out of depth data into the depth buffer based on
+ \a enable. 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.
@@ -4379,9 +6857,233 @@ QRhiResource::Type QRhiGraphicsPipeline::resourceType() const
*/
/*!
+ \fn QRhiGraphicsPipeline::CompareOp QRhiGraphicsPipeline::depthOp() const
+ \return the depth comparison function.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setDepthOp(CompareOp op)
+ Sets the depth comparison function \a op.
+ */
+
+/*!
+ \fn bool QRhiGraphicsPipeline::hasStencilTest() const
+ \return true if stencil testing is enabled.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setStencilTest(bool enable)
+ Enables or disables stencil tests based on \a enable.
+ By default this is disabled.
+ */
+
+/*!
+ \fn QRhiGraphicsPipeline::StencilOpState QRhiGraphicsPipeline::stencilFront() const
+ \return the current stencil test state for front faces.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setStencilFront(const StencilOpState &state)
+ Sets the stencil test \a state for front faces.
+ */
+
+/*!
+ \fn QRhiGraphicsPipeline::StencilOpState QRhiGraphicsPipeline::stencilBack() const
+ \return the current stencil test state for back faces.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setStencilBack(const StencilOpState &state)
+ Sets the stencil test \a state for back faces.
+ */
+
+/*!
+ \fn quint32 QRhiGraphicsPipeline::stencilReadMask() const
+ \return the currrent stencil read mask.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setStencilReadMask(quint32 mask)
+ Sets the stencil read \a mask. The default value is 0xFF.
+ */
+
+/*!
+ \fn quint32 QRhiGraphicsPipeline::stencilWriteMask() const
+ \return the current stencil write mask.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setStencilWriteMask(quint32 mask)
+ Sets the stencil write \a mask. The default value is 0xFF.
+ */
+
+/*!
+ \fn int QRhiGraphicsPipeline::sampleCount() const
+ \return the currently set sample count. 1 means no multisample antialiasing.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setSampleCount(int s)
+
+ Sets the sample count. Typical values for \a s are 1, 4, or 8. The pipeline
+ must always be compatible with the render target, i.e. the sample counts
+ must match.
+
+ \sa QRhi::supportedSampleCounts()
+ */
+
+/*!
+ \fn float QRhiGraphicsPipeline::lineWidth() const
+ \return the currently set line width. The default is 1.0f.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setLineWidth(float width)
+
+ Sets the line \a width. If the QRhi::WideLines feature is reported as
+ unsupported at runtime, values other than 1.0f are ignored.
+ */
+
+/*!
+ \fn int QRhiGraphicsPipeline::depthBias() const
+ \return the currently set depth bias.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setDepthBias(int bias)
+ Sets the depth \a bias. The default value is 0.
+ */
+
+/*!
+ \fn float QRhiGraphicsPipeline::slopeScaledDepthBias() const
+ \return the currently set slope scaled depth bias.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setSlopeScaledDepthBias(float bias)
+ Sets the slope scaled depth \a bias. The default value is 0.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setShaderStages(std::initializer_list<QRhiShaderStage> list)
+ Sets the \a list of shader stages.
+ */
+
+/*!
+ \fn template<typename InputIterator> void QRhiGraphicsPipeline::setShaderStages(InputIterator first, InputIterator last)
+ Sets the list of shader stages from the iterators \a first and \a last.
+ */
+
+/*!
+ \fn const QRhiShaderStage *QRhiGraphicsPipeline::cbeginShaderStages() const
+ \return a const iterator pointing to the first item in the shader stage list.
+ */
+
+/*!
+ \fn const QRhiShaderStage *QRhiGraphicsPipeline::cendShaderStages() const
+ \return a const iterator pointing just after the last item in the shader stage list.
+ */
+
+/*!
+ \fn const QRhiShaderStage *QRhiGraphicsPipeline::shaderStageAt(qsizetype index) const
+ \return the shader stage at the specified \a index.
+ */
+
+/*!
+ \fn qsizetype QRhiGraphicsPipeline::shaderStageCount() const
+ \return the number of shader stages in this pipeline.
+ */
+
+/*!
+ \fn QRhiVertexInputLayout QRhiGraphicsPipeline::vertexInputLayout() const
+ \return the currently set vertex input layout specification.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setVertexInputLayout(const QRhiVertexInputLayout &layout)
+ Specifies the vertex input \a layout.
+ */
+
+/*!
+ \fn QRhiShaderResourceBindings *QRhiGraphicsPipeline::shaderResourceBindings() const
+ \return the currently associated QRhiShaderResourceBindings object.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setShaderResourceBindings(QRhiShaderResourceBindings *srb)
+
+ Associates with \a srb describing the resource binding layout and the
+ resources (QRhiBuffer, QRhiTexture) themselves. The latter is optional,
+ because only the layout matters during pipeline creation. Therefore, the \a
+ srb passed in here can leave the actual buffer or texture objects
+ unspecified (\nullptr) as long as there is another,
+ \l{QRhiShaderResourceBindings::isLayoutCompatible()}{layout-compatible}
+ QRhiShaderResourceBindings bound via
+ \l{QRhiCommandBuffer::setShaderResources()}{setShaderResources()} before
+ recording the draw calls.
+ */
+
+/*!
+ \fn QRhiRenderPassDescriptor *QRhiGraphicsPipeline::renderPassDescriptor() const
+ \return the currently set QRhiRenderPassDescriptor.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setRenderPassDescriptor(QRhiRenderPassDescriptor *desc)
+ Associates with the specified QRhiRenderPassDescriptor \a desc.
+ */
+
+/*!
+ \fn int QRhiGraphicsPipeline::patchControlPointCount() const
+ \return the currently set patch control point count.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setPatchControlPointCount(int count)
+
+ Sets the number of patch control points to \a count. The default value is
+ 3. This is used only when the topology is set to \l Patches.
+ */
+
+/*!
+ \fn QRhiGraphicsPipeline::PolygonMode QRhiGraphicsPipeline::polygonMode() const
+ \return the polygon mode.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setPolygonMode(PolygonMode mode)
+ Sets the polygon \a mode. The default is Fill.
+
+ \sa QRhi::NonFillPolygonMode
+ */
+
+/*!
+ \fn int QRhiGraphicsPipeline::multiViewCount() const
+ \return the view count. The default is 0, indicating no multiview rendering.
+ \since 6.7
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setMultiViewCount(int count)
+ Sets the view \a count for multiview rendering. The default is 0,
+ indicating no multiview rendering.
+ \a count must be 2 or larger to trigger multiview rendering.
+
+ Multiview is only available when the \l{QRhi::MultiView}{MultiView feature}
+ is reported as supported. The render target must be a 2D texture array, and
+ the color attachment for the render target must have the same \a count set.
+
+ See QRhiColorAttachment::setMultiViewCount() for further details on
+ multiview rendering.
+
+ \since 6.7
+ \sa QRhi::MultiView, QRhiColorAttachment::setMultiViewCount()
+ */
+
+/*!
\class QRhiSwapChain
- \internal
\inmodule QtGui
+ \since 6.6
\brief Swapchain resource.
A swapchain enables presenting rendering results to a surface. A swapchain
@@ -4391,7 +7093,7 @@ QRhiResource::Type QRhiGraphicsPipeline::resourceType() const
Below is a typical pattern for creating and managing a swapchain and some
associated resources in order to render onto a QWindow:
- \badcode
+ \code
void init()
{
sc = rhi->newSwapChain();
@@ -4437,7 +7139,7 @@ QRhiResource::Type QRhiGraphicsPipeline::resourceType() const
Releasing the swapchain must happen while the QWindow and the underlying
native window is fully up and running. Building on the previous example:
- \badcode
+ \code
void releaseSwapChain()
{
if (hasSwapChain) {
@@ -4469,7 +7171,7 @@ QRhiResource::Type QRhiGraphicsPipeline::resourceType() const
events. QExposeEvent is a loosely specified event that is sent whenever a
window gets mapped, obscured, and resized, depending on the platform.
- \badcode
+ \code
void Window::exposeEvent(QExposeEvent *)
{
// initialize and start rendering when the window becomes usable for graphics purposes
@@ -4515,6 +7217,9 @@ QRhiResource::Type QRhiGraphicsPipeline::resourceType() const
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.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
@@ -4573,6 +7278,14 @@ QRhiResource::Type QRhiGraphicsPipeline::resourceType() const
\enum QRhiSwapChain::Format
Describes the swapchain format. The default format is SDR.
+ This enum is used with
+ \l{QRhiSwapChain::isFormatSupported()}{isFormatSupported()} to check
+ upfront if creating the swapchain with the given format is supported by the
+ platform and the window's associated screen, and with
+ \l{QRhiSwapChain::setFormat()}{setFormat()}
+ to set the requested format in the swapchain before calling
+ \l{QRhiSwapChain::createOrResize()}{createOrResize()} for the first time.
+
\value SDR 8-bit RGBA or BGRA, depending on the backend and platform. With
OpenGL ES in particular, it could happen that the platform provides less
than 8 bits (e.g. due to EGL and the QSurfaceFormat choosing a 565 or 444
@@ -4584,10 +7297,14 @@ QRhiResource::Type QRhiGraphicsPipeline::resourceType() const
(same as SDR/sRGB) and linear colors. Conversion to the display's native
color space (such as, HDR10) is performed by the windowing system. On
Windows this is the canonical color space of the system compositor, and is
- the recommended format for HDR swapchains in general.
+ the recommended format for HDR swapchains in general on desktop platforms.
\value HDR10 10-bit unsigned int RGB or BGR with 2 bit alpha, high dynamic
range, HDR10 (Rec. 2020) color space with an ST2084 PQ transfer function.
+
+ \value HDRExtendedDisplayP3Linear 16-bit float RGBA, high dynamic range,
+ extended linear Display P3 color space. The primary choice for HDR on
+ platforms such as iOS and VisionOS.
*/
/*!
@@ -4633,7 +7350,7 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
*/
/*!
- \fn QSize QRhiSwapChain::surfacePixelSize()
+ \fn virtual QSize QRhiSwapChain::surfacePixelSize() = 0
\return The size of the window's associated surface or layer.
@@ -4641,13 +7358,13 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
QWindow::devicePixelRatio()}. With some graphics APIs and windowing system
interfaces (for example, Vulkan) there is a theoretical possibility for a
surface to assume a size different from the associated window. To support
- these cases, rendering logic must always base size-derived calculations
+ these cases, \b{rendering logic must always base size-derived calculations
(such as, viewports) on the size reported from QRhiSwapChain, and never on
- the size queried from QWindow.
+ the size queried from QWindow}.
- \note Can also be called before createOrResize(), if at least window() is
- already set) This in combination with currentPixelSize() allows to detect
- when a swapchain needs to be resized. However, watch out for the fact that
+ \note \b{Can also be called before createOrResize(), if at least window() is
+ already set. This in combination with currentPixelSize() allows to detect
+ when a swapchain needs to be resized.} However, watch out for the fact that
the size of the underlying native object (surface, layer, or similar) is
"live", so whenever this function is called, it returns the latest value
reported by the underlying implementation, without any atomicity guarantee.
@@ -4668,9 +7385,9 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
*/
/*!
- \fn bool QRhiSwapChain::isFormatSuported(Format f)
+ \fn virtual bool QRhiSwapChain::isFormatSupported(Format f) = 0
- \return true if the given swapchain format is supported. SDR is always
+ \return true if the given swapchain format \a f is supported. SDR is always
supported.
\note Can be called independently of createOrResize(), but window() must
@@ -4681,20 +7398,45 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
time. If the result is true for a HDR format, then creating the swapchain
with that format is expected to succeed as long as the window is not moved
to another screen in the meantime.
+
+ The main use of this function is to call it before the first
+ createOrResize() after the window is already set. This allow the QRhi
+ backends to perform platform or windowing system specific queries to
+ determine if the window (and the screen it is on) is capable of true HDR
+ output with the specified format.
+
+ When the format is reported as supported, call setFormat() to set the
+ requested format and call createOrResize(). Be aware of the consequences
+ however: successfully requesting a HDR format will involve having to deal
+ with a different color space, possibly doing white level correction for
+ non-HDR-aware content, adjusting tonemapping methods, adjusting offscreen
+ render target settings, etc.
+
+ \sa setFormat()
*/
/*!
- \fn QRhiCommandBuffer *QRhiSwapChain::currentFrameCommandBuffer()
+ \fn virtual QRhiCommandBuffer *QRhiSwapChain::currentFrameCommandBuffer() = 0
- \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.
+ \return a command buffer on which rendering commands and resource updates
+ can be recorded within a \l{QRhi::beginFrame()}{beginFrame} -
+ \l{QRhi::endFrame()}{endFrame} block, assuming beginFrame() was called with
+ this swapchain.
- \note the value must not be cached and reused between frames
+ \note The returned object is valid also after endFrame(), up until the next
+ beginFrame(), but the returned command buffer should not be used to record
+ any commands then. Rather, it can be used to query data collected during
+ the frame (or previous frames), for example by calling
+ \l{QRhiCommandBuffer::lastCompletedGpuTime()}{lastCompletedGpuTime()}.
+
+ \note The value must not be cached and reused between frames. The caller
+ should not hold on to the returned object once
+ \l{QRhi::beginFrame()}{beginFrame()} is called again. Instead, the command
+ buffer object should be queried again by calling this function.
*/
/*!
- \fn QRhiRenderTarget *QRhiSwapChain::currentFrameRenderTarget()
+ \fn virtual QRhiRenderTarget *QRhiSwapChain::currentFrameRenderTarget() = 0
\return a render target that can used with beginPass() in order to render
the swapchain's current backbuffer. Only valid within a
@@ -4705,7 +7447,35 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
*/
/*!
- \fn bool QRhiSwapChain::createOrResize()
+ \enum QRhiSwapChain::StereoTargetBuffer
+ Selects the backbuffer to use with a stereoscopic swapchain.
+
+ \value LeftBuffer
+ \value RightBuffer
+ */
+
+/*!
+ \return a render target that can be used with beginPass() in order to
+ render to the swapchain's left or right backbuffer. This overload should be
+ used only with stereoscopic rendering, that is, when the associated QWindow
+ is backed by two color buffers, one for each eye, instead of just one.
+
+ When stereoscopic rendering is not supported, the return value will be
+ the default target. It is supported by all hardware backends except for Metal, in
+ combination with \l QSurfaceFormat::StereoBuffers, assuming it is supported
+ by the graphics and display driver stack at run time. Metal and Null backends
+ are going to return the default render target from this overload.
+
+ \note the value must not be cached and reused between frames
+ */
+QRhiRenderTarget *QRhiSwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer)
+{
+ Q_UNUSED(targetBuffer);
+ return currentFrameRenderTarget();
+}
+
+/*!
+ \fn virtual bool QRhiSwapChain::createOrResize() = 0
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
@@ -4721,18 +7491,121 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
*/
/*!
+ \fn QWindow *QRhiSwapChain::window() const
+ \return the currently set window.
+ */
+
+/*!
+ \fn void QRhiSwapChain::setWindow(QWindow *window)
+ Sets the \a window.
+ */
+
+/*!
+ \fn QRhiSwapChainProxyData QRhiSwapChain::proxyData() const
+ \return the currently set proxy data.
+ */
+
+/*!
+ \fn void QRhiSwapChain::setProxyData(const QRhiSwapChainProxyData &d)
+ Sets the proxy data \a d.
+
+ \sa QRhi::updateSwapChainProxyData()
+ */
+
+/*!
+ \fn QRhiSwapChain::Flags QRhiSwapChain::flags() const
+ \return the currently set flags.
+ */
+
+/*!
+ \fn void QRhiSwapChain::setFlags(Flags f)
+ Sets the flags \a f.
+ */
+
+/*!
+ \fn QRhiSwapChain::Format QRhiSwapChain::format() const
+ \return the currently set format.
+ */
+
+/*!
+ \fn void QRhiSwapChain::setFormat(Format f)
+ Sets the format \a f.
+
+ Avoid setting formats that are reported as unsupported from
+ isFormatSupported(). Note that support for a given format may depend on the
+ screen the swapchain's associated window is opened on. On some platforms,
+ such as Windows and macOS, for HDR output to work it is necessary to have
+ HDR output enabled in the display settings.
+
+ See isFormatSupported(), \l QRhiSwapChainHdrInfo, and \l Format for more
+ information on high dynamic range output.
+ */
+
+/*!
+ \fn QRhiRenderBuffer *QRhiSwapChain::depthStencil() const
+ \return the currently associated renderbuffer for depth-stencil.
+ */
+
+/*!
+ \fn void QRhiSwapChain::setDepthStencil(QRhiRenderBuffer *ds)
+ Sets the renderbuffer \a ds for use as a depth-stencil buffer.
+ */
+
+/*!
+ \fn int QRhiSwapChain::sampleCount() const
+ \return the currently set sample count. 1 means no multisample antialiasing.
+ */
+
+/*!
+ \fn void QRhiSwapChain::setSampleCount(int samples)
+
+ Sets the sample count. Common values for \a samples are 1 (no MSAA), 4 (4x
+ MSAA), or 8 (8x MSAA).
+
+ \sa QRhi::supportedSampleCounts()
+ */
+
+/*!
+ \fn QRhiRenderPassDescriptor *QRhiSwapChain::renderPassDescriptor() const
+ \return the currently associated QRhiRenderPassDescriptor object.
+ */
+
+/*!
+ \fn void QRhiSwapChain::setRenderPassDescriptor(QRhiRenderPassDescriptor *desc)
+ Associates with the QRhiRenderPassDescriptor \a desc.
+ */
+
+/*!
+ \fn virtual QRhiRenderPassDescriptor *QRhiSwapChain::newCompatibleRenderPassDescriptor() = 0;
+
+ \return a new QRhiRenderPassDescriptor that is compatible with this swapchain.
+
+ 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 swapchain that has a
+ \l{QRhiRenderPassDescriptor::isCompatible()}{compatible}
+ QRhiRenderPassDescriptor set.
+
+ \sa createOrResize()
+ */
+
+/*!
\struct QRhiSwapChainHdrInfo
- \internal
\inmodule QtGui
+ \since 6.6
\brief Describes the high dynamic range related information of the
swapchain's associated output.
- To perform tonemapping, one often needs to know the maximum luminance of
- the display the swapchain's window is associated with. While this is often
- made user-configurable, it can be highly useful to set defaults based on
- the values reported by the display itself, thus providing a decent starting
- point.
+ To perform HDR-compatible tonemapping, where the target range is not [0,1],
+ one often needs to know the maximum luminance of the display the
+ swapchain's window is associated with. While this is often made
+ user-configurable (think brightness, gamma and similar settings in games),
+ it can be highly useful to set defaults based on the values reported by the
+ display itself, thus providing a decent starting point.
There are some problems however: the information is exposed in different
forms on different platforms, whereas with cross-platform graphics APIs
@@ -4740,11 +7613,6 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
information is not in the scope of the API (and may rather be retrievable
via other platform-specific means, if any).
- The struct returned from QRhiSwapChain::hdrInfo() contains either some
- hard-coded defaults, indicated by the \c isHardCodedDefaults field, or real
- values received from an API such as DXGI (IDXGIOutput6) or Cocoa
- (NSScreen). The default is 1000 nits for maximum luminance.
-
With Metal on macOS/iOS, there is no luminance values exposed in the
platform APIs. Instead, the maximum color component value, that would be
1.0 in a non-HDR setup, is provided. The \c limitsType field indicates what
@@ -4753,17 +7621,168 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
fit.
With an API like Vulkan, where there is no way to get such information, the
- values are always the built-in defaults and \c isHardCodedDefaults is
- always true.
+ values are always the built-in defaults.
+
+ Therefore, the struct returned from QRhiSwapChain::hdrInfo() contains
+ either some hard-coded defaults or real values received from an API such as
+ DXGI (IDXGIOutput6) or Cocoa (NSScreen). When no platform queries are
+ available (or needs using platform facilities out of scope for QRhi), the
+ hard-coded defaults are a maximum luminance of 1000 nits and an SDR white
+ level of 200.
+
+ The struct also exposes the presumed luminance behavior of the platform and
+ its compositor, to indicate what a color component value of 1.0 is treated
+ as in a HDR color buffer. In some cases it will be necessary to perform
+ color correction of non-HDR content composited with HDR content. To enable
+ this, the SDR white level is queried from the system on some platforms
+ (Windows) and exposed here.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
+ \sa QRhiSwapChain::hdrInfo()
+ */
+
+/*!
+ \enum QRhiSwapChainHdrInfo::LimitsType
+
+ \value LuminanceInNits Indicates that the \l limits union has its
+ \c luminanceInNits struct set
+
+ \value ColorComponentValue Indicates that the \l limits union has its
+ \c colorComponentValue struct set
+*/
+
+/*!
+ \enum QRhiSwapChainHdrInfo::LuminanceBehavior
+
+ \value SceneReferred Indicates that the color value of 1.0 is interpreted
+ as 80 nits. This is the behavior of HDR-enabled windows with the Windows
+ compositor. See
+ \l{https://learn.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range}{this
+ page} for more information on HDR on Windows.
+
+ \value DisplayReferred Indicates that the color value of 1.0 is interpreted
+ as the value of the SDR white. (which can be e.g. 200 nits, but will vary
+ depending on screen brightness) This is the behavior of HDR-enabled windows
+ on Apple platforms. See
+ \l{https://developer.apple.com/documentation/metal/hdr_content/displaying_hdr_content_in_a_metal_layer}{this
+ page} for more information on Apple's EDR system.
+*/
+
+/*!
+ \variable QRhiSwapChainHdrInfo::limitsType
+
+ With Metal on macOS/iOS, there is no luminance values exposed in the
+ platform APIs. Instead, the maximum color component value, that would be
+ 1.0 in a non-HDR setup, is provided. This value indicates what kind of
+ information is available in \l limits.
+
+ \sa QRhiSwapChain::hdrInfo()
+*/
+
+/*!
+ \variable QRhiSwapChainHdrInfo::limits
+
+ Contains the actual values queried from the graphics API or the platform.
+ The type of data is indicated by \l limitsType. This is therefore a union.
+ There are currently two options:
+
+ Luminance values in nits:
+
+ \code
+ struct {
+ float minLuminance;
+ float maxLuminance;
+ } luminanceInNits;
+ \endcode
+
+ On Windows the minimum and maximum luminance depends on the screen
+ brightness. While not relevant for desktops, on laptops the screen
+ brightness may change at any time. Increasing brightness implies decreased
+ maximum luminance. In addition, the results may also be dependent on the
+ HDR Content Brightness set in Windows Settings' System/Display/HDR view,
+ if there is such a setting.
+
+ Note however that the changes made to the laptop screen's brightness or in
+ the system settings while the application is running are not necessarily
+ reflected in the returned values, meaning calling hdrInfo() again may still
+ return the same luminance range as before for the rest of the process'
+ lifetime. The exact behavior is up to DXGI and Qt has no control over it.
+
+ \note The Windows compositor works in scene-referred mode for HDR content.
+ A color component value of 1.0 corresponds to a luminance of 80 nits. When
+ rendering non-HDR content (e.g. 2D UI elements), the correction of the
+ white level is often necessary. (e.g., outputting the fragment color (1, 1,
+ 1) will likely lead to showing a shade of white that is too dim on-screen)
+ See \l sdrWhiteLevel.
+
+ For macOS/iOS, the current maximum and potential maximum color
+ component values are provided:
+
+ \code
+ struct {
+ float maxColorComponentValue;
+ float maxPotentialColorComponentValue;
+ } colorComponentValue;
+ \endcode
+
+ The value may depend on the screen brightness, which on laptops means that
+ the result may change in the next call to hdrInfo() if the brightness was
+ changed in the meantime. The maximum screen brightness implies a maximum
+ color value of 1.0.
+
+ \note Apple's EDR is display-referred. 1.0 corresponds to a luminance level
+ of SDR white (e.g. 200 nits), the value of which varies based on the screen
+ brightness and possibly other settings. The exact luminance value for that,
+ or the maximum luminance of the display, are not exposed to the
+ applications.
+
+ \note It has been observed that the color component values are not set to
+ the correct larger-than-1 value right away on startup on some macOS
+ systems, but the values tend to change during or after the first frame.
\sa QRhiSwapChain::hdrInfo()
+*/
+
+/*!
+ \variable QRhiSwapChainHdrInfo::luminanceBehavior
+
+ Describes the platform's presumed behavior with regards to color values.
+
+ \sa sdrWhiteLevel
*/
/*!
+ \variable QRhiSwapChainHdrInfo::sdrWhiteLevel
+
+ On Windows this is the dynamic SDR white level in nits. The value is
+ dependent on the screen brightness (on laptops), and the SDR or HDR Content
+ Brightness settings in the Windows settings' System/Display/HDR view.
+
+ To perform white level correction for non-HDR (SDR) content, such as 2D UI
+ elemenents, multiply the final color with sdrWhiteLevel / 80.0 whenever
+ \l luminanceBehavior is SceneReferred. (assuming Windows and a linear
+ extended sRGB (scRGB) color space)
+
+ On other platforms the value is always a pre-defined value, 200. This may
+ not match the system's actual SDR white level, but the value of this
+ variable is not relevant in practice when the \l luminanceBehavior is
+ DisplayReferred, because then the color component value of 1.0 refers to
+ the SDR white by default.
+
+ \sa luminanceBehavior
+*/
+
+/*!
\return the HDR information for the associated display.
- The returned struct is always the default one if createOrResize() has not
- been successfully called yet.
+ Do not assume that this is a cheap operation. Depending on the platform,
+ this function makes various platform queries which may have a performance
+ impact.
+
+ \note Can be called before createOrResize() as long as the window is
+ \l{setWindow()}{set}.
\note What happens when moving a window with an initialized swapchain
between displays (HDR to HDR with different characteristics, HDR to SDR,
@@ -4778,10 +7797,11 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
QRhiSwapChainHdrInfo QRhiSwapChain::hdrInfo()
{
QRhiSwapChainHdrInfo info;
- info.isHardCodedDefaults = true;
info.limitsType = QRhiSwapChainHdrInfo::LuminanceInNits;
info.limits.luminanceInNits.minLuminance = 0.0f;
info.limits.luminanceInNits.maxLuminance = 1000.0f;
+ info.luminanceBehavior = QRhiSwapChainHdrInfo::SceneReferred;
+ info.sdrWhiteLevel = 200.0f;
return info;
}
@@ -4789,7 +7809,7 @@ QRhiSwapChainHdrInfo QRhiSwapChain::hdrInfo()
QDebug operator<<(QDebug dbg, const QRhiSwapChainHdrInfo &info)
{
QDebugStateSaver saver(dbg);
- dbg.nospace() << "QRhiSwapChainHdrInfo(" << (info.isHardCodedDefaults ? "with hard-coded defaults" : "queried from system");
+ dbg.nospace() << "QRhiSwapChainHdrInfo(";
switch (info.limitsType) {
case QRhiSwapChainHdrInfo::LuminanceInNits:
dbg.nospace() << " minLuminance=" << info.limits.luminanceInNits.minLuminance
@@ -4797,6 +7817,15 @@ QDebug operator<<(QDebug dbg, const QRhiSwapChainHdrInfo &info)
break;
case QRhiSwapChainHdrInfo::ColorComponentValue:
dbg.nospace() << " maxColorComponentValue=" << info.limits.colorComponentValue.maxColorComponentValue;
+ dbg.nospace() << " maxPotentialColorComponentValue=" << info.limits.colorComponentValue.maxPotentialColorComponentValue;
+ break;
+ }
+ switch (info.luminanceBehavior) {
+ case QRhiSwapChainHdrInfo::SceneReferred:
+ dbg.nospace() << " scene-referred, SDR white level=" << info.sdrWhiteLevel;
+ break;
+ case QRhiSwapChainHdrInfo::DisplayReferred:
+ dbg.nospace() << " display-referred";
break;
}
dbg.nospace() << ')';
@@ -4806,8 +7835,8 @@ QDebug operator<<(QDebug dbg, const QRhiSwapChainHdrInfo &info)
/*!
\class QRhiComputePipeline
- \internal
\inmodule QtGui
+ \since 6.6
\brief Compute pipeline state resource.
\note Setting the shader resource bindings is mandatory. The referenced
@@ -4815,6 +7844,9 @@ QDebug operator<<(QDebug dbg, const QRhiSwapChainHdrInfo &info)
time create() is called.
\note Setting the shader is mandatory.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
@@ -4844,15 +7876,59 @@ QRhiComputePipeline::QRhiComputePipeline(QRhiImplementation *rhi)
}
/*!
+ \fn QRhiComputePipeline::Flags QRhiComputePipeline::flags() const
+ \return the currently set flags.
+ */
+
+/*!
+ \fn void QRhiComputePipeline::setFlags(Flags f)
+ Sets the flags \a f.
+ */
+
+/*!
+ \fn QRhiShaderStage QRhiComputePipeline::shaderStage() const
+ \return the currently set shader.
+ */
+
+/*!
+ \fn void QRhiComputePipeline::setShaderStage(const QRhiShaderStage &stage)
+
+ Sets the shader to use. \a stage can only refer to the
+ \l{QRhiShaderStage::Compute}{compute stage}.
+ */
+
+/*!
+ \fn QRhiShaderResourceBindings *QRhiComputePipeline::shaderResourceBindings() const
+ \return the currently associated QRhiShaderResourceBindings object.
+ */
+
+/*!
+ \fn void QRhiComputePipeline::setShaderResourceBindings(QRhiShaderResourceBindings *srb)
+
+ Associates with \a srb describing the resource binding layout and the
+ resources (QRhiBuffer, QRhiTexture) themselves. The latter is optional. As
+ with graphics pipelines, the \a srb passed in here can leave the actual
+ buffer or texture objects unspecified (\nullptr) as long as there is
+ another,
+ \l{QRhiShaderResourceBindings::isLayoutCompatible()}{layout-compatible}
+ QRhiShaderResourceBindings bound via
+ \l{QRhiCommandBuffer::setShaderResources()}{setShaderResources()} before
+ recording the dispatch call.
+ */
+
+/*!
\class QRhiCommandBuffer
- \internal
\inmodule QtGui
+ \since 6.6
\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().
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
@@ -4913,7 +7989,7 @@ QRhiResource::Type QRhiCommandBuffer::resourceType() const
return CommandBuffer;
}
-static const char *resourceTypeStr(QRhiResource *res)
+static const char *resourceTypeStr(const QRhiResource *res)
{
switch (res->resourceType()) {
case QRhiResource::Buffer:
@@ -4942,8 +8018,7 @@ static const char *resourceTypeStr(QRhiResource *res)
return "CommandBuffer";
}
- Q_UNREACHABLE();
- return "";
+ Q_UNREACHABLE_RETURN("");
}
QRhiImplementation::~QRhiImplementation()
@@ -4963,10 +8038,12 @@ QRhiImplementation::~QRhiImplementation()
if (!resources.isEmpty()) {
if (leakCheck) {
qWarning("QRhi %p going down with %d unreleased resources that own native graphics objects. This is not nice.",
- q, int(resources.count()));
+ q, int(resources.size()));
}
- for (QRhiResource *res : qAsConst(resources)) {
- if (leakCheck)
+ for (auto it = resources.cbegin(), end = resources.cend(); it != end; ++it) {
+ QRhiResource *res = it.key();
+ const bool ownsNativeResources = it.value();
+ if (leakCheck && ownsNativeResources)
qWarning(" %s resource %p (%s)", resourceTypeStr(res), res, res->m_objectName.constData());
// Null out the resource's rhi pointer. This is why it makes sense to do null
@@ -5176,6 +8253,17 @@ void QRhiImplementation::textureFormatInfo(QRhiTexture::Format format, const QSi
*bytesPerPixel = bpc;
}
+bool QRhiImplementation::isStencilSupportingFormat(QRhiTexture::Format format) const
+{
+ switch (format) {
+ case QRhiTexture::D24S8:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
bool QRhiImplementation::sanityCheckGraphicsPipeline(QRhiGraphicsPipeline *ps)
{
if (ps->cbeginShaderStages() == ps->cendShaderStages()) {
@@ -5217,7 +8305,7 @@ bool QRhiImplementation::sanityCheckShaderResourceBindings(QRhiShaderResourceBin
const int CHECKED_BINDINGS_COUNT = 64;
bool bindingSeen[CHECKED_BINDINGS_COUNT] = {};
for (auto it = srb->cbeginBindings(), end = srb->cendBindings(); it != end; ++it) {
- const int binding = it->data()->binding;
+ const int binding = shaderResourceBindingData(*it)->binding;
if (binding >= CHECKED_BINDINGS_COUNT)
continue;
if (binding < 0) {
@@ -5225,7 +8313,7 @@ bool QRhiImplementation::sanityCheckShaderResourceBindings(QRhiShaderResourceBin
bindingsOk = false;
continue;
}
- switch (it->data()->type) {
+ switch (shaderResourceBindingData(*it)->type) {
case QRhiShaderResourceBinding::UniformBuffer:
if (!bindingSeen[binding]) {
bindingSeen[binding] = true;
@@ -5279,7 +8367,7 @@ bool QRhiImplementation::sanityCheckShaderResourceBindings(QRhiShaderResourceBin
}
break;
default:
- qWarning("Unknown binding type %d", int(it->data()->type));
+ qWarning("Unknown binding type %d", int(shaderResourceBindingData(*it)->type));
bindingsOk = false;
break;
}
@@ -5295,6 +8383,41 @@ bool QRhiImplementation::sanityCheckShaderResourceBindings(QRhiShaderResourceBin
return true;
}
+int QRhiImplementation::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);
+ const QList<int> supported = supportedSampleCounts();
+ int result = 1;
+
+ // Stay compatible with Qt 5 in that requesting an unsupported sample count
+ // is not an error (although we still do a categorized debug print about
+ // this), and rather a supported value, preferably a close one, not just 1,
+ // is used instead. This is actually deviating from Qt 5 as that performs a
+ // clamping only and does not handle cases such as when sample count 2 is
+ // not supported but 4 is. (OpenGL handles things like that gracefully,
+ // other APIs may not, so improve this by picking the next largest, or in
+ // absence of that, the largest value; this with the goal to not reduce
+ // quality by rather picking a larger-than-requested value than a smaller one)
+
+ for (int i = 0, ie = supported.count(); i != ie; ++i) {
+ // assumes the 'supported' list is sorted
+ if (supported[i] >= s) {
+ result = supported[i];
+ break;
+ }
+ }
+
+ if (result != s) {
+ if (result == 1 && !supported.isEmpty())
+ result = supported.last();
+ qCDebug(QRHI_LOG_INFO, "Attempted to set unsupported sample count %d, using %d instead",
+ sampleCount, result);
+ }
+
+ return result;
+}
+
/*!
\internal
*/
@@ -5310,23 +8433,39 @@ QRhi::~QRhi()
if (!d)
return;
+ runCleanup();
+
qDeleteAll(d->pendingDeleteResources);
d->pendingDeleteResources.clear();
- runCleanup();
-
d->destroy();
delete d;
}
+void QRhiImplementation::prepareForCreate(QRhi *rhi, QRhi::Implementation impl, QRhi::Flags flags)
+{
+ q = rhi;
+
+ // Play nice with QSG_INFO since that is still the most commonly used
+ // way to get graphics info printed from Qt Quick apps, and the Quick
+ // scenegraph is our primary user.
+ if (qEnvironmentVariableIsSet("QSG_INFO"))
+ const_cast<QLoggingCategory &>(QRHI_LOG_INFO()).setEnabled(QtDebugMsg, true);
+
+ debugMarkers = flags.testFlag(QRhi::EnableDebugMarkers);
+
+ implType = impl;
+ implThread = QThread::currentThread();
+}
+
/*!
\return a new QRhi instance with a backend for the graphics API specified
by \a impl with the specified \a flags.
\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.
+ QRhiMetalInitParams, QRhiD3D11InitParams, QRhiD3D12InitParams,
+ QRhiGles2InitParams. See these classes for examples on creating a QRhi.
QRhi by design does not implement any fallback logic: if the specified API
cannot be initialized, create() will fail, with warnings printed on the
@@ -5340,6 +8479,13 @@ QRhi::~QRhi()
initialization of the infrastructure and is wasteful if that QRhi instance
is then thrown immediately away.
+ \a importDevice allows using an already existing graphics device, without
+ QRhi creating its own. When not null, this parameter must point to an
+ instance of one of the subclasses of QRhiNativeHandles:
+ QRhiVulkanNativeHandles, QRhiD3D11NativeHandles, QRhiD3D12NativeHandles,
+ QRhiMetalNativeHandles, QRhiGles2NativeHandles. The exact details and
+ semantics depend on the backand and the underlying graphics API.
+
\sa probe()
*/
QRhi *QRhi::create(Implementation impl, QRhiInitParams *params, Flags flags, QRhiNativeHandles *importDevice)
@@ -5379,7 +8525,7 @@ QRhi *QRhi::create(Implementation impl, QRhiInitParams *params, Flags flags, QRh
break;
#endif
case Metal:
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
r->d = new QRhiMetal(static_cast<QRhiMetalInitParams *>(params),
static_cast<QRhiMetalNativeHandles *>(importDevice));
break;
@@ -5387,24 +8533,29 @@ QRhi *QRhi::create(Implementation impl, QRhiInitParams *params, Flags flags, QRh
qWarning("This platform has no Metal support");
break;
#endif
+ case D3D12:
+#ifdef Q_OS_WIN
+#ifdef QRHI_D3D12_AVAILABLE
+ r->d = new QRhiD3D12(static_cast<QRhiD3D12InitParams *>(params),
+ static_cast<QRhiD3D12NativeHandles *>(importDevice));
+ break;
+#else
+ qWarning("Qt was built without Direct3D 12 support. "
+ "This is likely due to having ancient SDK headers (such as d3d12.h) in the Qt build environment. "
+ "Rebuild Qt with an SDK supporting D3D12 features introduced in Windows 10 version 1703, "
+ "or use an MSVC build as those typically are built with more up-to-date SDKs.");
+ break;
+#endif
+#else
+ qWarning("This platform has no Direct3D 12 support");
+ break;
+#endif
}
if (r->d) {
- r->d->q = r.get();
-
- // Play nice with QSG_INFO since that is still the most commonly used
- // way to get graphics info printed from Qt Quick apps, and the Quick
- // scenegraph is our primary user.
- if (qEnvironmentVariableIsSet("QSG_INFO"))
- const_cast<QLoggingCategory &>(QRHI_LOG_INFO()).setEnabled(QtDebugMsg, true);
-
- r->d->debugMarkers = flags.testFlag(EnableDebugMarkers);
-
- if (r->d->create(flags)) {
- r->d->implType = impl;
- r->d->implThread = QThread::currentThread();
+ r->d->prepareForCreate(r.get(), impl, flags);
+ if (r->d->create(flags))
return r.release();
- }
}
return nullptr;
@@ -5433,7 +8584,7 @@ bool QRhi::probe(QRhi::Implementation impl, QRhiInitParams *params)
// create() and then drop the result.
if (impl == Metal) {
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
ok = QRhiMetal::probe(static_cast<QRhiMetalInitParams *>(params));
#endif
} else {
@@ -5445,6 +8596,56 @@ bool QRhi::probe(QRhi::Implementation impl, QRhiInitParams *params)
}
/*!
+ \struct QRhiSwapChainProxyData
+ \inmodule QtGui
+ \since 6.6
+
+ \brief Opaque data describing native objects needed to set up a swapchain.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
+ \sa QRhi::updateSwapChainProxyData()
+ */
+
+/*!
+ Generates and returns a QRhiSwapChainProxyData struct containing opaque
+ data specific to the backend and graphics API specified by \a impl. \a
+ window is the QWindow a swapchain is targeting.
+
+ The returned struct can be passed to QRhiSwapChain::setProxyData(). This
+ makes sense in threaded rendering systems: this static function is expected
+ to be called on the \b{main (gui) thread}, unlike all QRhi operations, then
+ transferred to the thread working with the QRhi and QRhiSwapChain and passed
+ on to the swapchain. This allows doing native platform queries that are
+ only safe to be called on the main thread, for example to query the
+ CAMetalLayer from a NSView, and then passing on the data to the
+ QRhiSwapChain living on the rendering thread. With the Metal example, doing
+ the view.layer access on a dedicated rendering thread causes a warning in
+ the Xcode Thread Checker. With the data proxy mechanism, this is avoided.
+
+ When threads are not involved, generating and passing on the
+ QRhiSwapChainProxyData is not required: backends are guaranteed to be able
+ to query whatever is needed on their own, and if everything lives on the
+ main (gui) thread, that should be sufficient.
+
+ \note \a impl should match what the QRhi is created with. For example,
+ calling with QRhi::Metal on a non-Apple platform will not generate any
+ useful data.
+ */
+QRhiSwapChainProxyData QRhi::updateSwapChainProxyData(QRhi::Implementation impl, QWindow *window)
+{
+#if QT_CONFIG(metal)
+ if (impl == Metal)
+ return QRhiMetal::updateSwapChainProxyData(window);
+#else
+ Q_UNUSED(impl);
+ Q_UNUSED(window);
+#endif
+ return {};
+}
+
+/*!
\return the backend type for this QRhi.
*/
QRhi::Implementation QRhi::backend() const
@@ -5453,11 +8654,12 @@ QRhi::Implementation QRhi::backend() const
}
/*!
- \return the backend type as string for this QRhi.
+ \return a friendly name for the backend \a impl, usually the name of the 3D
+ API in use.
*/
-const char *QRhi::backendName() const
+const char *QRhi::backendName(Implementation impl)
{
- switch (d->implType) {
+ switch (impl) {
case QRhi::Null:
return "Null";
case QRhi::Vulkan:
@@ -5468,17 +8670,28 @@ const char *QRhi::backendName() const
return "D3D11";
case QRhi::Metal:
return "Metal";
+ case QRhi::D3D12:
+ return "D3D12";
}
- Q_UNREACHABLE();
- return nullptr;
+ Q_UNREACHABLE_RETURN("Unknown");
+}
+
+/*!
+ \return the backend type as string for this QRhi.
+ */
+const char *QRhi::backendName() const
+{
+ return backendName(d->implType);
}
/*!
\enum QRhiDriverInfo::DeviceType
- Specifies the graphics device's type, when the information is available. In
- practice this is only applicable with Vulkan and Metal. With others the
- value will always be UnknownDevice.
+ Specifies the graphics device's type, when the information is available.
+
+ In practice this is only applicable with Vulkan and Metal. With Direct 3D
+ 11 and 12, using an adapter with the software flag set leads to the value
+ \c CpuDevice. Otherwise, and with OpenGL, the value is always UnknownDevice.
\value UnknownDevice
\value IntegratedDevice
@@ -5490,9 +8703,8 @@ const char *QRhi::backendName() const
/*!
\struct QRhiDriverInfo
- \internal
\inmodule QtGui
- \since 6.1
+ \since 6.6
\brief Describes the physical device, adapter, or graphics API
implementation that is used by an initialized QRhi.
@@ -5504,8 +8716,35 @@ const char *QRhi::backendName() const
\c{GL_VERSION}. The deviceId is always 0 for OpenGL. vendorId is always 0
for OpenGL and Metal. deviceType is always UnknownDevice for OpenGL and
Direct 3D.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
+/*!
+ \variable QRhiDriverInfo::deviceName
+
+ \sa QRhi::driverInfo()
+*/
+
+/*!
+ \variable QRhiDriverInfo::deviceId
+
+ \sa QRhi::driverInfo()
+*/
+
+/*!
+ \variable QRhiDriverInfo::vendorId
+
+ \sa QRhi::driverInfo()
+*/
+
+/*!
+ \variable QRhiDriverInfo::deviceType
+
+ \sa QRhi::driverInfo(), QRhiDriverInfo::DeviceType
+*/
+
#ifndef QT_NO_DEBUG_STREAM
static inline const char *deviceTypeStr(QRhiDriverInfo::DeviceType type)
{
@@ -5524,8 +8763,7 @@ static inline const char *deviceTypeStr(QRhiDriverInfo::DeviceType type)
return "Cpu";
}
- Q_UNREACHABLE();
- return nullptr;
+ Q_UNREACHABLE_RETURN(nullptr);
}
QDebug operator<<(QDebug dbg, const QRhiDriverInfo &info)
{
@@ -5574,6 +8812,33 @@ void QRhi::addCleanupCallback(const CleanupCallback &callback)
}
/*!
+ \overload
+
+ Registers \a callback to be invoked either when the QRhi is destroyed or
+ when runCleanup() is called. This overload takes an opaque pointer, \a key,
+ that is used to ensure that a given callback is registered (and so called)
+ only once.
+
+ \sa removeCleanupCallback()
+ */
+void QRhi::addCleanupCallback(const void *key, const CleanupCallback &callback)
+{
+ d->addCleanupCallback(key, callback);
+}
+
+/*!
+ Deregisters the callback with \a key. If no cleanup callback was registered
+ with \a key, the function does nothing. Callbacks registered without a key
+ cannot be removed.
+
+ \sa addCleanupCallback()
+ */
+void QRhi::removeCleanupCallback(const void *key)
+{
+ d->removeCleanupCallback(key);
+}
+
+/*!
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
@@ -5583,46 +8848,21 @@ void QRhi::addCleanupCallback(const CleanupCallback &callback)
*/
void QRhi::runCleanup()
{
- for (const CleanupCallback &f : qAsConst(d->cleanupCallbacks))
+ for (const CleanupCallback &f : std::as_const(d->cleanupCallbacks))
f(this);
d->cleanupCallbacks.clear();
-}
-/*!
- Registers a \a callback that is called with an elapsed time calculated from
- GPU timestamps asynchronously after a timestamp becomes available at some
- point after presenting a frame.
+ for (auto it = d->keyedCleanupCallbacks.cbegin(), end = d->keyedCleanupCallbacks.cend(); it != end; ++it)
+ it.value()(this);
- The callback is called with a float value that is meant to be in
- milliseconds and represents the elapsed time on the GPU side for a given
- frame. Care must be exercised with the interpretation of the value, as what
- it exactly is is not controlled by Qt and depends on the underlying
- graphics API and its implementation. In particular, comparing the values
- between different graphics APIs is discouraged and may be meaningless.
-
- The timing values become available asynchronously, sometimes several frames
- after the frame has been submitted in endFrame(). There is currently no way
- to identify the frame. The callback is invoked whenever the timestamp
- queries complete.
-
- \note This is only supported when the Timestamp feature is reported as
- supported from isFeatureSupported(). Otherwise the \a callback is never
- called.
-
- The \a callback is always called on the thread the QRhi lives and operates
- on. While not guaranteed, it is typical that the callback is invoked from
- within beginFrame().
- */
-void QRhi::addGpuFrameTimeCallback(const GpuFrameTimeCallback &callback)
-{
- d->addGpuFrameTimeCallback(callback);
+ d->keyedCleanupCallbacks.clear();
}
/*!
\class QRhiResourceUpdateBatch
- \internal
\inmodule QtGui
+ \since 6.6
\brief Records upload and copy type of operations.
With QRhi it is no longer possible to perform copy type of operations at
@@ -5638,6 +8878,9 @@ void QRhi::addGpuFrameTimeCallback(const GpuFrameTimeCallback &callback)
To get an available, empty batch from the pool, call
QRhi::nextResourceUpdateBatch().
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
@@ -5681,29 +8924,26 @@ void QRhiResourceUpdateBatch::release()
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);
- ...
- }
+ \code
+ 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;
+ void render()
+ {
+ QRhiResourceUpdateBatch *resUpdates = rhi->nextResourceUpdateBatch();
+ if (initialUpdates) {
+ resUpdates->merge(initialUpdates);
+ initialUpdates->release();
+ initialUpdates = nullptr;
+ }
+ // resUpdates->updateDynamicBuffer(...);
+ cb->beginPass(rt, clearCol, clearDs, resUpdates);
}
- resUpdates->updateDynamicBuffer(...);
- ...
- cb->beginPass(rt, clearCol, clearDs, resUpdates);
- }
\endcode
*/
void QRhiResourceUpdateBatch::merge(QRhiResourceUpdateBatch *other)
@@ -5745,10 +8985,10 @@ bool QRhiResourceUpdateBatch::hasOptimalCapacity() const
\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.
+ multiple native buffer objects underneath can be safely ignored when using
+ the QRhi and QRhiResourceUpdateBatch.
*/
-void QRhiResourceUpdateBatch::updateDynamicBuffer(QRhiBuffer *buf, int offset, int size, const void *data)
+void QRhiResourceUpdateBatch::updateDynamicBuffer(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
{
if (size > 0) {
const int idx = d->activeBufferOpCount++;
@@ -5768,7 +9008,7 @@ void QRhiResourceUpdateBatch::updateDynamicBuffer(QRhiBuffer *buf, int offset, i
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)
+void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
{
if (size > 0) {
const int idx = d->activeBufferOpCount++;
@@ -5780,6 +9020,8 @@ void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, int offset, in
}
/*!
+ \overload
+
Enqueues updating the entire QRhiBuffer \a buf created with the type
QRhiBuffer::Immutable or QRhiBuffer::Static.
*/
@@ -5801,7 +9043,7 @@ void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, const void *da
A readback is asynchronous. \a result contains a callback that is invoked
when the operation has completed. The data is provided in
- QRhiBufferReadbackResult::data. Upon successful completion that QByteArray
+ QRhiReadbackResult::data. Upon successful completion that QByteArray
will have a size equal to \a size. On failure the QByteArray will be empty.
\note Reading buffers with a usage different than QRhiBuffer::UniformBuffer
@@ -5818,7 +9060,7 @@ void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, const void *da
\sa readBackTexture(), QRhi::isFeatureSupported(), QRhi::resourceLimit()
*/
-void QRhiResourceUpdateBatch::readBackBuffer(QRhiBuffer *buf, int offset, int size, QRhiBufferReadbackResult *result)
+void QRhiResourceUpdateBatch::readBackBuffer(QRhiBuffer *buf, quint32 offset, quint32 size, QRhiReadbackResult *result)
{
const int idx = d->activeBufferOpCount++;
if (idx < d->bufferOps.size())
@@ -5892,10 +9134,10 @@ void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src, co
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
- ...
+ \code
+ rhi->beginFrame(swapchain);
+ cb->beginPass(swapchain->currentFrameRenderTarget(), colorClear, dsClear);
+ // ...
QRhiReadbackResult *rbResult = new QRhiReadbackResult;
rbResult->completed = [rbResult] {
{
@@ -5906,11 +9148,11 @@ void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src, co
}
delete rbResult;
};
- u = nextResourceUpdateBatch();
+ QRhiResourceUpdateBatch *u = nextResourceUpdateBatch();
QRhiReadbackDescription rb; // no texture -> uses the current backbuffer of sc
u->readBackTexture(rb, rbResult);
- endPass(u);
- endFrame(sc);
+ cb->endPass(u);
+ rhi->endFrame(swapchain);
\endcode
\note The texture must be created with QRhiTexture::UsedAsTransferSource.
@@ -5987,11 +9229,43 @@ void QRhiResourceUpdateBatch::generateMips(QRhiTexture *tex)
\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.
- \warning The maximum number of batches is 64. When this limit is reached,
- the function will return null until a batch is returned to the pool.
+ Due to not being tied to a frame being recorded, the following sequence is
+ valid for example:
+
+ \code
+ rhi->beginFrame(swapchain);
+ QRhiResourceUpdateBatch *u = rhi->nextResourceUpdateBatch();
+ u->uploadStaticBuffer(buf, data);
+ // ... do not commit the batch
+ rhi->endFrame();
+ // u stays valid (assuming buf stays valid as well)
+ rhi->beginFrame(swapchain);
+ swapchain->currentFrameCommandBuffer()->resourceUpdate(u);
+ // ... draw with buf
+ rhi->endFrame();
+ \endcode
+
+ \warning The maximum number of batches per QRhi is 64. When this limit is
+ reached, the function will return null until a batch is returned to the
+ pool.
*/
QRhiResourceUpdateBatch *QRhi::nextResourceUpdateBatch()
{
+ // By default we prefer spreading out the utilization of the 64 batches as
+ // much as possible, meaning we won't pick the first one even if it's free,
+ // but prefer picking one after the last picked one. Relevant due to how
+ // QVLA and QRhiBufferData allocations behind the bufferOps are reused; in
+ // typical Qt Quick scenes this leads to a form of (eventually) seeding all
+ // the 64 resource batches with buffer operation data allocations which are
+ // then reused in subsequent frames. This comes at the expense of using
+ // more memory, but has proven good results when (CPU) profiling typical
+ // Quick/Quick3D apps.
+ //
+ // Prefering memory over performance means that we always pick the first
+ // free batch, and triggering the aggressive deallocating of all backing
+ // memory (see trimOpLists) before returning it.
+ static const bool preferMemoryOverPerformance = qEnvironmentVariableIntValue("QT_RHI_MINIMIZE_POOLS");
+
auto nextFreeBatch = [this]() -> QRhiResourceUpdateBatch * {
auto isFree = [this](int i) -> QRhiResourceUpdateBatch * {
const quint64 mask = 1ULL << quint64(i);
@@ -5999,7 +9273,8 @@ QRhiResourceUpdateBatch *QRhi::nextResourceUpdateBatch()
d->resUpdPoolMap |= mask;
QRhiResourceUpdateBatch *u = d->resUpdPool[i];
QRhiResourceUpdateBatchPrivate::get(u)->poolIndex = i;
- d->lastResUpdIdx = i;
+ if (!preferMemoryOverPerformance)
+ d->lastResUpdIdx = i;
return u;
}
return nullptr;
@@ -6018,7 +9293,7 @@ QRhiResourceUpdateBatch *QRhi::nextResourceUpdateBatch()
QRhiResourceUpdateBatch *u = nextFreeBatch();
if (!u) {
- const int oldSize = d->resUpdPool.count();
+ const int oldSize = d->resUpdPool.size();
const int newSize = oldSize + qMin(4, qMax(0, 64 - oldSize));
d->resUpdPool.resize(newSize);
for (int i = oldSize; i < newSize; ++i)
@@ -6028,6 +9303,9 @@ QRhiResourceUpdateBatch *QRhi::nextResourceUpdateBatch()
qWarning("Resource update batch pool exhausted (max is 64)");
}
+ if (preferMemoryOverPerformance && u)
+ u->d->trimOpLists();
+
return u;
}
@@ -6042,7 +9320,15 @@ void QRhiResourceUpdateBatchPrivate::free()
rhi->resUpdPoolMap &= ~mask;
poolIndex = -1;
+ // textureOps is cleared, to not keep the potentially large image pixel
+ // data alive, but it is expected that the container keeps the list alloc
+ // at least. Only trimOpList() goes for the more aggressive route with squeeze.
textureOps.clear();
+
+ // bufferOps is not touched, to allow reusing allocations (incl. in the
+ // elements' QRhiBufferData) as much as possible when this batch is used
+ // again in the future, which is important for performance, in particular
+ // with Qt Quick.
}
void QRhiResourceUpdateBatchPrivate::merge(QRhiResourceUpdateBatchPrivate *other)
@@ -6070,19 +9356,28 @@ bool QRhiResourceUpdateBatchPrivate::hasOptimalCapacity() const
void QRhiResourceUpdateBatchPrivate::trimOpLists()
{
- Q_ASSERT(poolIndex == -1); // must not be in use
+ // Unlike free(), this is expected to aggressively deallocate all memory
+ // used by both the buffer and texture operation lists. (i.e. using
+ // squeeze() to only keep the stack prealloc of the QVLAs)
+ //
+ // This (e.g. just the destruction of bufferOps elements) may have a
+ // non-negligible performance impact e.g. with Qt Quick with scenes where
+ // there are lots of buffer operations per frame.
activeBufferOpCount = 0;
bufferOps.clear();
+ bufferOps.squeeze();
activeTextureOpCount = 0;
textureOps.clear();
+ textureOps.squeeze();
}
/*!
- 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.
+ Sometimes committing resource updates is necessary or just more convenient
+ without starting a render pass. Calling this function with \a
+ resourceUpdates is an alternative to passing \a resourceUpdates to a
+ beginPass() call (or endPass(), which would be typical in case of readbacks).
\note Cannot be called inside a pass.
*/
@@ -6130,16 +9425,20 @@ void QRhiCommandBuffer::resourceUpdate(QRhiResourceUpdateBatch *resourceUpdates)
made on \a rt. Therefore, if \a rt has a QRhiTexture color attachment \c
texture, and one needs to make the texture a different size, the following
is then valid:
- \badcode
- rt = rhi->newTextureRenderTarget({ { texture } });
+ \code
+ QRhiTextureRenderTarget *rt = rhi->newTextureRenderTarget({ { texture } });
rt->create();
- ...
+ // ...
texture->setPixelSize(new_size);
texture->create();
- cb->beginPass(rt, ...); // this is ok, no explicit rt->create() is required before
+ cb->beginPass(rt, colorClear, dsClear); // this is ok, no explicit rt->create() is required before
\endcode
- \sa endPass()
+ \a flags allow controlling certain advanced functionality. One commonly used
+ flag is \c ExternalContents. This should be specified whenever
+ beginExternal() will be called within the pass started by this function.
+
+ \sa endPass(), BeginPassFlags
*/
void QRhiCommandBuffer::beginPass(QRhiRenderTarget *rt,
const QColor &colorClearValue,
@@ -6277,7 +9576,7 @@ void QRhiCommandBuffer::setShaderResources(QRhiShaderResourceBindings *srb,
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
+ \code
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 5 * sizeof(float) }
@@ -6290,11 +9589,10 @@ void QRhiCommandBuffer::setShaderResources(QRhiShaderResourceBindings *srb,
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:
+ vertex bindings can be specified simply like the following, assuming vbuf
+ is the QRhiBuffer with all the interleaved position+color data:
- \badcode
+ \code
const QRhiCommandBuffer::VertexInput vbufBinding(vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding);
\endcode
@@ -6434,8 +9732,9 @@ void QRhiCommandBuffer::drawIndexed(quint32 indexCount,
}
/*!
- Records a named debug group on the command buffer. This is shown in
- graphics debugging tools such as \l{https://renderdoc.org/}{RenderDoc} and
+ Records a named debug group on the command buffer with the specified \a
+ name. 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().
@@ -6493,6 +9792,8 @@ void QRhiCommandBuffer::debugMarkMsg(const QByteArray &msg)
\note Compute is only available when the \l{QRhi::Compute}{Compute} feature
is reported as supported.
+
+ \a flags is not currently used.
*/
void QRhiCommandBuffer::beginComputePass(QRhiResourceUpdateBatch *resourceUpdates, BeginPassFlags flags)
{
@@ -6576,9 +9877,9 @@ const QRhiNativeHandles *QRhiCommandBuffer::nativeHandles()
called when the pass recording was started with specifying
QRhiCommandBuffer::ExternalContent.
- 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
+ With Vulkan, Metal, or Direct3D 12 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
@@ -6626,6 +9927,72 @@ void QRhiCommandBuffer::endExternal()
}
/*!
+ \return the last available timestamp, in seconds, when
+ \l QRhi::EnableTimestamps was enabled when creating the QRhi. The value
+ indicates the elapsed time on the GPU during the last completed frame.
+
+ \note Do not expect results other than 0 when the QRhi::Timestamps feature
+ is not reported as supported, or when QRhi::EnableTimestamps was not passed
+ to QRhi::create(). There are exceptions to this, because with some graphics
+ APIs (Metal) timings are available without having to perform extra
+ operations (timestamp queries), but portable applications should always
+ consciously opt-in to timestamp collection when they know it is needed, and
+ call this function accordingly.
+
+ Care must be exercised with the interpretation of the value, as its
+ precision and granularity is often not controlled by Qt, and depends on the
+ underlying graphics API and its implementation. In particular, comparing
+ the values between different graphics APIs and hardware is discouraged and
+ may be meaningless.
+
+ When the frame was recorded with \l{QRhi::beginFrame()}{beginFrame()} and
+ \l{QRhi::endFrame()}{endFrame()}, i.e., with a swapchain, the timing values
+ will likely become available asynchronously. The returned value may
+ therefore be 0 (e.g., for the first 1-2 frames) or the last known value
+ referring to some previous frame. The value my also
+ become 0 again under certain conditions, such as when resizing the window.
+ It can be expected that the most up-to-date available value is retrieved in
+ beginFrame() and becomes queriable via this function once beginFrame()
+ returns.
+
+ \note Do not assume that the value refers to the previous
+ (\c{currently_recorded - 1}) frame. It may refer to \c{currently_recorded -
+ 2} or \c{currently_recorded - 3} as well. The exact behavior may depend on
+ the graphics API and its implementation.
+
+ On the other hand, with offscreen frames the returned value is up-to-date
+ once \l{QRhi::endOffscreenFrame()}{endOffscreenFrame()} returns, because
+ offscreen frames reduce GPU pipelining and wait the the commands to be
+ complete.
+
+ \note This means that, unlike with swapchain frames, with offscreen frames
+ the returned value is guaranteed to refer to the frame that has just been
+ submitted and completed. (assuming this function is called after
+ endOffscreenFrame() but before the next beginOffscreenFrame())
+
+ Watch out for the consequences of GPU frequency scaling and GPU clock
+ changes, depending on the platform. For example, on Windows the returned
+ timing may vary in a quite wide range between frames with modern graphics
+ cards, even when submitting frames with a similar, or the same workload.
+ This is out of scope for Qt to control and solve, generally speaking.
+ However, the D3D12 backend automatically calls
+ \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-setstablepowerstate}{ID3D12Device::SetStablePowerState()}
+ whenever the environment variable \c QT_D3D_STABLE_POWER_STATE is set to a
+ non-zero value. This can greatly stabilize the result. It can also have a
+ non-insignificant effect on the CPU-side timings measured via QElapsedTimer
+ for example, especially when offscreen frames are involved.
+
+ \note Do not and never ship applications to production with
+ \c QT_D3D_STABLE_POWER_STATE set. See the Windows API documentation for details.
+
+ \sa QRhi::Timestamps, QRhi::EnableTimestamps
+ */
+double QRhiCommandBuffer::lastCompletedGpuTime()
+{
+ return m_rhi->lastCompletedGpuTime(this);
+}
+
+/*!
\return the value (typically an offset) \a v aligned to the uniform buffer
alignment given by by ubufAlignment().
*/
@@ -6638,7 +10005,7 @@ int QRhi::ubufAligned(int v) const
/*!
\return the number of mip levels for a given \a size.
*/
-int QRhi::mipLevelsForSize(const QSize &size) const
+int QRhi::mipLevelsForSize(const QSize &size)
{
return qFloor(std::log2(qMax(size.width(), size.height()))) + 1;
}
@@ -6647,7 +10014,7 @@ int QRhi::mipLevelsForSize(const QSize &size) const
\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
+QSize QRhi::sizeForMipLevel(int mipLevel, const QSize &baseLevelSize)
{
const int w = qMax(1, baseLevelSize.width() >> mipLevel);
const int h = qMax(1, baseLevelSize.height() >> mipLevel);
@@ -6761,7 +10128,8 @@ int QRhi::resourceLimit(ResourceLimit limit) const
for the device, context, and similar concepts used by the backend.
Cast to QRhiVulkanNativeHandles, QRhiD3D11NativeHandles,
- QRhiGles2NativeHandles, QRhiMetalNativeHandles as appropriate.
+ QRhiD3D12NativeHandles, QRhiGles2NativeHandles, or QRhiMetalNativeHandles
+ as appropriate.
\note No ownership is transferred, neither for the returned pointer nor for
any native objects.
@@ -6862,7 +10230,7 @@ bool QRhi::isDeviceLost() const
}
/*!
- \return a binary \a data blob with data collected from the
+ \return a binary data blob with data collected from the
QRhiGraphicsPipeline and QRhiComputePipeline successfully created during
the lifetime of this QRhi.
@@ -6893,6 +10261,11 @@ bool QRhi::isDeviceLost() const
See EnablePipelineCacheDataSave for further details about this feature.
+ \note Minimize the number of calls to this function. Retrieving the blob is
+ not always a cheap operation, and therefore this function should only be
+ called at a low frequency, ideally only once e.g. when closing the
+ application.
+
\sa setPipelineCacheData(), create(), isFeatureSupported()
*/
QByteArray QRhi::pipelineCacheData()
@@ -6942,6 +10315,11 @@ QByteArray QRhi::pipelineCacheData()
visible at all in case other caching mechanisms outside of Qt's control are
already active.
+ \note Minimize the number of calls to this function. Loading the blob is
+ not always a cheap operation, and therefore this function should only be
+ called at a low frequency, ideally only once e.g. when starting the
+ application.
+
\sa pipelineCacheData(), isFeatureSupported()
*/
void QRhi::setPipelineCacheData(const QByteArray &data)
@@ -6950,42 +10328,134 @@ void QRhi::setPipelineCacheData(const QByteArray &data)
}
/*!
- \struct QRhiMemAllocStats
- \internal
+ \struct QRhiStats
\inmodule QtGui
+ \since 6.6
\brief Statistics provided from the underlying memory allocator.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
+/*!
+ \variable QRhiStats::totalPipelineCreationTime
+
+ The total time in milliseconds spent in graphics and compute pipeline
+ creation, which usually involves shader compilation or cache lookups, and
+ potentially expensive processing.
+
+ \note The value should not be compared between different backends since the
+ concept of "pipelines" and what exactly happens under the hood during, for
+ instance, a call to QRhiGraphicsPipeline::create(), differ greatly between
+ graphics APIs and their implementations.
+
+ \sa QRhi::statistics()
+*/
+
+/*!
+ \variable QRhiStats::blockCount
+
+ Statistic reported from the Vulkan or D3D12 memory allocator.
+
+ \sa QRhi::statistics()
+*/
+
+/*!
+ \variable QRhiStats::allocCount
+
+ Statistic reported from the Vulkan or D3D12 memory allocator.
+
+ \sa QRhi::statistics()
+*/
+
+/*!
+ \variable QRhiStats::usedBytes
+
+ Statistic reported from the Vulkan or D3D12 memory allocator.
+
+ \sa QRhi::statistics()
+*/
+
+/*!
+ \variable QRhiStats::unusedBytes
+
+ Statistic reported from the Vulkan or D3D12 memory allocator.
+
+ \sa QRhi::statistics()
+*/
+
+/*!
+ \variable QRhiStats::totalUsageBytes
+
+ Valid only with D3D12 currently. Matches IDXGIAdapter3::QueryVideoMemoryInfo().
+
+ \sa QRhi::statistics()
+*/
+
#ifndef QT_NO_DEBUG_STREAM
-QDebug operator<<(QDebug dbg, const QRhiMemAllocStats &info)
+QDebug operator<<(QDebug dbg, const QRhiStats &info)
{
QDebugStateSaver saver(dbg);
- dbg.nospace() << "QRhiMemAllocStats(blockCount=" << info.blockCount
+ dbg.nospace() << "QRhiStats("
+ << "totalPipelineCreationTime=" << info.totalPipelineCreationTime
+ << " blockCount=" << info.blockCount
<< " allocCount=" << info.allocCount
<< " usedBytes=" << info.usedBytes
<< " unusedBytes=" << info.unusedBytes
+ << " totalUsageBytes=" << info.totalUsageBytes
<< ')';
return dbg;
}
#endif
/*!
- Gathers and returns some statistics about the memory allocation of graphics
- resources. Only supported with some backends. With graphics APIs where
- there is no lower level control over resource memory allocations, this will
- never be supported and all fields in the results are 0.
+ Gathers and returns statistics about the timings and allocations of
+ graphics resources.
+
+ Data about memory allocations is only available with some backends, where
+ such operations are under Qt's control. With graphics APIs where there is
+ no lower level control over resource memory allocations, this will never be
+ supported and all relevant fields in the results are 0.
- With Vulkan, the values are valid always, and are queried from the
- underlying memory allocator library. This gives an insight into the memory
- requirements of the active buffers and textures.
+ With Vulkan in particular, the values are valid always, and are queried
+ from the underlying memory allocator library. This gives an insight into
+ the memory requirements of the active buffers and textures.
- \note Gathering the data may not be free, and therefore the function should
- not be called at a high frequency.
+ The same is true for Direct 3D 12. In addition to the memory allocator
+ library's statistics, here the result also includes a \c totalUsageBytes
+ field which reports the total size including additional resources that are
+ not under the memory allocator library's control (swapchain buffers,
+ descriptor heaps, etc.), as reported by DXGI.
+
+ The values correspond to all types of memory used, combined. (i.e. video +
+ system in case of a discreet GPU)
+
+ Additional data, such as the total time in milliseconds spent in graphics
+ and compute pipeline creation (which usually involves shader compilation or
+ cache lookups, and potentially expensive processing) is available with most
+ backends.
+
+ \note The elapsed times for operations such as pipeline creation may be
+ affected by various factors. The results should not be compared between
+ different backends since the concept of "pipelines" and what exactly
+ happens under the hood during, for instance, a call to
+ QRhiGraphicsPipeline::create(), differ greatly between graphics APIs and
+ their implementations.
+
+ \note Additionally, many drivers will likely employ various caching
+ strategies for shaders, programs, pipelines. (independently of Qt's own
+ similar facilities, such as setPipelineCacheData() or the OpenGL-specific
+ program binary disk cache). Because such internal behavior is transparent
+ to the API client, Qt and QRhi have no knowledge or control over the exact
+ caching strategy, persistency, invalidation of the cached data, etc. When
+ reading timings, such as the time spent on pipeline creation, the potential
+ presence and unspecified behavior of driver-level caching mechanisms should
+ be kept in mind.
*/
-QRhiMemAllocStats QRhi::graphicsMemoryAllocationStatistics() const
+QRhiStats QRhi::statistics() const
{
- return d->graphicsMemoryAllocationStatistics();
+ return d->statistics();
}
/*!
@@ -7037,7 +10507,7 @@ QRhiShaderResourceBindings *QRhi::newShaderResourceBindings()
*/
QRhiBuffer *QRhi::newBuffer(QRhiBuffer::Type type,
QRhiBuffer::UsageFlags usage,
- int size)
+ quint32 size)
{
return d->createBuffer(type, usage, size);
}
@@ -7072,14 +10542,21 @@ QRhiRenderBuffer *QRhi::newRenderBuffer(QRhiRenderBuffer::Type type,
}
/*!
- \return a new 2D texture with the specified \a format, \a pixelSize, \a
+ \return a new 1D or 2D texture with the specified \a format, \a pixelSize, \a
sampleCount, and \a flags.
+ A 1D texture array must have QRhiTexture::OneDimensional set in \a flags. This
+ function will implicitly set this flag if the \a pixelSize height is 0.
+
\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.
+ \note 1D textures are only functional when the OneDimensionalTextures feature is
+ reported as supported at run time. Further, mipmaps on 1D textures are only
+ functional when the OneDimensionalTextureMipmaps feature is reported at run time.
+
\sa QRhiResource::destroy()
*/
QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
@@ -7087,22 +10564,32 @@ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
int sampleCount,
QRhiTexture::Flags flags)
{
+ if (pixelSize.height() == 0)
+ flags |= QRhiTexture::OneDimensional;
+
return d->createTexture(format, pixelSize, 1, 0, sampleCount, flags);
}
/*!
- \return a new 2D or 3D texture with the specified \a format, \a width, \a
+ \return a new 1D, 2D or 3D texture with the specified \a format, \a width, \a
height, \a depth, \a sampleCount, and \a flags.
This overload is suitable for 3D textures because it allows specifying \a
depth. A 3D texture must have QRhiTexture::ThreeDimensional set in \a
flags, but using this overload that can be omitted because the flag is set
- implicitly whenever \a depth is greater than 0. For 2D and cube textures \a
+ implicitly whenever \a depth is greater than 0. For 1D, 2D and cube textures \a
depth should be set to 0.
+ A 1D texture must have QRhiTexture::OneDimensional set in \a flags. This overload
+ will implicitly set this flag if both \a height and \a depth are 0.
+
\note 3D textures are only functional when the ThreeDimensionalTextures
feature is reported as supported at run time.
+ \note 1D textures are only functional when the OneDimensionalTextures feature is
+ reported as supported at run time. Further, mipmaps on 1D textures are only
+ functional when the OneDimensionalTextureMipmaps feature is reported at run time.
+
\overload
*/
QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
@@ -7113,17 +10600,23 @@ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
if (depth > 0)
flags |= QRhiTexture::ThreeDimensional;
+ if (height == 0 && depth == 0)
+ flags |= QRhiTexture::OneDimensional;
+
return d->createTexture(format, QSize(width, height), depth, 0, sampleCount, flags);
}
/*!
- \return a new 2D texture array with the specified \a format, \a arraySize,
+ \return a new 1D or 2D texture array with the specified \a format, \a arraySize,
\a pixelSize, \a sampleCount, and \a flags.
This function implicitly sets QRhiTexture::TextureArray in \a flags.
+ A 1D texture array must have QRhiTexture::OneDimensional set in \a flags. This
+ function will implicitly set this flag if the \a pixelSize height is 0.
+
\note Do not confuse texture arrays with arrays of textures. A QRhiTexture
- created by this function is usable with 2D array samplers in the shader, for
+ created by this function is usable with 1D or 2D array samplers in the shader, for
example: \c{layout(binding = 1) uniform sampler2DArray texArr;}. Arrays of
textures refers to a list of textures that are exposed to the shader via
QRhiShaderResourceBinding::sampledTextures() and a count > 1, and declared
@@ -7133,6 +10626,11 @@ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
\note This is only functional when the TextureArrays feature is reported as
supported at run time.
+ \note 1D textures are only functional when the OneDimensionalTextures feature is
+ reported as supported at run time. Further, mipmaps on 1D textures are only
+ functional when the OneDimensionalTextureMipmaps feature is reported at run time.
+
+
\sa newTexture()
*/
QRhiTexture *QRhi::newTextureArray(QRhiTexture::Format format,
@@ -7142,6 +10640,10 @@ QRhiTexture *QRhi::newTextureArray(QRhiTexture::Format format,
QRhiTexture::Flags flags)
{
flags |= QRhiTexture::TextureArray;
+
+ if (pixelSize.height() == 0)
+ flags |= QRhiTexture::OneDimensional;
+
return d->createTexture(format, pixelSize, 1, arraySize, sampleCount, flags);
}
@@ -7150,6 +10652,14 @@ QRhiTexture *QRhi::newTextureArray(QRhiTexture::Format format,
minification filter \a minFilter, mipmapping mode \a mipmapMode, and the
addressing (wrap) modes \a addressU, \a addressV, and \a addressW.
+ \note Setting \a mipmapMode to a value other than \c None implies that
+ images for all relevant mip levels will be provided either via
+ \l{QRhiResourceUpdateBatch::uploadTexture()}{texture uploads} or by calling
+ \l{QRhiResourceUpdateBatch::generateMips()}{generateMips()} on the texture
+ that is used with this sampler. Attempting to use the sampler with a
+ texture that has no data for all relevant mip levels will lead to rendering
+ errors, with the exact behavior dependent on the underlying graphics API.
+
\sa QRhiResource::destroy()
*/
QRhiSampler *QRhi::newSampler(QRhiSampler::Filter magFilter,
@@ -7353,26 +10863,29 @@ int QRhi::currentFrameSlot() const
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
+ 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.
+ frames targeting a swapchain: there the GPU is potentially better utilized,
+ but working with readback operations needs more care from the application
+ because endFrame(), unlike endOffscreenFrame(), does not guarantee that the
+ results from the readback are available at that point.
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
+ \code
+ QRhiReadbackResult rbResult;
+ QRhiCommandBuffer *cb;
+ rhi->beginOffscreenFrame(&cb);
+ cb->beginPass(rt, colorClear, dsClear);
+ // ...
+ u = nextResourceUpdateBatch();
+ u->readBackTexture(rb, &rbResult);
+ cb->endPass(u);
+ rhi->endOffscreenFrame();
+ // image data available in rbResult
\endcode
\sa endOffscreenFrame(), beginFrame()
@@ -7390,7 +10903,9 @@ QRhi::FrameOpResult QRhi::beginOffscreenFrame(QRhiCommandBuffer **cb, BeginFrame
}
/*!
- Ends and waits for the offscreen frame.
+ Ends, submits, and waits for the offscreen frame.
+
+ \a flags is not currently used.
\sa beginOffscreenFrame()
*/
@@ -7431,6 +10946,9 @@ QRhi::FrameOpResult QRhi::finish()
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.
+
+ \sa QRhiRenderBuffer::setSampleCount(), QRhiTexture::setSampleCount(),
+ QRhiGraphicsPipeline::setSampleCount(), QRhiSwapChain::setSampleCount()
*/
QList<int> QRhi::supportedSampleCounts() const
{
@@ -7452,7 +10970,7 @@ int QRhi::ubufAlignment() const
return d->ubufAlignment();
}
-static QBasicAtomicInteger<QRhiGlobalObjectIdGenerator::Type> counter = Q_BASIC_ATOMIC_INITIALIZER(0);
+Q_CONSTINIT static QBasicAtomicInteger<QRhiGlobalObjectIdGenerator::Type> counter = Q_BASIC_ATOMIC_INITIALIZER(0);
QRhiGlobalObjectIdGenerator::Type QRhiGlobalObjectIdGenerator::newId()
{
@@ -7564,8 +11082,7 @@ QRhiPassResourceTracker::BufferStage QRhiPassResourceTracker::toPassTrackerBuffe
if (stages.testFlag(QRhiShaderResourceBinding::GeometryStage))
return QRhiPassResourceTracker::BufGeometryStage;
- Q_UNREACHABLE();
- return QRhiPassResourceTracker::BufVertexStage;
+ Q_UNREACHABLE_RETURN(QRhiPassResourceTracker::BufVertexStage);
}
QRhiPassResourceTracker::TextureStage QRhiPassResourceTracker::toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages)
@@ -7584,8 +11101,7 @@ QRhiPassResourceTracker::TextureStage QRhiPassResourceTracker::toPassTrackerText
if (stages.testFlag(QRhiShaderResourceBinding::GeometryStage))
return QRhiPassResourceTracker::TexGeometryStage;
- Q_UNREACHABLE();
- return QRhiPassResourceTracker::TexVertexStage;
+ Q_UNREACHABLE_RETURN(QRhiPassResourceTracker::TexVertexStage);
}
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhi.h b/src/gui/rhi/qrhi.h
new file mode 100644
index 0000000000..d20b7e00d1
--- /dev/null
+++ b/src/gui/rhi/qrhi.h
@@ -0,0 +1,2026 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRHI_H
+#define QRHI_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is part of the RHI API, with limited compatibility guarantees.
+// Usage of this API may make your code source and binary incompatible with
+// future versions of Qt.
+//
+
+#include <QtGui/qtguiglobal.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qthread.h>
+#include <QtGui/qmatrix4x4.h>
+#include <QtGui/qcolor.h>
+#include <QtGui/qimage.h>
+#include <functional>
+#include <array>
+
+#include <rhi/qshader.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindow;
+class QRhi;
+class QRhiImplementation;
+class QRhiBuffer;
+class QRhiRenderBuffer;
+class QRhiTexture;
+class QRhiSampler;
+class QRhiCommandBuffer;
+class QRhiResourceUpdateBatch;
+class QRhiResourceUpdateBatchPrivate;
+class QRhiSwapChain;
+
+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;
+
+ friend bool operator==(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) noexcept
+ {
+ return a.m_d == b.m_d && a.m_s == b.m_s;
+ }
+
+ friend bool operator!=(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) noexcept
+ {
+ return !(a == b);
+ }
+
+ friend size_t qHash(const QRhiDepthStencilClearValue &v, size_t seed = 0) noexcept
+ {
+ QtPrivate::QHashCombine hash;
+ seed = hash(seed, v.m_d);
+ seed = hash(seed, v.m_s);
+ return seed;
+ }
+};
+
+Q_DECLARE_TYPEINFO(QRhiDepthStencilClearValue, Q_RELOCATABLE_TYPE);
+
+#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;
+
+ friend bool operator==(const QRhiViewport &a, const QRhiViewport &b) noexcept
+ {
+ return a.m_rect == b.m_rect
+ && a.m_minDepth == b.m_minDepth
+ && a.m_maxDepth == b.m_maxDepth;
+ }
+
+ friend bool operator!=(const QRhiViewport &a, const QRhiViewport &b) noexcept
+ {
+ return !(a == b);
+ }
+
+ friend size_t qHash(const QRhiViewport &v, size_t seed = 0) noexcept
+ {
+ QtPrivate::QHashCombine hash;
+ seed = hash(seed, v.m_rect[0]);
+ seed = hash(seed, v.m_rect[1]);
+ seed = hash(seed, v.m_rect[2]);
+ seed = hash(seed, v.m_rect[3]);
+ seed = hash(seed, v.m_minDepth);
+ seed = hash(seed, v.m_maxDepth);
+ return seed;
+ }
+};
+
+Q_DECLARE_TYPEINFO(QRhiViewport, Q_RELOCATABLE_TYPE);
+
+#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 } };
+
+ friend bool operator==(const QRhiScissor &a, const QRhiScissor &b) noexcept
+ {
+ return a.m_rect == b.m_rect;
+ }
+
+ friend bool operator!=(const QRhiScissor &a, const QRhiScissor &b) noexcept
+ {
+ return !(a == b);
+ }
+
+ friend size_t qHash(const QRhiScissor &v, size_t seed = 0) noexcept
+ {
+ QtPrivate::QHashCombine hash;
+ seed = hash(seed, v.m_rect[0]);
+ seed = hash(seed, v.m_rect[1]);
+ seed = hash(seed, v.m_rect[2]);
+ seed = hash(seed, v.m_rect[3]);
+ return seed;
+ }
+};
+
+Q_DECLARE_TYPEINFO(QRhiScissor, Q_RELOCATABLE_TYPE);
+
+#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, quint32 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; }
+
+ quint32 instanceStepRate() const { return m_instanceStepRate; }
+ void setInstanceStepRate(quint32 rate) { m_instanceStepRate = rate; }
+
+private:
+ quint32 m_stride = 0;
+ Classification m_classification = PerVertex;
+ quint32 m_instanceStepRate = 1;
+
+ friend bool operator==(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) noexcept
+ {
+ return a.m_stride == b.m_stride
+ && a.m_classification == b.m_classification
+ && a.m_instanceStepRate == b.m_instanceStepRate;
+ }
+
+ friend bool operator!=(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) noexcept
+ {
+ return !(a == b);
+ }
+
+ friend size_t qHash(const QRhiVertexInputBinding &v, size_t seed = 0) noexcept
+ {
+ QtPrivate::QHashCombine hash;
+ seed = hash(seed, v.m_stride);
+ seed = hash(seed, v.m_classification);
+ seed = hash(seed, v.m_instanceStepRate);
+ return seed;
+ }
+};
+
+Q_DECLARE_TYPEINFO(QRhiVertexInputBinding, Q_RELOCATABLE_TYPE);
+
+#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,
+ UInt4,
+ UInt3,
+ UInt2,
+ UInt,
+ SInt4,
+ SInt3,
+ SInt2,
+ SInt,
+ Half4,
+ Half3,
+ Half2,
+ Half,
+ UShort4,
+ UShort3,
+ UShort2,
+ UShort,
+ SShort4,
+ SShort3,
+ SShort2,
+ SShort,
+ };
+
+ QRhiVertexInputAttribute() = default;
+ QRhiVertexInputAttribute(int binding, int location, Format format, quint32 offset, int matrixSlice = -1);
+
+ 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 setFormat(Format f) { m_format = f; }
+
+ quint32 offset() const { return m_offset; }
+ void setOffset(quint32 ofs) { m_offset = ofs; }
+
+ int matrixSlice() const { return m_matrixSlice; }
+ void setMatrixSlice(int slice) { m_matrixSlice = slice; }
+
+private:
+ int m_binding = 0;
+ int m_location = 0;
+ Format m_format = Float4;
+ quint32 m_offset = 0;
+ int m_matrixSlice = -1;
+
+ friend bool operator==(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) noexcept
+ {
+ return a.m_binding == b.m_binding
+ && a.m_location == b.m_location
+ && a.m_format == b.m_format
+ && a.m_offset == b.m_offset;
+ // matrixSlice excluded intentionally
+ }
+
+ friend bool operator!=(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) noexcept
+ {
+ return !(a == b);
+ }
+
+ friend size_t qHash(const QRhiVertexInputAttribute &v, size_t seed = 0) noexcept
+ {
+ QtPrivate::QHashCombine hash;
+ seed = hash(seed, v.m_binding);
+ seed = hash(seed, v.m_location);
+ seed = hash(seed, v.m_format);
+ seed = hash(seed, v.m_offset);
+ return seed;
+ }
+};
+
+Q_DECLARE_TYPEINFO(QRhiVertexInputAttribute, Q_RELOCATABLE_TYPE);
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputAttribute &);
+#endif
+
+class Q_GUI_EXPORT QRhiVertexInputLayout
+{
+public:
+ QRhiVertexInputLayout() = default;
+
+ void setBindings(std::initializer_list<QRhiVertexInputBinding> list) { m_bindings = list; }
+ template<typename InputIterator>
+ void setBindings(InputIterator first, InputIterator last)
+ {
+ m_bindings.clear();
+ std::copy(first, last, std::back_inserter(m_bindings));
+ }
+ const QRhiVertexInputBinding *cbeginBindings() const { return m_bindings.cbegin(); }
+ const QRhiVertexInputBinding *cendBindings() const { return m_bindings.cend(); }
+ const QRhiVertexInputBinding *bindingAt(qsizetype index) const { return &m_bindings.at(index); }
+ qsizetype bindingCount() const { return m_bindings.count(); }
+
+ void setAttributes(std::initializer_list<QRhiVertexInputAttribute> list) { m_attributes = list; }
+ template<typename InputIterator>
+ void setAttributes(InputIterator first, InputIterator last)
+ {
+ m_attributes.clear();
+ std::copy(first, last, std::back_inserter(m_attributes));
+ }
+ const QRhiVertexInputAttribute *cbeginAttributes() const { return m_attributes.cbegin(); }
+ const QRhiVertexInputAttribute *cendAttributes() const { return m_attributes.cend(); }
+ const QRhiVertexInputAttribute *attributeAt(qsizetype index) const { return &m_attributes.at(index); }
+ qsizetype attributeCount() const { return m_attributes.count(); }
+
+private:
+ QVarLengthArray<QRhiVertexInputBinding, 8> m_bindings;
+ QVarLengthArray<QRhiVertexInputAttribute, 8> m_attributes;
+
+ friend bool operator==(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) noexcept
+ {
+ return a.m_bindings == b.m_bindings && a.m_attributes == b.m_attributes;
+ }
+
+ friend bool operator!=(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) noexcept
+ {
+ return !(a == b);
+ }
+
+ friend size_t qHash(const QRhiVertexInputLayout &v, size_t seed = 0) noexcept
+ {
+ QtPrivate::QHashCombine hash;
+ seed = hash(seed, v.m_bindings);
+ seed = hash(seed, v.m_attributes);
+ return seed;
+ }
+
+ friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputLayout &);
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputLayout &);
+#endif
+
+class Q_GUI_EXPORT QRhiShaderStage
+{
+public:
+ enum Type {
+ Vertex,
+ TessellationControl,
+ TessellationEvaluation,
+ Geometry,
+ 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;
+
+ friend bool operator==(const QRhiShaderStage &a, const QRhiShaderStage &b) noexcept
+ {
+ return a.m_type == b.m_type
+ && a.m_shader == b.m_shader
+ && a.m_shaderVariant == b.m_shaderVariant;
+ }
+
+ friend bool operator!=(const QRhiShaderStage &a, const QRhiShaderStage &b) noexcept
+ {
+ return !(a == b);
+ }
+
+ friend size_t qHash(const QRhiShaderStage &v, size_t seed = 0) noexcept
+ {
+ QtPrivate::QHashCombine hash;
+ seed = hash(seed, v.m_type);
+ seed = hash(seed, v.m_shader);
+ seed = hash(seed, v.m_shaderVariant);
+ return seed;
+ }
+};
+
+Q_DECLARE_TYPEINFO(QRhiShaderStage, Q_RELOCATABLE_TYPE);
+
+#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,
+ Texture,
+ Sampler,
+ ImageLoad,
+ ImageStore,
+ ImageLoadStore,
+ BufferLoad,
+ BufferStore,
+ BufferLoadStore
+ };
+
+ enum StageFlag {
+ VertexStage = 1 << 0,
+ TessellationControlStage = 1 << 1,
+ TessellationEvaluationStage = 1 << 2,
+ GeometryStage = 1 << 3,
+ FragmentStage = 1 << 4,
+ ComputeStage = 1 << 5
+ };
+ Q_DECLARE_FLAGS(StageFlags, StageFlag)
+
+ QRhiShaderResourceBinding() = default;
+
+ bool isLayoutCompatible(const QRhiShaderResourceBinding &other) const;
+
+ static QRhiShaderResourceBinding uniformBuffer(int binding, StageFlags stage, QRhiBuffer *buf);
+ static QRhiShaderResourceBinding uniformBuffer(int binding, StageFlags stage, QRhiBuffer *buf, quint32 offset, quint32 size);
+ static QRhiShaderResourceBinding uniformBufferWithDynamicOffset(int binding, StageFlags stage, QRhiBuffer *buf, quint32 size);
+
+ static QRhiShaderResourceBinding sampledTexture(int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler);
+
+ struct TextureAndSampler {
+ QRhiTexture *tex;
+ QRhiSampler *sampler;
+ };
+ static QRhiShaderResourceBinding sampledTextures(int binding, StageFlags stage, int count, const TextureAndSampler *texSamplers);
+
+ static QRhiShaderResourceBinding texture(int binding, StageFlags stage, QRhiTexture *tex);
+ static QRhiShaderResourceBinding textures(int binding, StageFlags stage, int count, QRhiTexture **tex);
+ static QRhiShaderResourceBinding sampler(int binding, StageFlags stage, 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, quint32 offset, quint32 size);
+ static QRhiShaderResourceBinding bufferStore(int binding, StageFlags stage, QRhiBuffer *buf);
+ static QRhiShaderResourceBinding bufferStore(int binding, StageFlags stage, QRhiBuffer *buf, quint32 offset, quint32 size);
+ static QRhiShaderResourceBinding bufferLoadStore(int binding, StageFlags stage, QRhiBuffer *buf);
+ static QRhiShaderResourceBinding bufferLoadStore(int binding, StageFlags stage, QRhiBuffer *buf, quint32 offset, quint32 size);
+
+ struct Data
+ {
+ int binding;
+ QRhiShaderResourceBinding::StageFlags stage;
+ QRhiShaderResourceBinding::Type type;
+ struct UniformBufferData {
+ QRhiBuffer *buf;
+ quint32 offset;
+ quint32 maybeSize;
+ bool hasDynamicOffset;
+ };
+ static constexpr int MAX_TEX_SAMPLER_ARRAY_SIZE = 16;
+ struct TextureAndOrSamplerData {
+ int count;
+ TextureAndSampler texSamplers[MAX_TEX_SAMPLER_ARRAY_SIZE];
+ };
+ struct StorageImageData {
+ QRhiTexture *tex;
+ int level;
+ };
+ struct StorageBufferData {
+ QRhiBuffer *buf;
+ quint32 offset;
+ quint32 maybeSize;
+ };
+ union {
+ UniformBufferData ubuf;
+ TextureAndOrSamplerData stex;
+ StorageImageData simage;
+ StorageBufferData sbuf;
+ } u;
+
+ int arraySize() const
+ {
+ return type == QRhiShaderResourceBinding::SampledTexture || type == QRhiShaderResourceBinding::Texture
+ ? u.stex.count
+ : 1;
+ }
+
+ template<typename Output>
+ Output serialize(Output dst) const
+ {
+ // must write out exactly LAYOUT_DESC_ENTRIES_PER_BINDING elements here
+ *dst++ = quint32(binding);
+ *dst++ = quint32(stage);
+ *dst++ = quint32(type);
+ *dst++ = quint32(arraySize());
+ return dst;
+ }
+ };
+
+ static constexpr int LAYOUT_DESC_ENTRIES_PER_BINDING = 4;
+
+ template<typename Output>
+ static void serializeLayoutDescription(const QRhiShaderResourceBinding *first,
+ const QRhiShaderResourceBinding *last,
+ Output dst)
+ {
+ while (first != last) {
+ dst = first->d.serialize(dst);
+ ++first;
+ }
+ }
+
+private:
+ Data d;
+ friend class QRhiImplementation;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiShaderResourceBinding::StageFlags)
+
+Q_DECLARE_TYPEINFO(QRhiShaderResourceBinding, Q_PRIMITIVE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) noexcept;
+Q_GUI_EXPORT bool operator!=(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) noexcept;
+Q_GUI_EXPORT size_t qHash(const QRhiShaderResourceBinding &b, size_t seed = 0) noexcept;
+#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; }
+
+ int multiViewCount() const { return m_multiViewCount; }
+ void setMultiViewCount(int count) { m_multiViewCount = count; }
+
+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;
+ int m_multiViewCount = 0;
+};
+
+Q_DECLARE_TYPEINFO(QRhiColorAttachment, Q_RELOCATABLE_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);
+
+ void setColorAttachments(std::initializer_list<QRhiColorAttachment> list) { m_colorAttachments = list; }
+ template<typename InputIterator>
+ void setColorAttachments(InputIterator first, InputIterator last)
+ {
+ m_colorAttachments.clear();
+ std::copy(first, last, std::back_inserter(m_colorAttachments));
+ }
+ const QRhiColorAttachment *cbeginColorAttachments() const { return m_colorAttachments.cbegin(); }
+ const QRhiColorAttachment *cendColorAttachments() const { return m_colorAttachments.cend(); }
+ const QRhiColorAttachment *colorAttachmentAt(qsizetype index) const { return &m_colorAttachments.at(index); }
+ qsizetype colorAttachmentCount() const { return m_colorAttachments.count(); }
+
+ 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; }
+
+ QRhiTexture *depthResolveTexture() const { return m_depthResolveTexture; }
+ void setDepthResolveTexture(QRhiTexture *tex) { m_depthResolveTexture = tex; }
+
+private:
+ QVarLengthArray<QRhiColorAttachment, 8> m_colorAttachments;
+ QRhiRenderBuffer *m_depthStencilBuffer = nullptr;
+ QRhiTexture *m_depthTexture = nullptr;
+ QRhiTexture *m_depthResolveTexture = nullptr;
+};
+
+class Q_GUI_EXPORT QRhiTextureSubresourceUploadDescription
+{
+public:
+ QRhiTextureSubresourceUploadDescription() = default;
+ explicit QRhiTextureSubresourceUploadDescription(const QImage &image);
+ QRhiTextureSubresourceUploadDescription(const void *data, quint32 size);
+ explicit QRhiTextureSubresourceUploadDescription(const QByteArray &data);
+
+ 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; }
+
+ quint32 dataStride() const { return m_dataStride; }
+ void setDataStride(quint32 stride) { m_dataStride = stride; }
+
+ 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;
+ quint32 m_dataStride = 0;
+ QPoint m_destinationTopLeft;
+ QSize m_sourceSize;
+ QPoint m_sourceTopLeft;
+};
+
+Q_DECLARE_TYPEINFO(QRhiTextureSubresourceUploadDescription, Q_RELOCATABLE_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_RELOCATABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiTextureUploadDescription
+{
+public:
+ QRhiTextureUploadDescription() = default;
+ QRhiTextureUploadDescription(const QRhiTextureUploadEntry &entry);
+ QRhiTextureUploadDescription(std::initializer_list<QRhiTextureUploadEntry> list);
+
+ void setEntries(std::initializer_list<QRhiTextureUploadEntry> list) { m_entries = list; }
+ template<typename InputIterator>
+ void setEntries(InputIterator first, InputIterator last)
+ {
+ m_entries.clear();
+ std::copy(first, last, std::back_inserter(m_entries));
+ }
+ const QRhiTextureUploadEntry *cbeginEntries() const { return m_entries.cbegin(); }
+ const QRhiTextureUploadEntry *cendEntries() const { return m_entries.cend(); }
+ const QRhiTextureUploadEntry *entryAt(qsizetype index) const { return &m_entries.at(index); }
+ qsizetype entryCount() const { return m_entries.count(); }
+
+private:
+ QVarLengthArray<QRhiTextureUploadEntry, 16> m_entries;
+};
+
+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_RELOCATABLE_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_RELOCATABLE_TYPE);
+
+struct Q_GUI_EXPORT QRhiNativeHandles
+{
+};
+
+class Q_GUI_EXPORT QRhiResource
+{
+public:
+ enum Type {
+ Buffer,
+ Texture,
+ Sampler,
+ RenderBuffer,
+ RenderPassDescriptor,
+ SwapChainRenderTarget,
+ TextureRenderTarget,
+ ShaderResourceBindings,
+ GraphicsPipeline,
+ SwapChain,
+ ComputePipeline,
+ CommandBuffer
+ };
+
+ virtual ~QRhiResource();
+
+ virtual Type resourceType() const = 0;
+
+ virtual void destroy() = 0;
+
+ void deleteLater();
+
+ QByteArray name() const;
+ void setName(const QByteArray &name);
+
+ quint64 globalResourceId() const;
+
+ QRhi *rhi() 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)
+
+ struct NativeBuffer {
+ const void *objects[3];
+ int slotCount;
+ };
+
+ 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; }
+
+ quint32 size() const { return m_size; }
+ void setSize(quint32 sz) { m_size = sz; }
+
+ virtual bool create() = 0;
+
+ virtual NativeBuffer nativeBuffer();
+
+ virtual char *beginFullDynamicBufferUpdateForCurrentFrame();
+ virtual void endFullDynamicBufferUpdateForCurrentFrame();
+
+protected:
+ QRhiBuffer(QRhiImplementation *rhi, Type type_, UsageFlags usage_, quint32 size_);
+ Type m_type;
+ UsageFlags m_usage;
+ quint32 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,
+ UsedAsCompressedAtlas = 1 << 8,
+ ExternalOES = 1 << 9,
+ ThreeDimensional = 1 << 10,
+ TextureRectangleGL = 1 << 11,
+ TextureArray = 1 << 12,
+ OneDimensional = 1 << 13
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ enum Format {
+ UnknownFormat,
+
+ RGBA8,
+ BGRA8,
+ R8,
+ RG8,
+ R16,
+ RG16,
+ RED_OR_ALPHA8,
+
+ RGBA16F,
+ RGBA32F,
+ R16F,
+ R32F,
+
+ RGB10A2,
+
+ D16,
+ D24,
+ D24S8,
+ 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
+ };
+
+ struct NativeTexture {
+ quint64 object;
+ int layout; // or state
+ };
+
+ 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; }
+
+ int depth() const { return m_depth; }
+ void setDepth(int depth) { m_depth = depth; }
+
+ int arraySize() const { return m_arraySize; }
+ void setArraySize(int arraySize) { m_arraySize = arraySize; }
+
+ int arrayRangeStart() const { return m_arrayRangeStart; }
+ int arrayRangeLength() const { return m_arrayRangeLength; }
+ void setArrayRange(int startIndex, int count)
+ {
+ m_arrayRangeStart = startIndex;
+ m_arrayRangeLength = count;
+ }
+
+ 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; }
+
+ struct ViewFormat {
+ QRhiTexture::Format format;
+ bool srgb;
+ };
+ ViewFormat readViewFormat() const { return m_readViewFormat; }
+ void setReadViewFormat(const ViewFormat &fmt) { m_readViewFormat = fmt; }
+ ViewFormat writeViewFormat() const { return m_writeViewFormat; }
+ void setWriteViewFormat(const ViewFormat &fmt) { m_writeViewFormat = fmt; }
+
+ virtual bool create() = 0;
+ virtual NativeTexture nativeTexture();
+ virtual bool createFrom(NativeTexture src);
+ virtual void setNativeLayout(int layout);
+
+protected:
+ QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, int depth_,
+ int arraySize_, int sampleCount_, Flags flags_);
+ Format m_format;
+ QSize m_pixelSize;
+ int m_depth;
+ int m_arraySize;
+ int m_sampleCount;
+ Flags m_flags;
+ int m_arrayRangeStart = -1;
+ int m_arrayRangeLength = -1;
+ ViewFormat m_readViewFormat = { UnknownFormat, false };
+ ViewFormat m_writeViewFormat = { UnknownFormat, false };
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiTexture::Flags)
+
+class Q_GUI_EXPORT QRhiSampler : public QRhiResource
+{
+public:
+ enum Filter {
+ None,
+ Nearest,
+ Linear
+ };
+
+ enum AddressMode {
+ Repeat,
+ ClampToEdge,
+ Mirror,
+ };
+
+ 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 create() = 0;
+
+protected:
+ QRhiSampler(QRhiImplementation *rhi,
+ Filter magFilter_, Filter minFilter_, Filter mipmapMode_,
+ AddressMode u_, AddressMode v_, AddressMode w_);
+ 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)
+
+ struct NativeRenderBuffer {
+ quint64 object;
+ };
+
+ 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 f) { m_flags = f; }
+
+ virtual bool create() = 0;
+ virtual bool createFrom(NativeRenderBuffer src);
+
+ virtual QRhiTexture::Format backingFormat() const = 0;
+
+protected:
+ QRhiRenderBuffer(QRhiImplementation *rhi, Type type_, const QSize &pixelSize_,
+ int sampleCount_, Flags flags_, QRhiTexture::Format backingFormatHint_);
+ Type m_type;
+ QSize m_pixelSize;
+ int m_sampleCount;
+ Flags m_flags;
+ QRhiTexture::Format m_backingFormatHint;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiRenderBuffer::Flags)
+
+class Q_GUI_EXPORT QRhiRenderPassDescriptor : public QRhiResource
+{
+public:
+ QRhiResource::Type resourceType() const override;
+
+ virtual bool isCompatible(const QRhiRenderPassDescriptor *other) const = 0;
+ virtual const QRhiNativeHandles *nativeHandles();
+
+ virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const = 0;
+
+ virtual QVector<quint32> serializedFormat() const = 0;
+
+protected:
+ QRhiRenderPassDescriptor(QRhiImplementation *rhi);
+};
+
+class Q_GUI_EXPORT QRhiRenderTarget : public QRhiResource
+{
+public:
+ 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 QRhiSwapChainRenderTarget : public QRhiRenderTarget
+{
+public:
+ QRhiResource::Type resourceType() const override;
+ QRhiSwapChain *swapChain() const { return m_swapchain; }
+
+protected:
+ QRhiSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain_);
+ QRhiSwapChain *m_swapchain;
+};
+
+class Q_GUI_EXPORT QRhiTextureRenderTarget : public QRhiRenderTarget
+{
+public:
+ enum Flag {
+ PreserveColorContents = 1 << 0,
+ PreserveDepthStencilContents = 1 << 1,
+ DoNotStoreDepthStencilContents = 1 << 2
+ };
+ 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 create() = 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;
+
+ void setBindings(std::initializer_list<QRhiShaderResourceBinding> list) { m_bindings = list; }
+ template<typename InputIterator>
+ void setBindings(InputIterator first, InputIterator last)
+ {
+ m_bindings.clear();
+ std::copy(first, last, std::back_inserter(m_bindings));
+ }
+ const QRhiShaderResourceBinding *cbeginBindings() const { return m_bindings.cbegin(); }
+ const QRhiShaderResourceBinding *cendBindings() const { return m_bindings.cend(); }
+ const QRhiShaderResourceBinding *bindingAt(qsizetype index) const { return &m_bindings.at(index); }
+ qsizetype bindingCount() const { return m_bindings.count(); }
+
+ bool isLayoutCompatible(const QRhiShaderResourceBindings *other) const;
+
+ QVector<quint32> serializedLayoutDescription() const { return m_layoutDesc; }
+
+ virtual bool create() = 0;
+
+ enum UpdateFlag {
+ BindingsAreSorted = 0x01
+ };
+ Q_DECLARE_FLAGS(UpdateFlags, UpdateFlag)
+
+ virtual void updateResources(UpdateFlags flags = {}) = 0;
+
+protected:
+ static constexpr int BINDING_PREALLOC = 12;
+ QRhiShaderResourceBindings(QRhiImplementation *rhi);
+ QVarLengthArray<QRhiShaderResourceBinding, BINDING_PREALLOC> m_bindings;
+ size_t m_layoutDescHash = 0;
+ // Intentionally not using QVLA for m_layoutDesc: clients like Qt Quick are much
+ // better served with an implicitly shared container here, because they will likely
+ // throw this directly into structs serving as cache keys.
+ QVector<quint32> m_layoutDesc;
+ friend class QRhiImplementation;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBindings &);
+#endif
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiShaderResourceBindings::UpdateFlags)
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBindings &);
+#endif
+
+// The proper name. Until it gets rolled out universally, have the better name
+// as a typedef. Eventually it should be reversed (the old name being a typedef
+// to the new one).
+using QRhiShaderResourceBindingSet = QRhiShaderResourceBindings;
+
+class Q_GUI_EXPORT QRhiGraphicsPipeline : public QRhiResource
+{
+public:
+ enum Flag {
+ UsesBlendConstants = 1 << 0,
+ UsesStencilRef = 1 << 1,
+ UsesScissor = 1 << 2,
+ CompileShadersWithDebugInfo = 1 << 3
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ enum Topology {
+ Triangles,
+ TriangleStrip,
+ TriangleFan,
+ Lines,
+ LineStrip,
+ Points,
+ Patches
+ };
+
+ 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;
+ };
+
+ enum PolygonMode {
+ Fill,
+ Line
+ };
+
+ 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; }
+
+ void setTargetBlends(std::initializer_list<TargetBlend> list) { m_targetBlends = list; }
+ template<typename InputIterator>
+ void setTargetBlends(InputIterator first, InputIterator last)
+ {
+ m_targetBlends.clear();
+ std::copy(first, last, std::back_inserter(m_targetBlends));
+ }
+ const TargetBlend *cbeginTargetBlends() const { return m_targetBlends.cbegin(); }
+ const TargetBlend *cendTargetBlends() const { return m_targetBlends.cend(); }
+ const TargetBlend *targetBlendAt(qsizetype index) const { return &m_targetBlends.at(index); }
+ qsizetype targetBlendCount() const { return m_targetBlends.count(); }
+
+ 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; }
+
+ float lineWidth() const { return m_lineWidth; }
+ void setLineWidth(float width) { m_lineWidth = width; }
+
+ int depthBias() const { return m_depthBias; }
+ void setDepthBias(int bias) { m_depthBias = bias; }
+
+ float slopeScaledDepthBias() const { return m_slopeScaledDepthBias; }
+ void setSlopeScaledDepthBias(float bias) { m_slopeScaledDepthBias = bias; }
+
+ void setShaderStages(std::initializer_list<QRhiShaderStage> list) { m_shaderStages = list; }
+ template<typename InputIterator>
+ void setShaderStages(InputIterator first, InputIterator last)
+ {
+ m_shaderStages.clear();
+ std::copy(first, last, std::back_inserter(m_shaderStages));
+ }
+ const QRhiShaderStage *cbeginShaderStages() const { return m_shaderStages.cbegin(); }
+ const QRhiShaderStage *cendShaderStages() const { return m_shaderStages.cend(); }
+ const QRhiShaderStage *shaderStageAt(qsizetype index) const { return &m_shaderStages.at(index); }
+ qsizetype shaderStageCount() const { return m_shaderStages.count(); }
+
+ 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; }
+
+ int patchControlPointCount() const { return m_patchControlPointCount; }
+ void setPatchControlPointCount(int count) { m_patchControlPointCount = count; }
+
+ PolygonMode polygonMode() const {return m_polygonMode; }
+ void setPolygonMode(PolygonMode mode) {m_polygonMode = mode; }
+
+ int multiViewCount() const { return m_multiViewCount; }
+ void setMultiViewCount(int count) { m_multiViewCount = count; }
+
+ virtual bool create() = 0;
+
+protected:
+ QRhiGraphicsPipeline(QRhiImplementation *rhi);
+ Flags m_flags;
+ Topology m_topology = Triangles;
+ CullMode m_cullMode = None;
+ FrontFace m_frontFace = CCW;
+ QVarLengthArray<TargetBlend, 8> 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;
+ float m_lineWidth = 1.0f;
+ int m_depthBias = 0;
+ float m_slopeScaledDepthBias = 0.0f;
+ int m_patchControlPointCount = 3;
+ PolygonMode m_polygonMode = Fill;
+ int m_multiViewCount = 0;
+ QVarLengthArray<QRhiShaderStage, 4> 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_RELOCATABLE_TYPE);
+
+struct QRhiSwapChainHdrInfo
+{
+ enum LimitsType {
+ LuminanceInNits,
+ ColorComponentValue
+ };
+
+ enum LuminanceBehavior {
+ SceneReferred,
+ DisplayReferred
+ };
+
+ LimitsType limitsType;
+ union {
+ struct {
+ float minLuminance;
+ float maxLuminance;
+ } luminanceInNits;
+ struct {
+ float maxColorComponentValue;
+ float maxPotentialColorComponentValue;
+ } colorComponentValue;
+ } limits;
+ LuminanceBehavior luminanceBehavior;
+ float sdrWhiteLevel;
+};
+
+Q_DECLARE_TYPEINFO(QRhiSwapChainHdrInfo, Q_RELOCATABLE_TYPE);
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiSwapChainHdrInfo &);
+#endif
+
+struct QRhiSwapChainProxyData
+{
+ void *reserved[2] = {};
+};
+
+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)
+
+ enum Format {
+ SDR,
+ HDRExtendedSrgbLinear,
+ HDR10,
+ HDRExtendedDisplayP3Linear
+ };
+
+ enum StereoTargetBuffer {
+ LeftBuffer,
+ RightBuffer
+ };
+
+ QRhiResource::Type resourceType() const override;
+
+ QWindow *window() const { return m_window; }
+ void setWindow(QWindow *window) { m_window = window; }
+
+ QRhiSwapChainProxyData proxyData() const { return m_proxyData; }
+ void setProxyData(const QRhiSwapChainProxyData &d) { m_proxyData = d; }
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags f) { m_flags = f; }
+
+ Format format() const { return m_format; }
+ void setFormat(Format f) { m_format = 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 QRhiRenderTarget *currentFrameRenderTarget(StereoTargetBuffer targetBuffer);
+ virtual QSize surfacePixelSize() = 0;
+ virtual bool isFormatSupported(Format f) = 0;
+ virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() = 0;
+ virtual bool createOrResize() = 0;
+ virtual QRhiSwapChainHdrInfo hdrInfo();
+
+protected:
+ QRhiSwapChain(QRhiImplementation *rhi);
+ QWindow *m_window = nullptr;
+ Flags m_flags;
+ Format m_format = SDR;
+ QRhiRenderBuffer *m_depthStencil = nullptr;
+ int m_sampleCount = 1;
+ QRhiRenderPassDescriptor *m_renderPassDesc = nullptr;
+ QSize m_currentPixelSize;
+ QRhiSwapChainProxyData m_proxyData;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiSwapChain::Flags)
+
+class Q_GUI_EXPORT QRhiComputePipeline : public QRhiResource
+{
+public:
+ enum Flag {
+ CompileShadersWithDebugInfo = 1 << 0
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ QRhiResource::Type resourceType() const override;
+ virtual bool create() = 0;
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags f) { m_flags = f; }
+
+ 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);
+ Flags m_flags;
+ QRhiShaderStage m_shaderStage;
+ QRhiShaderResourceBindings *m_shaderResourceBindings = nullptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiComputePipeline::Flags)
+
+class Q_GUI_EXPORT QRhiCommandBuffer : public QRhiResource
+{
+public:
+ enum IndexFormat {
+ IndexUInt16,
+ IndexUInt32
+ };
+
+ enum BeginPassFlag {
+ ExternalContent = 0x01,
+ DoNotTrackResourcesForCompute = 0x02
+ };
+ Q_DECLARE_FLAGS(BeginPassFlags, BeginPassFlag)
+
+ QRhiResource::Type resourceType() const override;
+
+ void resourceUpdate(QRhiResourceUpdateBatch *resourceUpdates);
+
+ void beginPass(QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates = nullptr,
+ BeginPassFlags flags = {});
+ 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, BeginPassFlags flags = {});
+ void endComputePass(QRhiResourceUpdateBatch *resourceUpdates = nullptr);
+ void setComputePipeline(QRhiComputePipeline *ps);
+ void dispatch(int x, int y, int z);
+
+ const QRhiNativeHandles *nativeHandles();
+ void beginExternal();
+ void endExternal();
+
+ double lastCompletedGpuTime();
+
+protected:
+ QRhiCommandBuffer(QRhiImplementation *rhi);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiCommandBuffer::BeginPassFlags)
+
+struct Q_GUI_EXPORT QRhiReadbackResult
+{
+ std::function<void()> completed = nullptr;
+ QRhiTexture::Format format;
+ QSize pixelSize;
+ QByteArray data;
+};
+
+class Q_GUI_EXPORT QRhiResourceUpdateBatch
+{
+public:
+ ~QRhiResourceUpdateBatch();
+
+ void release();
+
+ void merge(QRhiResourceUpdateBatch *other);
+ bool hasOptimalCapacity() const;
+
+ void updateDynamicBuffer(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data);
+ void uploadStaticBuffer(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data);
+ void uploadStaticBuffer(QRhiBuffer *buf, const void *data);
+ void readBackBuffer(QRhiBuffer *buf, quint32 offset, quint32 size, QRhiReadbackResult *result);
+ 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);
+
+private:
+ QRhiResourceUpdateBatch(QRhiImplementation *rhi);
+ Q_DISABLE_COPY(QRhiResourceUpdateBatch)
+ QRhiResourceUpdateBatchPrivate *d;
+ friend class QRhiResourceUpdateBatchPrivate;
+ friend class QRhi;
+};
+
+struct Q_GUI_EXPORT QRhiDriverInfo
+{
+ enum DeviceType {
+ UnknownDevice,
+ IntegratedDevice,
+ DiscreteDevice,
+ ExternalDevice,
+ VirtualDevice,
+ CpuDevice
+ };
+
+ QByteArray deviceName;
+ quint64 deviceId = 0;
+ quint64 vendorId = 0;
+ DeviceType deviceType = UnknownDevice;
+};
+
+Q_DECLARE_TYPEINFO(QRhiDriverInfo, Q_RELOCATABLE_TYPE);
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiDriverInfo &);
+#endif
+
+struct Q_GUI_EXPORT QRhiStats
+{
+ qint64 totalPipelineCreationTime = 0;
+ // Vulkan or D3D12 memory allocator statistics
+ quint32 blockCount = 0;
+ quint32 allocCount = 0;
+ quint64 usedBytes = 0;
+ quint64 unusedBytes = 0;
+ // D3D12 only, from IDXGIAdapter3::QueryVideoMemoryInfo(), incl. all resources
+ quint64 totalUsageBytes = 0;
+};
+
+Q_DECLARE_TYPEINFO(QRhiStats, Q_RELOCATABLE_TYPE);
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiStats &);
+#endif
+
+struct Q_GUI_EXPORT QRhiInitParams
+{
+};
+
+class Q_GUI_EXPORT QRhi
+{
+public:
+ enum Implementation {
+ Null,
+ Vulkan,
+ OpenGLES2,
+ D3D11,
+ Metal,
+ D3D12
+ };
+
+ enum Flag {
+ EnableDebugMarkers = 1 << 0,
+ PreferSoftwareRenderer = 1 << 1,
+ EnablePipelineCacheDataSave = 1 << 2,
+ EnableTimestamps = 1 << 3,
+ SuppressSmokeTestWarnings = 1 << 4
+ };
+ 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,
+ WideLines,
+ VertexShaderPointSize,
+ BaseVertex,
+ BaseInstance,
+ TriangleFanTopology,
+ ReadBackNonUniformBuffer,
+ ReadBackNonBaseMipLevel,
+ TexelFetch,
+ RenderToNonBaseMipLevel,
+ IntAttributes,
+ ScreenSpaceDerivatives,
+ ReadBackAnyTextureFormat,
+ PipelineCacheDataLoadSave,
+ ImageDataStride,
+ RenderBufferImport,
+ ThreeDimensionalTextures,
+ RenderTo3DTextureSlice,
+ TextureArrays,
+ Tessellation,
+ GeometryShader,
+ TextureArrayRange,
+ NonFillPolygonMode,
+ OneDimensionalTextures,
+ OneDimensionalTextureMipmaps,
+ HalfAttributes,
+ RenderToOneDimensionalTexture,
+ ThreeDimensionalTextureMipmaps,
+ MultiView,
+ TextureViewFormat,
+ ResolveDepthStencil
+ };
+
+ enum BeginFrameFlag {
+ };
+ Q_DECLARE_FLAGS(BeginFrameFlags, BeginFrameFlag)
+
+ enum EndFrameFlag {
+ SkipPresent = 1 << 0
+ };
+ Q_DECLARE_FLAGS(EndFrameFlags, EndFrameFlag)
+
+ enum ResourceLimit {
+ TextureSizeMin = 1,
+ TextureSizeMax,
+ MaxColorAttachments,
+ FramesInFlight,
+ MaxAsyncReadbackFrames,
+ MaxThreadGroupsPerDimension,
+ MaxThreadsPerThreadGroup,
+ MaxThreadGroupX,
+ MaxThreadGroupY,
+ MaxThreadGroupZ,
+ TextureArraySizeMax,
+ MaxUniformBufferRange,
+ MaxVertexInputs,
+ MaxVertexOutputs
+ };
+
+ ~QRhi();
+
+ static QRhi *create(Implementation impl,
+ QRhiInitParams *params,
+ Flags flags = {},
+ QRhiNativeHandles *importDevice = nullptr);
+ static bool probe(Implementation impl, QRhiInitParams *params);
+
+ Implementation backend() const;
+ const char *backendName() const;
+ static const char *backendName(Implementation impl);
+ QRhiDriverInfo driverInfo() const;
+ QThread *thread() const;
+
+ using CleanupCallback = std::function<void(QRhi *)>;
+ void addCleanupCallback(const CleanupCallback &callback);
+ void addCleanupCallback(const void *key, const CleanupCallback &callback);
+ void removeCleanupCallback(const void *key);
+ void runCleanup();
+
+ QRhiGraphicsPipeline *newGraphicsPipeline();
+ QRhiComputePipeline *newComputePipeline();
+ QRhiShaderResourceBindings *newShaderResourceBindings();
+
+ QRhiBuffer *newBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ quint32 size);
+
+ QRhiRenderBuffer *newRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount = 1,
+ QRhiRenderBuffer::Flags flags = {},
+ QRhiTexture::Format backingFormatHint = QRhiTexture::UnknownFormat);
+
+ QRhiTexture *newTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount = 1,
+ QRhiTexture::Flags flags = {});
+
+ QRhiTexture *newTexture(QRhiTexture::Format format,
+ int width, int height, int depth,
+ int sampleCount = 1,
+ QRhiTexture::Flags flags = {});
+
+ QRhiTexture *newTextureArray(QRhiTexture::Format format,
+ int arraySize,
+ const QSize &pixelSize,
+ int sampleCount = 1,
+ QRhiTexture::Flags flags = {});
+
+ QRhiSampler *newSampler(QRhiSampler::Filter magFilter,
+ QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler::AddressMode addressU,
+ QRhiSampler::AddressMode addressV,
+ QRhiSampler::AddressMode addressW = QRhiSampler::Repeat);
+
+ QRhiTextureRenderTarget *newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags = {});
+
+ QRhiSwapChain *newSwapChain();
+ FrameOpResult beginFrame(QRhiSwapChain *swapChain, BeginFrameFlags flags = {});
+ FrameOpResult endFrame(QRhiSwapChain *swapChain, EndFrameFlags flags = {});
+ bool isRecordingFrame() const;
+ int currentFrameSlot() const;
+
+ FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, BeginFrameFlags flags = {});
+ FrameOpResult endOffscreenFrame(EndFrameFlags flags = {});
+
+ QRhi::FrameOpResult finish();
+
+ QRhiResourceUpdateBatch *nextResourceUpdateBatch();
+
+ QList<int> supportedSampleCounts() const;
+
+ int ubufAlignment() const;
+ int ubufAligned(int v) const;
+
+ static int mipLevelsForSize(const QSize &size);
+ static QSize sizeForMipLevel(int mipLevel, const QSize &baseLevelSize);
+
+ bool isYUpInFramebuffer() const;
+ bool isYUpInNDC() const;
+ bool isClipDepthZeroToOne() const;
+
+ QMatrix4x4 clipSpaceCorrMatrix() const;
+
+ bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags = {}) const;
+ bool isFeatureSupported(QRhi::Feature feature) const;
+ int resourceLimit(ResourceLimit limit) const;
+
+ const QRhiNativeHandles *nativeHandles();
+ bool makeThreadLocalNativeContextCurrent();
+
+ static constexpr int MAX_MIP_LEVELS = 16; // -> max width or height is 65536
+
+ void releaseCachedResources();
+
+ bool isDeviceLost() const;
+
+ QByteArray pipelineCacheData();
+ void setPipelineCacheData(const QByteArray &data);
+
+ QRhiStats statistics() const;
+
+ static QRhiSwapChainProxyData updateSwapChainProxyData(Implementation impl, QWindow *window);
+
+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
+
+#include <rhi/qrhi_platform.h>
+
+#endif
diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h
index f825866585..b5429372a8 100644
--- a/src/gui/rhi/qrhi_p.h
+++ b/src/gui/rhi/qrhi_p.h
@@ -1,8 +1,8 @@
-// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QRHI_H
-#define QRHI_H
+#ifndef QRHI_P_H
+#define QRHI_P_H
//
// W A R N I N G
@@ -15,1798 +15,803 @@
// We mean it.
//
-#include <QtGui/qtguiglobal.h>
-#include <QSize>
-#include <QMatrix4x4>
-#include <QList>
-#include <QVarLengthArray>
-#include <QThread>
-#include <QColor>
-#include <QImage>
-#include <functional>
-#include <array>
-#include <private/qshader_p.h>
+#include <rhi/qrhi.h>
+#include <QBitArray>
+#include <QAtomicInt>
+#include <QElapsedTimer>
+#include <QLoggingCategory>
+#include <QtCore/qset.h>
+#include <QtCore/qvarlengtharray.h>
QT_BEGIN_NAMESPACE
-class QWindow;
-class QRhiImplementation;
-class QRhiBuffer;
-class QRhiRenderBuffer;
-class QRhiTexture;
-class QRhiSampler;
-class QRhiCommandBuffer;
-class QRhiResourceUpdateBatch;
-class QRhiResourceUpdateBatchPrivate;
-class QRhiSwapChain;
-
-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; }
+#define QRHI_RES(t, x) static_cast<t *>(x)
+#define QRHI_RES_RHI(t) t *rhiD = static_cast<t *>(m_rhi)
-private:
- float m_d = 1.0f;
- quint32 m_s = 0;
-};
+Q_DECLARE_LOGGING_CATEGORY(QRHI_LOG_INFO)
-Q_DECLARE_TYPEINFO(QRhiDepthStencilClearValue, Q_RELOCATABLE_TYPE);
-
-Q_GUI_EXPORT bool operator==(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) noexcept;
-Q_GUI_EXPORT bool operator!=(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) noexcept;
-Q_GUI_EXPORT size_t qHash(const QRhiDepthStencilClearValue &v, size_t seed = 0) noexcept;
-#ifndef QT_NO_DEBUG_STREAM
-Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiDepthStencilClearValue &);
-#endif
-
-class Q_GUI_EXPORT QRhiViewport
+class QRhiImplementation
{
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_RELOCATABLE_TYPE);
-
-Q_GUI_EXPORT bool operator==(const QRhiViewport &a, const QRhiViewport &b) noexcept;
-Q_GUI_EXPORT bool operator!=(const QRhiViewport &a, const QRhiViewport &b) noexcept;
-Q_GUI_EXPORT size_t qHash(const QRhiViewport &v, size_t seed = 0) noexcept;
-#ifndef QT_NO_DEBUG_STREAM
-Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiViewport &);
-#endif
+ virtual ~QRhiImplementation();
-class Q_GUI_EXPORT QRhiScissor
-{
-public:
- QRhiScissor() = default;
- QRhiScissor(int x, int y, int w, int h);
+ virtual bool create(QRhi::Flags flags) = 0;
+ virtual void destroy() = 0;
- 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;
+ virtual QRhiGraphicsPipeline *createGraphicsPipeline() = 0;
+ virtual QRhiComputePipeline *createComputePipeline() = 0;
+ virtual QRhiShaderResourceBindings *createShaderResourceBindings() = 0;
+ virtual QRhiBuffer *createBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ quint32 size) = 0;
+ virtual QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags,
+ QRhiTexture::Format backingFormatHint) = 0;
+ virtual QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int depth,
+ int arraySize,
+ int sampleCount,
+ QRhiTexture::Flags flags) = 0;
+ virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
+ QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u,
+ QRhiSampler::AddressMode v,
+ QRhiSampler::AddressMode w) = 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, QRhi::BeginFrameFlags flags) = 0;
+ virtual QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) = 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,
+ QRhiCommandBuffer::BeginPassFlags flags) = 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,
+ QRhiCommandBuffer::BeginPassFlags flags) = 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 double lastCompletedGpuTime(QRhiCommandBuffer *cb) = 0;
+
+ virtual QList<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 QRhiDriverInfo driverInfo() const = 0;
+ virtual QRhiStats statistics() = 0;
+ virtual bool makeThreadLocalNativeContextCurrent() = 0;
+ virtual void releaseCachedResources() = 0;
+ virtual bool isDeviceLost() const = 0;
+
+ virtual QByteArray pipelineCacheData() = 0;
+ virtual void setPipelineCacheData(const QByteArray &data) = 0;
+
+ void prepareForCreate(QRhi *rhi, QRhi::Implementation impl, QRhi::Flags flags);
+
+ 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, quint32 *bytesPerPixel) const;
+ bool isStencilSupportingFormat(QRhiTexture::Format format) const;
+
+ void registerResource(QRhiResource *res, bool ownsNativeResources = true)
+ {
+ // The ownsNativeResources is relevant for the (graphics resource) leak
+ // check in ~QRhiImplementation; when false, the registration's sole
+ // purpose is to automatically null out the resource's m_rhi pointer in
+ // case the rhi goes away first. (which should not happen in
+ // well-written applications but we try to be graceful)
+ resources.insert(res, ownsNativeResources);
}
-private:
- std::array<int, 4> m_rect { { 0, 0, 0, 0 } };
-};
-
-Q_DECLARE_TYPEINFO(QRhiScissor, Q_RELOCATABLE_TYPE);
-
-Q_GUI_EXPORT bool operator==(const QRhiScissor &a, const QRhiScissor &b) noexcept;
-Q_GUI_EXPORT bool operator!=(const QRhiScissor &a, const QRhiScissor &b) noexcept;
-Q_GUI_EXPORT size_t qHash(const QRhiScissor &v, size_t seed = 0) noexcept;
-#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_RELOCATABLE_TYPE);
-
-Q_GUI_EXPORT bool operator==(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) noexcept;
-Q_GUI_EXPORT bool operator!=(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) noexcept;
-Q_GUI_EXPORT size_t qHash(const QRhiVertexInputBinding &v, size_t seed = 0) noexcept;
-#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,
- UInt4,
- UInt3,
- UInt2,
- UInt,
- SInt4,
- SInt3,
- SInt2,
- SInt
- };
-
- QRhiVertexInputAttribute() = default;
- QRhiVertexInputAttribute(int binding, int location, Format format, quint32 offset, int matrixSlice = -1);
-
- 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 setFormat(Format f) { m_format = f; }
-
- quint32 offset() const { return m_offset; }
- void setOffset(quint32 ofs) { m_offset = ofs; }
-
- int matrixSlice() const { return m_matrixSlice; }
- void setMatrixSlice(int slice) { m_matrixSlice = slice; }
-
-private:
- int m_binding = 0;
- int m_location = 0;
- Format m_format = Float4;
- quint32 m_offset = 0;
- int m_matrixSlice = -1;
-};
-
-Q_DECLARE_TYPEINFO(QRhiVertexInputAttribute, Q_RELOCATABLE_TYPE);
-
-Q_GUI_EXPORT bool operator==(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) noexcept;
-Q_GUI_EXPORT bool operator!=(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) noexcept;
-Q_GUI_EXPORT size_t qHash(const QRhiVertexInputAttribute &v, size_t seed = 0) noexcept;
-#ifndef QT_NO_DEBUG_STREAM
-Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputAttribute &);
-#endif
-
-class Q_GUI_EXPORT QRhiVertexInputLayout
-{
-public:
- QRhiVertexInputLayout() = default;
-
- void setBindings(std::initializer_list<QRhiVertexInputBinding> list) { m_bindings = list; }
- template<typename InputIterator>
- void setBindings(InputIterator first, InputIterator last)
+ void unregisterResource(QRhiResource *res)
{
- m_bindings.clear();
- std::copy(first, last, std::back_inserter(m_bindings));
+ resources.remove(res);
}
- const QRhiVertexInputBinding *cbeginBindings() const { return m_bindings.cbegin(); }
- const QRhiVertexInputBinding *cendBindings() const { return m_bindings.cend(); }
- const QRhiVertexInputBinding *bindingAt(int index) const { return &m_bindings.at(index); }
- void setAttributes(std::initializer_list<QRhiVertexInputAttribute> list) { m_attributes = list; }
- template<typename InputIterator>
- void setAttributes(InputIterator first, InputIterator last)
+ void addDeleteLater(QRhiResource *res)
{
- m_attributes.clear();
- std::copy(first, last, std::back_inserter(m_attributes));
+ if (inFrame)
+ pendingDeleteResources.insert(res);
+ else
+ delete res;
}
- const QRhiVertexInputAttribute *cbeginAttributes() const { return m_attributes.cbegin(); }
- const QRhiVertexInputAttribute *cendAttributes() const { return m_attributes.cend(); }
-
-private:
- QVarLengthArray<QRhiVertexInputBinding, 8> m_bindings;
- QVarLengthArray<QRhiVertexInputAttribute, 8> m_attributes;
-
- friend Q_GUI_EXPORT bool operator==(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) noexcept;
- friend Q_GUI_EXPORT size_t qHash(const QRhiVertexInputLayout &v, size_t seed) noexcept;
- friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputLayout &);
-};
-
-Q_GUI_EXPORT bool operator==(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) noexcept;
-Q_GUI_EXPORT bool operator!=(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) noexcept;
-Q_GUI_EXPORT size_t qHash(const QRhiVertexInputLayout &v, size_t seed = 0) noexcept;
-#ifndef QT_NO_DEBUG_STREAM
-Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputLayout &);
-#endif
-
-class Q_GUI_EXPORT QRhiShaderStage
-{
-public:
- enum Type {
- Vertex,
- TessellationControl,
- TessellationEvaluation,
- Geometry,
- 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_RELOCATABLE_TYPE);
-
-Q_GUI_EXPORT bool operator==(const QRhiShaderStage &a, const QRhiShaderStage &b) noexcept;
-Q_GUI_EXPORT bool operator!=(const QRhiShaderStage &a, const QRhiShaderStage &b) noexcept;
-Q_GUI_EXPORT size_t qHash(const QRhiShaderStage &s, size_t seed = 0) noexcept;
-#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,
- Texture,
- Sampler,
- ImageLoad,
- ImageStore,
- ImageLoadStore,
- BufferLoad,
- BufferStore,
- BufferLoadStore
- };
-
- enum StageFlag {
- VertexStage = 1 << 0,
- TessellationControlStage = 1 << 1,
- TessellationEvaluationStage = 1 << 2,
- GeometryStage = 1 << 3,
- FragmentStage = 1 << 4,
- ComputeStage = 1 << 5
- };
- Q_DECLARE_FLAGS(StageFlags, StageFlag)
- QRhiShaderResourceBinding() = default;
-
- 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);
-
- struct TextureAndSampler {
- QRhiTexture *tex;
- QRhiSampler *sampler;
- };
- static QRhiShaderResourceBinding sampledTextures(int binding, StageFlags stage, int count, const TextureAndSampler *texSamplers);
-
- static QRhiShaderResourceBinding texture(int binding, StageFlags stage, QRhiTexture *tex);
- static QRhiShaderResourceBinding textures(int binding, StageFlags stage, int count, QRhiTexture **tex);
- static QRhiShaderResourceBinding sampler(int binding, StageFlags stage, 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);
-
- struct Data
+ void addCleanupCallback(const QRhi::CleanupCallback &callback)
{
- int binding;
- QRhiShaderResourceBinding::StageFlags stage;
- QRhiShaderResourceBinding::Type type;
- struct UniformBufferData {
- QRhiBuffer *buf;
- int offset;
- int maybeSize;
- bool hasDynamicOffset;
- };
- static const int MAX_TEX_SAMPLER_ARRAY_SIZE = 16;
- struct TextureAndOrSamplerData {
- int count;
- TextureAndSampler texSamplers[MAX_TEX_SAMPLER_ARRAY_SIZE];
- };
- struct StorageImageData {
- QRhiTexture *tex;
- int level;
- };
- struct StorageBufferData {
- QRhiBuffer *buf;
- int offset;
- int maybeSize;
- };
- union {
- UniformBufferData ubuf;
- TextureAndOrSamplerData stex;
- StorageImageData simage;
- StorageBufferData sbuf;
- } u;
-
- int arraySize() const
- {
- return type == QRhiShaderResourceBinding::SampledTexture || type == QRhiShaderResourceBinding::Texture
- ? u.stex.count
- : 1;
- }
-
- template<typename Output>
- Output serialize(Output dst) const
- {
- // must write out exactly LAYOUT_DESC_ENTRIES_PER_BINDING elements here
- *dst++ = quint32(binding);
- *dst++ = quint32(stage);
- *dst++ = quint32(type);
- *dst++ = quint32(arraySize());
- return dst;
- }
- };
-
- Data *data() { return &d; }
- const Data *data() const { return &d; }
-
- static const int LAYOUT_DESC_ENTRIES_PER_BINDING = 4;
+ cleanupCallbacks.append(callback);
+ }
- template<typename Output>
- static void serializeLayoutDescription(const QRhiShaderResourceBinding *first,
- const QRhiShaderResourceBinding *last,
- Output dst)
+ void addCleanupCallback(const void *key, const QRhi::CleanupCallback &callback)
{
- while (first != last) {
- dst = first->data()->serialize(dst);
- ++first;
- }
+ keyedCleanupCallbacks[key] = callback;
}
-private:
- Data d;
-};
-
-Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiShaderResourceBinding::StageFlags)
-
-Q_DECLARE_TYPEINFO(QRhiShaderResourceBinding, Q_PRIMITIVE_TYPE);
-
-Q_GUI_EXPORT bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) noexcept;
-Q_GUI_EXPORT bool operator!=(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) noexcept;
-Q_GUI_EXPORT size_t qHash(const QRhiShaderResourceBinding &b, size_t seed = 0) noexcept;
-#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_RELOCATABLE_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);
-
- void setColorAttachments(std::initializer_list<QRhiColorAttachment> list) { m_colorAttachments = list; }
- template<typename InputIterator>
- void setColorAttachments(InputIterator first, InputIterator last)
+ void removeCleanupCallback(const void *key)
{
- m_colorAttachments.clear();
- std::copy(first, last, std::back_inserter(m_colorAttachments));
+ keyedCleanupCallbacks.remove(key);
}
- const QRhiColorAttachment *cbeginColorAttachments() const { return m_colorAttachments.cbegin(); }
- const QRhiColorAttachment *cendColorAttachments() const { return m_colorAttachments.cend(); }
- const QRhiColorAttachment *colorAttachmentAt(int index) const { return &m_colorAttachments.at(index); }
-
- 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:
- QVarLengthArray<QRhiColorAttachment, 8> m_colorAttachments;
- QRhiRenderBuffer *m_depthStencilBuffer = nullptr;
- QRhiTexture *m_depthTexture = nullptr;
-};
-
-class Q_GUI_EXPORT QRhiTextureSubresourceUploadDescription
-{
-public:
- QRhiTextureSubresourceUploadDescription() = default;
- explicit QRhiTextureSubresourceUploadDescription(const QImage &image);
- QRhiTextureSubresourceUploadDescription(const void *data, int size);
- explicit QRhiTextureSubresourceUploadDescription(const QByteArray &data);
-
- 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; }
-
- quint32 dataStride() const { return m_dataStride; }
- void setDataStride(quint32 stride) { m_dataStride = stride; }
-
- 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; }
+ bool sanityCheckGraphicsPipeline(QRhiGraphicsPipeline *ps);
+ bool sanityCheckShaderResourceBindings(QRhiShaderResourceBindings *srb);
+ void updateLayoutDesc(QRhiShaderResourceBindings *srb);
-private:
- QImage m_image;
- QByteArray m_data;
- quint32 m_dataStride = 0;
- QPoint m_destinationTopLeft;
- QSize m_sourceSize;
- QPoint m_sourceTopLeft;
-};
-
-Q_DECLARE_TYPEINFO(QRhiTextureSubresourceUploadDescription, Q_RELOCATABLE_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_RELOCATABLE_TYPE);
+ quint32 pipelineCacheRhiId() const
+ {
+ const quint32 ver = (QT_VERSION_MAJOR << 16) | (QT_VERSION_MINOR << 8) | (QT_VERSION_PATCH);
+ return (quint32(implType) << 24) | ver;
+ }
-class Q_GUI_EXPORT QRhiTextureUploadDescription
-{
-public:
- QRhiTextureUploadDescription() = default;
- QRhiTextureUploadDescription(const QRhiTextureUploadEntry &entry);
- QRhiTextureUploadDescription(std::initializer_list<QRhiTextureUploadEntry> list);
+ void pipelineCreationStart()
+ {
+ pipelineCreationTimer.start();
+ }
- void setEntries(std::initializer_list<QRhiTextureUploadEntry> list) { m_entries = list; }
- template<typename InputIterator>
- void setEntries(InputIterator first, InputIterator last)
+ void pipelineCreationEnd()
{
- m_entries.clear();
- std::copy(first, last, std::back_inserter(m_entries));
+ accumulatedPipelineCreationTime += pipelineCreationTimer.elapsed();
}
- const QRhiTextureUploadEntry *cbeginEntries() const { return m_entries.cbegin(); }
- const QRhiTextureUploadEntry *cendEntries() const { return m_entries.cend(); }
-private:
- QVarLengthArray<QRhiTextureUploadEntry, 16> m_entries;
-};
+ qint64 totalPipelineCreationTime() const
+ {
+ return accumulatedPipelineCreationTime;
+ }
-class Q_GUI_EXPORT QRhiTextureCopyDescription
-{
-public:
- QRhiTextureCopyDescription() = default;
+ QRhiVertexInputAttribute::Format shaderDescVariableFormatToVertexInputFormat(QShaderDescription::VariableType type) const;
+ quint32 byteSizePerVertexForVertexInputFormat(QRhiVertexInputAttribute::Format format) const;
- QSize pixelSize() const { return m_pixelSize; }
- void setPixelSize(const QSize &sz) { m_pixelSize = sz; }
+ static const QRhiShaderResourceBinding::Data *shaderResourceBindingData(const QRhiShaderResourceBinding &binding)
+ {
+ return &binding.d;
+ }
- int sourceLayer() const { return m_sourceLayer; }
- void setSourceLayer(int layer) { m_sourceLayer = layer; }
+ static QRhiShaderResourceBinding::Data *shaderResourceBindingData(QRhiShaderResourceBinding &binding)
+ {
+ return &binding.d;
+ }
- int sourceLevel() const { return m_sourceLevel; }
- void setSourceLevel(int level) { m_sourceLevel = level; }
+ static bool sortedBindingLessThan(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
+ {
+ return a.d.binding < b.d.binding;
+ }
- QPoint sourceTopLeft() const { return m_sourceTopLeft; }
- void setSourceTopLeft(const QPoint &p) { m_sourceTopLeft = p; }
+ int effectiveSampleCount(int sampleCount) const;
- int destinationLayer() const { return m_destinationLayer; }
- void setDestinationLayer(int layer) { m_destinationLayer = layer; }
+ QRhi *q;
- int destinationLevel() const { return m_destinationLevel; }
- void setDestinationLevel(int level) { m_destinationLevel = level; }
+ static const int MAX_SHADER_CACHE_ENTRIES = 128;
- QPoint destinationTopLeft() const { return m_destinationTopLeft; }
- void setDestinationTopLeft(const QPoint &p) { m_destinationTopLeft = p; }
+ bool debugMarkers = false;
+ int currentFrameSlot = 0; // for vk, mtl, and similar. unused by gl and d3d11.
+ bool inFrame = false;
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_RELOCATABLE_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; }
+ QRhi::Implementation implType;
+ QThread *implThread;
+ QVarLengthArray<QRhiResourceUpdateBatch *, 4> resUpdPool;
+ quint64 resUpdPoolMap = 0;
+ int lastResUpdIdx = -1;
+ QHash<QRhiResource *, bool> resources;
+ QSet<QRhiResource *> pendingDeleteResources;
+ QVarLengthArray<QRhi::CleanupCallback, 4> cleanupCallbacks;
+ QHash<const void *, QRhi::CleanupCallback> keyedCleanupCallbacks;
+ QElapsedTimer pipelineCreationTimer;
+ qint64 accumulatedPipelineCreationTime = 0;
- 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;
+ friend class QRhi;
+ friend class QRhiResourceUpdateBatchPrivate;
};
-Q_DECLARE_TYPEINFO(QRhiReadbackDescription, Q_RELOCATABLE_TYPE);
-
-struct Q_GUI_EXPORT QRhiNativeHandles
+enum QRhiTargetRectBoundMode
{
+ UnBounded,
+ Bounded
};
-class Q_GUI_EXPORT QRhiResource
+template<QRhiTargetRectBoundMode boundingMode, 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)
{
-public:
- enum Type {
- Buffer,
- Texture,
- Sampler,
- RenderBuffer,
- RenderPassDescriptor,
- SwapChainRenderTarget,
- TextureRenderTarget,
- ShaderResourceBindings,
- GraphicsPipeline,
- SwapChain,
- ComputePipeline,
- CommandBuffer
- };
-
- virtual ~QRhiResource();
+ // x,y are bottom-left in QRhiScissor and QRhiViewport but top-left in
+ // Vulkan/Metal/D3D. Our input is an OpenGL-style scissor rect where both
+ // negative x or y, and partly or completely out of bounds rects are
+ // allowed. The only thing the input here cannot have is a negative width
+ // or height. We must handle all other input gracefully, clamping to a zero
+ // width or height rect in the worst case, and ensuring the resulting rect
+ // is inside the rendertarget's bounds because some APIs' validation/debug
+ // layers are allergic to out of bounds scissor rects.
- virtual Type resourceType() const = 0;
+ const T outputWidth = outputSize.width();
+ const T outputHeight = outputSize.height();
+ const T inputWidth = r[2];
+ const T inputHeight = r[3];
- virtual void destroy() = 0;
+ if (inputWidth < 0 || inputHeight < 0)
+ return false;
- void deleteLater();
+ *x = r[0];
+ *y = outputHeight - (r[1] + inputHeight);
+ *w = inputWidth;
+ *h = inputHeight;
- QByteArray name() const;
- void setName(const QByteArray &name);
+ if (boundingMode == Bounded) {
+ const T widthOffset = *x < 0 ? -*x : 0;
+ const T heightOffset = *y < 0 ? -*y : 0;
+ *w = *x < outputWidth ? qMax<T>(0, inputWidth - widthOffset) : 0;
+ *h = *y < outputHeight ? qMax<T>(0, inputHeight - heightOffset) : 0;
- quint64 globalResourceId() const;
+ if (outputWidth > 0)
+ *x = qBound<T>(0, *x, outputWidth - 1);
+ if (outputHeight > 0)
+ *y = qBound<T>(0, *y, outputHeight - 1);
-protected:
- QRhiResource(QRhiImplementation *rhi);
- Q_DISABLE_COPY(QRhiResource)
- friend class QRhiImplementation;
- QRhiImplementation *m_rhi = nullptr;
- quint64 m_id;
- QByteArray m_objectName;
-};
+ if (*x + *w > outputWidth)
+ *w = qMax<T>(0, outputWidth - *x);
+ if (*y + *h > outputHeight)
+ *h = qMax<T>(0, outputHeight - *y);
+ }
+ return true;
+}
-class Q_GUI_EXPORT QRhiBuffer : public QRhiResource
+struct QRhiBufferDataPrivate
{
-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)
-
- struct NativeBuffer {
- const void *objects[3];
- int slotCount;
- };
-
- 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 create() = 0;
-
- virtual NativeBuffer nativeBuffer();
-
- virtual char *beginFullDynamicBufferUpdateForCurrentFrame();
- virtual void endFullDynamicBufferUpdateForCurrentFrame();
-
-protected:
- QRhiBuffer(QRhiImplementation *rhi, Type type_, UsageFlags usage_, int size_);
- Type m_type;
- UsageFlags m_usage;
- int m_size;
+ Q_DISABLE_COPY_MOVE(QRhiBufferDataPrivate)
+ QRhiBufferDataPrivate() { }
+ ~QRhiBufferDataPrivate() { delete[] largeData; }
+ int ref = 1;
+ quint32 size = 0;
+ quint32 largeAlloc = 0;
+ char *largeData = nullptr;
+ static constexpr quint32 SMALL_DATA_SIZE = 1024;
+ char data[SMALL_DATA_SIZE];
};
-Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiBuffer::UsageFlags)
-
-class Q_GUI_EXPORT QRhiTexture : public QRhiResource
+// no detach-with-contents, no atomic refcount, no shrink
+class QRhiBufferData
{
public:
- enum Flag {
- RenderTarget = 1 << 0,
- CubeMap = 1 << 2,
- MipMapped = 1 << 3,
- sRGB = 1 << 4,
- UsedAsTransferSource = 1 << 5,
- UsedWithGenerateMips = 1 << 6,
- UsedWithLoadStore = 1 << 7,
- UsedAsCompressedAtlas = 1 << 8,
- ExternalOES = 1 << 9,
- ThreeDimensional = 1 << 10,
- TextureRectangleGL = 1 << 11,
- TextureArray = 1 << 12
- };
- Q_DECLARE_FLAGS(Flags, Flag)
-
- enum Format {
- UnknownFormat,
-
- RGBA8,
- BGRA8,
- R8,
- RG8,
- R16,
- RG16,
- RED_OR_ALPHA8,
-
- RGBA16F,
- RGBA32F,
- R16F,
- R32F,
-
- RGB10A2,
-
- D16,
- D24,
- D24S8,
- 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
- };
-
- struct NativeTexture {
- quint64 object;
- int layout;
- };
-
- 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; }
-
- int depth() const { return m_depth; }
- void setDepth(int depth) { m_depth = depth; }
-
- int arraySize() const { return m_arraySize; }
- void setArraySize(int arraySize) { m_arraySize = arraySize; }
-
- int arrayRangeStart() const { return m_arrayRangeStart; }
- int arrayRangeLength() const { return m_arrayRangeLength; }
- void setArrayRange(int startIndex, int count)
+ QRhiBufferData() = default;
+ ~QRhiBufferData()
{
- m_arrayRangeStart = startIndex;
- m_arrayRangeLength = count;
+ if (d && !--d->ref)
+ delete d;
}
-
- 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 create() = 0;
- virtual NativeTexture nativeTexture();
- virtual bool createFrom(NativeTexture src);
- virtual void setNativeLayout(int layout);
-
-protected:
- QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, int depth_,
- int arraySize_, int sampleCount_, Flags flags_);
- Format m_format;
- QSize m_pixelSize;
- int m_depth;
- int m_arraySize;
- int m_sampleCount;
- Flags m_flags;
- int m_arrayRangeStart = -1;
- int m_arrayRangeLength = -1;
+ QRhiBufferData(const QRhiBufferData &other)
+ : d(other.d)
+ {
+ if (d)
+ d->ref += 1;
+ }
+ QRhiBufferData &operator=(const QRhiBufferData &other)
+ {
+ if (d == other.d)
+ return *this;
+ if (other.d)
+ other.d->ref += 1;
+ if (d && !--d->ref)
+ delete d;
+ d = other.d;
+ return *this;
+ }
+ const char *constData() const
+ {
+ return d->size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE ? d->data : d->largeData;
+ }
+ quint32 size() const
+ {
+ return d->size;
+ }
+ void assign(const char *s, quint32 size)
+ {
+ if (!d) {
+ d = new QRhiBufferDataPrivate;
+ } else if (d->ref != 1) {
+ d->ref -= 1;
+ d = new QRhiBufferDataPrivate;
+ }
+ d->size = size;
+ if (size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE) {
+ memcpy(d->data, s, size);
+ } else {
+ if (d->largeAlloc < size) {
+ delete[] d->largeData;
+ d->largeAlloc = size;
+ d->largeData = new char[size];
+ }
+ memcpy(d->largeData, s, size);
+ }
+ }
+private:
+ QRhiBufferDataPrivate *d = nullptr;
};
-Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiTexture::Flags)
+Q_DECLARE_TYPEINFO(QRhiBufferData, Q_RELOCATABLE_TYPE);
-class Q_GUI_EXPORT QRhiSampler : public QRhiResource
+class QRhiResourceUpdateBatchPrivate
{
public:
- enum Filter {
- None,
- Nearest,
- Linear
- };
-
- enum AddressMode {
- Repeat,
- ClampToEdge,
- Mirror,
- };
-
- 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 create() = 0;
+ struct BufferOp {
+ enum Type {
+ DynamicUpdate,
+ StaticUpload,
+ Read
+ };
+ Type type;
+ QRhiBuffer *buf;
+ quint32 offset;
+ QRhiBufferData data;
+ quint32 readSize;
+ QRhiReadbackResult *result;
+
+ static BufferOp dynamicUpdate(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
+ {
+ BufferOp op = {};
+ op.type = DynamicUpdate;
+ op.buf = buf;
+ op.offset = offset;
+ const int effectiveSize = size ? size : buf->size();
+ op.data.assign(reinterpret_cast<const char *>(data), effectiveSize);
+ return op;
+ }
-protected:
- QRhiSampler(QRhiImplementation *rhi,
- Filter magFilter_, Filter minFilter_, Filter mipmapMode_,
- AddressMode u_, AddressMode v_, AddressMode w_);
- Filter m_magFilter;
- Filter m_minFilter;
- Filter m_mipmapMode;
- AddressMode m_addressU;
- AddressMode m_addressV;
- AddressMode m_addressW;
- CompareOp m_compareOp;
-};
+ static void changeToDynamicUpdate(BufferOp *op, QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
+ {
+ op->type = DynamicUpdate;
+ op->buf = buf;
+ op->offset = offset;
+ const int effectiveSize = size ? size : buf->size();
+ op->data.assign(reinterpret_cast<const char *>(data), effectiveSize);
+ }
-class Q_GUI_EXPORT QRhiRenderBuffer : public QRhiResource
-{
-public:
- enum Type {
- DepthStencil,
- Color
- };
+ static BufferOp staticUpload(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
+ {
+ BufferOp op = {};
+ op.type = StaticUpload;
+ op.buf = buf;
+ op.offset = offset;
+ const int effectiveSize = size ? size : buf->size();
+ op.data.assign(reinterpret_cast<const char *>(data), effectiveSize);
+ return op;
+ }
- enum Flag {
- UsedWithSwapChainOnly = 1 << 0
- };
- Q_DECLARE_FLAGS(Flags, Flag)
+ static void changeToStaticUpload(BufferOp *op, QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
+ {
+ op->type = StaticUpload;
+ op->buf = buf;
+ op->offset = offset;
+ const int effectiveSize = size ? size : buf->size();
+ op->data.assign(reinterpret_cast<const char *>(data), effectiveSize);
+ }
- struct NativeRenderBuffer {
- quint64 object;
+ static BufferOp read(QRhiBuffer *buf, quint32 offset, quint32 size, QRhiReadbackResult *result)
+ {
+ BufferOp op = {};
+ op.type = Read;
+ op.buf = buf;
+ op.offset = offset;
+ op.readSize = size;
+ op.result = result;
+ return op;
+ }
};
- 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 create() = 0;
- virtual bool createFrom(NativeRenderBuffer src);
+ struct TextureOp {
+ enum Type {
+ Upload,
+ Copy,
+ Read,
+ GenMips
+ };
+ Type type;
+ QRhiTexture *dst;
+ // 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.
+ using MipLevelUploadList = std::array<QVector<QRhiTextureSubresourceUploadDescription>, QRhi::MAX_MIP_LEVELS>;
+ QVarLengthArray<MipLevelUploadList, 6> subresDesc;
+ QRhiTexture *src;
+ QRhiTextureCopyDescription desc;
+ QRhiReadbackDescription rb;
+ QRhiReadbackResult *result;
+
+ static TextureOp upload(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
+ {
+ TextureOp op = {};
+ op.type = Upload;
+ op.dst = tex;
+ int maxLayer = -1;
+ for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it) {
+ if (it->layer() > maxLayer)
+ maxLayer = it->layer();
+ }
+ op.subresDesc.resize(maxLayer + 1);
+ for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it)
+ op.subresDesc[it->layer()][it->level()].append(it->description());
+ return op;
+ }
- virtual QRhiTexture::Format backingFormat() const = 0;
+ static TextureOp copy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc)
+ {
+ TextureOp op = {};
+ op.type = Copy;
+ op.dst = dst;
+ op.src = src;
+ op.desc = desc;
+ return op;
+ }
-protected:
- QRhiRenderBuffer(QRhiImplementation *rhi, Type type_, const QSize &pixelSize_,
- int sampleCount_, Flags flags_, QRhiTexture::Format backingFormatHint_);
- Type m_type;
- QSize m_pixelSize;
- int m_sampleCount;
- Flags m_flags;
- QRhiTexture::Format m_backingFormatHint;
-};
+ static TextureOp read(const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
+ {
+ TextureOp op = {};
+ op.type = Read;
+ op.rb = rb;
+ op.result = result;
+ return op;
+ }
-Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiRenderBuffer::Flags)
+ static TextureOp genMips(QRhiTexture *tex)
+ {
+ TextureOp op = {};
+ op.type = GenMips;
+ op.dst = tex;
+ return op;
+ }
+ };
-class Q_GUI_EXPORT QRhiRenderPassDescriptor : public QRhiResource
-{
-public:
- QRhiResource::Type resourceType() const override;
+ int activeBufferOpCount = 0; // this is the real number of used elements in bufferOps, not bufferOps.count()
+ static const int BUFFER_OPS_STATIC_ALLOC = 1024;
+ QVarLengthArray<BufferOp, BUFFER_OPS_STATIC_ALLOC> bufferOps;
- virtual bool isCompatible(const QRhiRenderPassDescriptor *other) const = 0;
- virtual const QRhiNativeHandles *nativeHandles();
+ int activeTextureOpCount = 0; // this is the real number of used elements in textureOps, not textureOps.count()
+ static const int TEXTURE_OPS_STATIC_ALLOC = 256;
+ QVarLengthArray<TextureOp, TEXTURE_OPS_STATIC_ALLOC> textureOps;
- virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const = 0;
+ QRhiResourceUpdateBatch *q = nullptr;
+ QRhiImplementation *rhi = nullptr;
+ int poolIndex = -1;
- virtual QVector<quint32> serializedFormat() const = 0;
+ void free();
+ void merge(QRhiResourceUpdateBatchPrivate *other);
+ bool hasOptimalCapacity() const;
+ void trimOpLists();
-protected:
- QRhiRenderPassDescriptor(QRhiImplementation *rhi);
+ static QRhiResourceUpdateBatchPrivate *get(QRhiResourceUpdateBatch *b) { return b->d; }
};
-class Q_GUI_EXPORT QRhiRenderTarget : public QRhiResource
+template<typename T>
+struct QRhiBatchedBindings
{
-public:
- virtual QSize pixelSize() const = 0;
- virtual float devicePixelRatio() const = 0;
- virtual int sampleCount() const = 0;
+ 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;
+ }
- QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_renderPassDesc; }
- void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc) { m_renderPassDesc = desc; }
+ bool finish() {
+ if (!curBatch.resources.isEmpty())
+ batches.append(curBatch);
+ return !batches.isEmpty();
+ }
-protected:
- QRhiRenderTarget(QRhiImplementation *rhi);
- QRhiRenderPassDescriptor *m_renderPassDesc = nullptr;
-};
+ void clear() {
+ batches.clear();
+ curBatch.resources.clear();
+ curBinding = -1;
+ }
-class Q_GUI_EXPORT QRhiSwapChainRenderTarget : public QRhiRenderTarget
-{
-public:
- QRhiResource::Type resourceType() const override;
- QRhiSwapChain *swapChain() const { return m_swapchain; }
+ struct Batch {
+ uint startBinding;
+ QVarLengthArray<T, 4> resources;
-protected:
- QRhiSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain_);
- QRhiSwapChain *m_swapchain;
-};
+ bool operator==(const Batch &other) const
+ {
+ return startBinding == other.startBinding && resources == other.resources;
+ }
-class Q_GUI_EXPORT QRhiTextureRenderTarget : public QRhiRenderTarget
-{
-public:
- enum Flag {
- PreserveColorContents = 1 << 0,
- PreserveDepthStencilContents = 1 << 1
+ bool operator!=(const Batch &other) const
+ {
+ return !operator==(other);
+ }
};
- Q_DECLARE_FLAGS(Flags, Flag)
-
- QRhiResource::Type resourceType() const override;
- QRhiTextureRenderTargetDescription description() const { return m_desc; }
- void setDescription(const QRhiTextureRenderTargetDescription &desc) { m_desc = desc; }
+ QVarLengthArray<Batch, 4> batches; // sorted by startBinding
- Flags flags() const { return m_flags; }
- void setFlags(Flags f) { m_flags = f; }
-
- virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() = 0;
+ bool operator==(const QRhiBatchedBindings<T> &other) const
+ {
+ return batches == other.batches;
+ }
- virtual bool create() = 0;
+ bool operator!=(const QRhiBatchedBindings<T> &other) const
+ {
+ return !operator==(other);
+ }
-protected:
- QRhiTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc_, Flags flags_);
- QRhiTextureRenderTargetDescription m_desc;
- Flags m_flags;
+private:
+ Batch curBatch;
+ int curBinding = -1;
};
-Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiTextureRenderTarget::Flags)
-
-class Q_GUI_EXPORT QRhiShaderResourceBindings : public QRhiResource
+class QRhiGlobalObjectIdGenerator
{
public:
- QRhiResource::Type resourceType() const override;
-
- void setBindings(std::initializer_list<QRhiShaderResourceBinding> list) { m_bindings = list; }
-
- template<typename InputIterator>
- void setBindings(InputIterator first, InputIterator last)
- {
- m_bindings.clear();
- std::copy(first, last, std::back_inserter(m_bindings));
- }
-
- const QRhiShaderResourceBinding *cbeginBindings() const { return m_bindings.cbegin(); }
- const QRhiShaderResourceBinding *cendBindings() const { return m_bindings.cend(); }
-
- bool isLayoutCompatible(const QRhiShaderResourceBindings *other) const;
-
- QVector<quint32> serializedLayoutDescription() const { return m_layoutDesc; }
-
- virtual bool create() = 0;
-
- enum UpdateFlag {
- BindingsAreSorted = 0x01
- };
- Q_DECLARE_FLAGS(UpdateFlags, UpdateFlag)
-
- virtual void updateResources(UpdateFlags flags = {}) = 0;
-
-protected:
- static const int BINDING_PREALLOC = 12;
- QRhiShaderResourceBindings(QRhiImplementation *rhi);
- QVarLengthArray<QRhiShaderResourceBinding, BINDING_PREALLOC> m_bindings;
- size_t m_layoutDescHash = 0;
- // Intentionally not using QVLA for m_layoutDesc: clients like Qt Quick are much
- // better served with an implicitly shared container here, because they will likely
- // throw this directly into structs serving as cache keys.
- QVector<quint32> m_layoutDesc;
- friend class QRhiImplementation;
-#ifndef QT_NO_DEBUG_STREAM
- friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBindings &);
+#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
+ using Type = quint64;
+#else
+ using Type = quint32;
#endif
+ static Type newId();
};
-Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiShaderResourceBindings::UpdateFlags)
-
-#ifndef QT_NO_DEBUG_STREAM
-Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBindings &);
-#endif
-
-class Q_GUI_EXPORT QRhiGraphicsPipeline : public QRhiResource
+class QRhiPassResourceTracker
{
public:
- enum Flag {
- UsesBlendConstants = 1 << 0,
- UsesStencilRef = 1 << 1,
- UsesScissor = 1 << 2,
- CompileShadersWithDebugInfo = 1 << 3
- };
- Q_DECLARE_FLAGS(Flags, Flag)
-
- enum Topology {
- Triangles,
- TriangleStrip,
- TriangleFan,
- Lines,
- LineStrip,
- Points,
- Patches
- };
-
- 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
- };
+ bool isEmpty() const;
+ void reset();
- 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;
+ struct UsageState {
+ int layout;
+ int access;
+ int stage;
};
- enum PolygonMode {
- Fill,
- Line
+ enum BufferStage {
+ BufVertexInputStage,
+ BufVertexStage,
+ BufTCStage,
+ BufTEStage,
+ BufFragmentStage,
+ BufComputeStage,
+ BufGeometryStage
};
- 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; }
-
- void setTargetBlends(std::initializer_list<TargetBlend> list) { m_targetBlends = list; }
- template<typename InputIterator>
- void setTargetBlends(InputIterator first, InputIterator last)
- {
- m_targetBlends.clear();
- std::copy(first, last, std::back_inserter(m_targetBlends));
- }
- const TargetBlend *cbeginTargetBlends() const { return m_targetBlends.cbegin(); }
- const TargetBlend *cendTargetBlends() const { return m_targetBlends.cend(); }
-
- 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; }
-
- float lineWidth() const { return m_lineWidth; }
- void setLineWidth(float width) { m_lineWidth = width; }
-
- int depthBias() const { return m_depthBias; }
- void setDepthBias(int bias) { m_depthBias = bias; }
-
- float slopeScaledDepthBias() const { return m_slopeScaledDepthBias; }
- void setSlopeScaledDepthBias(float bias) { m_slopeScaledDepthBias = bias; }
-
- void setShaderStages(std::initializer_list<QRhiShaderStage> list) { m_shaderStages = list; }
- template<typename InputIterator>
- void setShaderStages(InputIterator first, InputIterator last)
- {
- m_shaderStages.clear();
- std::copy(first, last, std::back_inserter(m_shaderStages));
- }
- const QRhiShaderStage *cbeginShaderStages() const { return m_shaderStages.cbegin(); }
- const QRhiShaderStage *cendShaderStages() const { return m_shaderStages.cend(); }
-
- 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; }
-
- int patchControlPointCount() const { return m_patchControlPointCount; }
- void setPatchControlPointCount(int count) { m_patchControlPointCount = count; }
-
- PolygonMode polygonMode() const {return m_polygonMode; }
- void setPolygonMode(PolygonMode mode) {m_polygonMode = mode; }
-
- virtual bool create() = 0;
-
-protected:
- QRhiGraphicsPipeline(QRhiImplementation *rhi);
- Flags m_flags;
- Topology m_topology = Triangles;
- CullMode m_cullMode = None;
- FrontFace m_frontFace = CCW;
- QVarLengthArray<TargetBlend, 8> 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;
- float m_lineWidth = 1.0f;
- int m_depthBias = 0;
- float m_slopeScaledDepthBias = 0.0f;
- int m_patchControlPointCount = 3;
- PolygonMode m_polygonMode = Fill;
- QVarLengthArray<QRhiShaderStage, 4> 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_RELOCATABLE_TYPE);
-
-struct QRhiSwapChainHdrInfo
-{
- bool isHardCodedDefaults;
- enum LimitsType {
- LuminanceInNits,
- ColorComponentValue
+ enum BufferAccess {
+ BufVertexInput,
+ BufIndexRead,
+ BufUniformRead,
+ BufStorageLoad,
+ BufStorageStore,
+ BufStorageLoadStore
};
- LimitsType limitsType;
- union {
- struct {
- float minLuminance;
- float maxLuminance;
- } luminanceInNits;
- struct {
- float maxColorComponentValue;
- } colorComponentValue;
- } limits;
-};
-Q_DECLARE_TYPEINFO(QRhiSwapChainHdrInfo, Q_RELOCATABLE_TYPE);
+ void registerBuffer(QRhiBuffer *buf, int slot, BufferAccess *access, BufferStage *stage,
+ const UsageState &state);
-#ifndef QT_NO_DEBUG_STREAM
-Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiSwapChainHdrInfo &);
-#endif
-
-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
+ enum TextureStage {
+ TexVertexStage,
+ TexTCStage,
+ TexTEStage,
+ TexFragmentStage,
+ TexColorOutputStage,
+ TexDepthOutputStage,
+ TexComputeStage,
+ TexGeometryStage
};
- Q_DECLARE_FLAGS(Flags, Flag)
- enum Format {
- SDR,
- HDRExtendedSrgbLinear,
- HDR10
+ enum TextureAccess {
+ TexSample,
+ TexColorOutput,
+ TexDepthOutput,
+ TexStorageLoad,
+ TexStorageStore,
+ TexStorageLoadStore
};
- 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; }
-
- Format format() const { return m_format; }
- void setFormat(Format f) { m_format = 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 bool isFormatSupported(Format f) = 0;
- virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() = 0;
- virtual bool createOrResize() = 0;
- virtual QRhiSwapChainHdrInfo hdrInfo();
-
-protected:
- QRhiSwapChain(QRhiImplementation *rhi);
- QWindow *m_window = nullptr;
- Flags m_flags;
- Format m_format = SDR;
- QRhiRenderBuffer *m_depthStencil = nullptr;
- int m_sampleCount = 1;
- QRhiRenderPassDescriptor *m_renderPassDesc = nullptr;
- QSize m_currentPixelSize;
-};
-
-Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiSwapChain::Flags)
+ void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage,
+ const UsageState &state);
-class Q_GUI_EXPORT QRhiComputePipeline : public QRhiResource
-{
-public:
- enum Flag {
- CompileShadersWithDebugInfo = 1 << 0
+ struct Buffer {
+ int slot;
+ BufferAccess access;
+ BufferStage stage;
+ UsageState stateAtPassBegin;
};
- Q_DECLARE_FLAGS(Flags, Flag)
-
- QRhiResource::Type resourceType() const override;
- virtual bool create() = 0;
-
- Flags flags() const { return m_flags; }
- void setFlags(Flags f) { m_flags = f; }
-
- 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);
- Flags m_flags;
- QRhiShaderStage m_shaderStage;
- QRhiShaderResourceBindings *m_shaderResourceBindings = nullptr;
-};
-Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiComputePipeline::Flags)
+ using BufferIterator = QHash<QRhiBuffer *, Buffer>::const_iterator;
+ BufferIterator cbeginBuffers() const { return m_buffers.cbegin(); }
+ BufferIterator cendBuffers() const { return m_buffers.cend(); }
-class Q_GUI_EXPORT QRhiCommandBuffer : public QRhiResource
-{
-public:
- enum IndexFormat {
- IndexUInt16,
- IndexUInt32
+ struct Texture {
+ TextureAccess access;
+ TextureStage stage;
+ UsageState stateAtPassBegin;
};
- enum BeginPassFlag {
- ExternalContent = 0x01,
- DoNotTrackResourcesForCompute = 0x02
- };
- Q_DECLARE_FLAGS(BeginPassFlags, BeginPassFlag)
-
- QRhiResource::Type resourceType() const override;
-
- void resourceUpdate(QRhiResourceUpdateBatch *resourceUpdates);
-
- void beginPass(QRhiRenderTarget *rt,
- const QColor &colorClearValue,
- const QRhiDepthStencilClearValue &depthStencilClearValue,
- QRhiResourceUpdateBatch *resourceUpdates = nullptr,
- BeginPassFlags flags = {});
- 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, BeginPassFlags flags = {});
- 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);
-};
+ using TextureIterator = QHash<QRhiTexture *, Texture>::const_iterator;
+ TextureIterator cbeginTextures() const { return m_textures.cbegin(); }
+ TextureIterator cendTextures() const { return m_textures.cend(); }
-Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiCommandBuffer::BeginPassFlags)
+ static BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages);
+ static TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages);
-struct Q_GUI_EXPORT QRhiReadbackResult
-{
- std::function<void()> completed = nullptr;
- QRhiTexture::Format format;
- QSize pixelSize;
- QByteArray data;
+private:
+ QHash<QRhiBuffer *, Buffer> m_buffers;
+ QHash<QRhiTexture *, Texture> m_textures;
};
-struct Q_GUI_EXPORT QRhiBufferReadbackResult
-{
- std::function<void()> completed = nullptr;
- QByteArray data;
-};
+Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Buffer, Q_RELOCATABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Texture, Q_RELOCATABLE_TYPE);
-class Q_GUI_EXPORT QRhiResourceUpdateBatch
+template<typename T, int GROW = 1024>
+class QRhiBackendCommandList
{
public:
- ~QRhiResourceUpdateBatch();
-
- void release();
-
- void merge(QRhiResourceUpdateBatch *other);
- bool hasOptimalCapacity() const;
-
- 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 readBackBuffer(QRhiBuffer *buf, int offset, int size, QRhiBufferReadbackResult *result);
- 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);
-
+ QRhiBackendCommandList() = default;
+ ~QRhiBackendCommandList() { delete[] v; }
+ inline void reset() { p = 0; }
+ inline bool isEmpty() const { return p == 0; }
+ inline T &get() {
+ if (p == a) {
+ a += GROW;
+ T *nv = new T[a];
+ if (v) {
+ memcpy(nv, v, p * sizeof(T));
+ delete[] v;
+ }
+ v = nv;
+ }
+ return v[p++];
+ }
+ inline void unget() { --p; }
+ inline T *cbegin() const { return v; }
+ inline T *cend() const { return v + p; }
+ inline T *begin() { return v; }
+ inline T *end() { return v + p; }
private:
- QRhiResourceUpdateBatch(QRhiImplementation *rhi);
- Q_DISABLE_COPY(QRhiResourceUpdateBatch)
- QRhiResourceUpdateBatchPrivate *d;
- friend class QRhiResourceUpdateBatchPrivate;
- friend class QRhi;
+ Q_DISABLE_COPY(QRhiBackendCommandList)
+ T *v = nullptr;
+ int a = 0;
+ int p = 0;
};
-struct Q_GUI_EXPORT QRhiDriverInfo
+struct QRhiRenderTargetAttachmentTracker
{
- enum DeviceType {
- UnknownDevice,
- IntegratedDevice,
- DiscreteDevice,
- ExternalDevice,
- VirtualDevice,
- CpuDevice
- };
+ struct ResId { quint64 id; uint generation; };
+ using ResIdList = QVarLengthArray<ResId, 8 * 2 + 1>; // color, resolve, ds
- QByteArray deviceName;
- quint64 deviceId = 0;
- quint64 vendorId = 0;
- DeviceType deviceType = UnknownDevice;
-};
-
-Q_DECLARE_TYPEINFO(QRhiDriverInfo, Q_RELOCATABLE_TYPE);
+ template<typename TexType, typename RenderBufferType>
+ static void updateResIdList(const QRhiTextureRenderTargetDescription &desc, ResIdList *dst);
-#ifndef QT_NO_DEBUG_STREAM
-Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiDriverInfo &);
-#endif
-
-struct Q_GUI_EXPORT QRhiMemAllocStats
-{
- quint32 blockCount = 0;
- quint32 allocCount = 0;
- quint64 usedBytes = 0;
- quint64 unusedBytes = 0;
+ template<typename TexType, typename RenderBufferType>
+ static bool isUpToDate(const QRhiTextureRenderTargetDescription &desc, const ResIdList &currentResIdList);
};
-Q_DECLARE_TYPEINFO(QRhiMemAllocStats, Q_RELOCATABLE_TYPE);
-
-#ifndef QT_NO_DEBUG_STREAM
-Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiMemAllocStats &);
-#endif
-
-struct Q_GUI_EXPORT QRhiInitParams
+inline bool operator==(const QRhiRenderTargetAttachmentTracker::ResId &a, const QRhiRenderTargetAttachmentTracker::ResId &b)
{
-};
+ return a.id == b.id && a.generation == b.generation;
+}
-class Q_GUI_EXPORT QRhi
+inline bool operator!=(const QRhiRenderTargetAttachmentTracker::ResId &a, const QRhiRenderTargetAttachmentTracker::ResId &b)
{
-public:
- enum Implementation {
- Null,
- Vulkan,
- OpenGLES2,
- D3D11,
- Metal
- };
-
- enum Flag {
- EnableProfiling = 1 << 0,
- EnableDebugMarkers = 1 << 1,
- PreferSoftwareRenderer = 1 << 2,
- EnablePipelineCacheDataSave = 1 << 3
- };
- 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,
- WideLines,
- VertexShaderPointSize,
- BaseVertex,
- BaseInstance,
- TriangleFanTopology,
- ReadBackNonUniformBuffer,
- ReadBackNonBaseMipLevel,
- TexelFetch,
- RenderToNonBaseMipLevel,
- IntAttributes,
- ScreenSpaceDerivatives,
- ReadBackAnyTextureFormat,
- PipelineCacheDataLoadSave,
- ImageDataStride,
- RenderBufferImport,
- ThreeDimensionalTextures,
- RenderTo3DTextureSlice,
- TextureArrays,
- Tessellation,
- GeometryShader,
- TextureArrayRange,
- NonFillPolygonMode
- };
-
- enum BeginFrameFlag {
- };
- Q_DECLARE_FLAGS(BeginFrameFlags, BeginFrameFlag)
-
- enum EndFrameFlag {
- SkipPresent = 1 << 0
- };
- Q_DECLARE_FLAGS(EndFrameFlags, EndFrameFlag)
-
- enum ResourceLimit {
- TextureSizeMin = 1,
- TextureSizeMax,
- MaxColorAttachments,
- FramesInFlight,
- MaxAsyncReadbackFrames,
- MaxThreadGroupsPerDimension,
- MaxThreadsPerThreadGroup,
- MaxThreadGroupX,
- MaxThreadGroupY,
- MaxThreadGroupZ,
- TextureArraySizeMax,
- MaxUniformBufferRange,
- MaxVertexInputs,
- MaxVertexOutputs
- };
-
- ~QRhi();
-
- static QRhi *create(Implementation impl,
- QRhiInitParams *params,
- Flags flags = {},
- QRhiNativeHandles *importDevice = nullptr);
- static bool probe(Implementation impl, QRhiInitParams *params);
-
- Implementation backend() const;
- const char *backendName() const;
- QRhiDriverInfo driverInfo() const;
- QThread *thread() const;
-
- using CleanupCallback = std::function<void(QRhi *)>;
- void addCleanupCallback(const CleanupCallback &callback);
- void runCleanup();
-
- using GpuFrameTimeCallback = std::function<void(float t)>;
- void addGpuFrameTimeCallback(const GpuFrameTimeCallback &callback);
-
- 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 = {},
- QRhiTexture::Format backingFormatHint = QRhiTexture::UnknownFormat);
-
- QRhiTexture *newTexture(QRhiTexture::Format format,
- const QSize &pixelSize,
- int sampleCount = 1,
- QRhiTexture::Flags flags = {});
-
- QRhiTexture *newTexture(QRhiTexture::Format format,
- int width, int height, int depth,
- int sampleCount = 1,
- QRhiTexture::Flags flags = {});
+ return !(a == b);
+}
- QRhiTexture *newTextureArray(QRhiTexture::Format format,
- int arraySize,
- const QSize &pixelSize,
- int sampleCount = 1,
- QRhiTexture::Flags flags = {});
-
- QRhiSampler *newSampler(QRhiSampler::Filter magFilter,
- QRhiSampler::Filter minFilter,
- QRhiSampler::Filter mipmapMode,
- QRhiSampler::AddressMode addressU,
- QRhiSampler::AddressMode addressV,
- QRhiSampler::AddressMode addressW = QRhiSampler::Repeat);
-
- QRhiTextureRenderTarget *newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
- QRhiTextureRenderTarget::Flags flags = {});
-
- QRhiSwapChain *newSwapChain();
- FrameOpResult beginFrame(QRhiSwapChain *swapChain, BeginFrameFlags flags = {});
- FrameOpResult endFrame(QRhiSwapChain *swapChain, EndFrameFlags flags = {});
- bool isRecordingFrame() const;
- int currentFrameSlot() const;
-
- FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, BeginFrameFlags flags = {});
- FrameOpResult endOffscreenFrame(EndFrameFlags flags = {});
-
- QRhi::FrameOpResult finish();
-
- QRhiResourceUpdateBatch *nextResourceUpdateBatch();
-
- QList<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 = {}) const;
- bool isFeatureSupported(QRhi::Feature feature) const;
- int resourceLimit(ResourceLimit limit) const;
-
- const QRhiNativeHandles *nativeHandles();
- bool makeThreadLocalNativeContextCurrent();
-
- static const int MAX_MIP_LEVELS = 16; // a width and/or height of 65536 should be enough for everyone
-
- void releaseCachedResources();
-
- bool isDeviceLost() const;
-
- QByteArray pipelineCacheData();
- void setPipelineCacheData(const QByteArray &data);
-
- QRhiMemAllocStats graphicsMemoryAllocationStatistics() const;
-
-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)
+template<typename TexType, typename RenderBufferType>
+void QRhiRenderTargetAttachmentTracker::updateResIdList(const QRhiTextureRenderTargetDescription &desc, ResIdList *dst)
+{
+ const bool hasDepthStencil = desc.depthStencilBuffer() || desc.depthTexture();
+ dst->resize(desc.colorAttachmentCount() * 2 + (hasDepthStencil ? 1 : 0));
+ int n = 0;
+ for (auto it = desc.cbeginColorAttachments(), itEnd = desc.cendColorAttachments(); it != itEnd; ++it, ++n) {
+ const QRhiColorAttachment &colorAtt(*it);
+ if (colorAtt.texture()) {
+ TexType *texD = QRHI_RES(TexType, colorAtt.texture());
+ (*dst)[n] = { texD->globalResourceId(), texD->generation };
+ } else if (colorAtt.renderBuffer()) {
+ RenderBufferType *rbD = QRHI_RES(RenderBufferType, colorAtt.renderBuffer());
+ (*dst)[n] = { rbD->globalResourceId(), rbD->generation };
+ } else {
+ (*dst)[n] = { 0, 0 };
+ }
+ ++n;
+ if (colorAtt.resolveTexture()) {
+ TexType *texD = QRHI_RES(TexType, colorAtt.resolveTexture());
+ (*dst)[n] = { texD->globalResourceId(), texD->generation };
+ } else {
+ (*dst)[n] = { 0, 0 };
+ }
+ }
+ if (hasDepthStencil) {
+ if (desc.depthTexture()) {
+ TexType *depthTexD = QRHI_RES(TexType, desc.depthTexture());
+ (*dst)[n] = { depthTexD->globalResourceId(), depthTexD->generation };
+ } else if (desc.depthStencilBuffer()) {
+ RenderBufferType *depthRbD = QRHI_RES(RenderBufferType, desc.depthStencilBuffer());
+ (*dst)[n] = { depthRbD->globalResourceId(), depthRbD->generation };
+ } else {
+ (*dst)[n] = { 0, 0 };
+ }
+ }
+}
+
+template<typename TexType, typename RenderBufferType>
+bool QRhiRenderTargetAttachmentTracker::isUpToDate(const QRhiTextureRenderTargetDescription &desc, const ResIdList &currentResIdList)
+{
+ // Just as setShaderResources() recognizes if an srb's referenced
+ // resources have been rebuilt (got a create() since the srb's
+ // create()), we should do the same for the textures and renderbuffers
+ // referenced from the rendertarget. It is not uncommon that a texture
+ // or ds buffer gets resized due to following a window size in some
+ // form, which involves a create() on them. It is then nice if the
+ // render target auto-rebuilds in beginPass().
+
+ ResIdList resIdList;
+ updateResIdList<TexType, RenderBufferType>(desc, &resIdList);
+ return resIdList == currentResIdList;
+}
+
+template<typename T>
+inline T *qrhi_objectFromProxyData(QRhiSwapChainProxyData *pd, QWindow *window, QRhi::Implementation impl, uint objectIndex)
+{
+ Q_ASSERT(objectIndex < std::size(pd->reserved));
+ if (!pd->reserved[objectIndex]) // // was not set, no other choice, do it here, whatever thread this is
+ *pd = QRhi::updateSwapChainProxyData(impl, window);
+ return static_cast<T *>(pd->reserved[objectIndex]);
+}
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h
deleted file mode 100644
index 2a0cb7d631..0000000000
--- a/src/gui/rhi/qrhi_p_p.h
+++ /dev/null
@@ -1,764 +0,0 @@
-// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#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 <QBitArray>
-#include <QAtomicInt>
-#include <QLoggingCategory>
-#include <QtCore/qset.h>
-#include <QtCore/qvarlengtharray.h>
-
-QT_BEGIN_NAMESPACE
-
-#define QRHI_RES(t, x) static_cast<t *>(x)
-#define QRHI_RES_RHI(t) t *rhiD = static_cast<t *>(m_rhi)
-
-Q_DECLARE_LOGGING_CATEGORY(QRHI_LOG_INFO)
-
-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,
- QRhiTexture::Format backingFormatHint) = 0;
- virtual QRhiTexture *createTexture(QRhiTexture::Format format,
- const QSize &pixelSize,
- int depth,
- int arraySize,
- int sampleCount,
- QRhiTexture::Flags flags) = 0;
- virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
- QRhiSampler::Filter minFilter,
- QRhiSampler::Filter mipmapMode,
- QRhiSampler:: AddressMode u,
- QRhiSampler::AddressMode v,
- QRhiSampler::AddressMode w) = 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, QRhi::BeginFrameFlags flags) = 0;
- virtual QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) = 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,
- QRhiCommandBuffer::BeginPassFlags flags) = 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,
- QRhiCommandBuffer::BeginPassFlags flags) = 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 QList<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 QRhiDriverInfo driverInfo() const = 0;
- virtual QRhiMemAllocStats graphicsMemoryAllocationStatistics() = 0;
- virtual bool makeThreadLocalNativeContextCurrent() = 0;
- virtual void releaseCachedResources() = 0;
- virtual bool isDeviceLost() const = 0;
-
- virtual QByteArray pipelineCacheData() = 0;
- virtual void setPipelineCacheData(const QByteArray &data) = 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, quint32 *bytesPerPixel) const;
-
- // 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 addDeleteLater(QRhiResource *res)
- {
- if (inFrame)
- pendingDeleteResources.insert(res);
- else
- delete res;
- }
-
- void addCleanupCallback(const QRhi::CleanupCallback &callback)
- {
- cleanupCallbacks.append(callback);
- }
-
- void addGpuFrameTimeCallback(const QRhi::GpuFrameTimeCallback &callback)
- {
- gpuFrameTimeCallbacks.append(callback);
- }
-
- bool hasGpuFrameTimeCallback() const
- {
- return !gpuFrameTimeCallbacks.isEmpty();
- }
-
- void runGpuFrameTimeCallbacks(float t)
- {
- for (const QRhi::GpuFrameTimeCallback &f : qAsConst(gpuFrameTimeCallbacks))
- f(t);
- }
-
- bool sanityCheckGraphicsPipeline(QRhiGraphicsPipeline *ps);
- bool sanityCheckShaderResourceBindings(QRhiShaderResourceBindings *srb);
- void updateLayoutDesc(QRhiShaderResourceBindings *srb);
-
- quint32 pipelineCacheRhiId() const
- {
- const quint32 ver = (QT_VERSION_MAJOR << 16) | (QT_VERSION_MINOR << 8) | (QT_VERSION_PATCH);
- return (quint32(implType) << 24) | ver;
- }
-
- QRhi *q;
-
- static const int MAX_SHADER_CACHE_ENTRIES = 128;
-
- 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;
- QVarLengthArray<QRhiResourceUpdateBatch *, 4> resUpdPool;
- quint64 resUpdPoolMap = 0;
- int lastResUpdIdx = -1;
- QSet<QRhiResource *> resources;
- QSet<QRhiResource *> pendingDeleteResources;
- QVarLengthArray<QRhi::CleanupCallback, 4> cleanupCallbacks;
- QVarLengthArray<QRhi::GpuFrameTimeCallback, 4> gpuFrameTimeCallbacks;
-
- 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. Our input is an OpenGL-style scissor rect where both
- // negative x or y, and partly or completely out of bounds rects are
- // allowed. The only thing the input here cannot have is a negative width
- // or height. We must handle all other input gracefully, clamping to a zero
- // width or height rect in the worst case, and ensuring the resulting rect
- // is inside the rendertarget's bounds because some APIs' 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];
-
- if (inputWidth < 0 || inputHeight < 0)
- return false;
-
- *x = r[0];
- *y = outputHeight - (r[1] + inputHeight);
-
- const T widthOffset = *x < 0 ? -*x : 0;
- const T heightOffset = *y < 0 ? -*y : 0;
- *w = *x < outputWidth ? qMax<T>(0, inputWidth - widthOffset) : 0;
- *h = *y < outputHeight ? qMax<T>(0, inputHeight - heightOffset) : 0;
-
- *x = qBound<T>(0, *x, outputWidth - 1);
- *y = qBound<T>(0, *y, outputHeight - 1);
-
- if (*x + *w > outputWidth)
- *w = qMax<T>(0, outputWidth - *x);
- if (*y + *h > outputHeight)
- *h = qMax<T>(0, outputHeight - *y);
-
- return true;
-}
-
-struct QRhiBufferDataPrivate
-{
- Q_DISABLE_COPY_MOVE(QRhiBufferDataPrivate)
- QRhiBufferDataPrivate() { }
- ~QRhiBufferDataPrivate() { delete[] largeData; }
- int ref = 1;
- int size = 0;
- int largeAlloc = 0;
- char *largeData = nullptr;
- static constexpr int SMALL_DATA_SIZE = 1024;
- char data[SMALL_DATA_SIZE];
-};
-
-// no detach-with-contents, no atomic refcount, no shrink
-class QRhiBufferData
-{
-public:
- QRhiBufferData() = default;
- ~QRhiBufferData()
- {
- if (d && !--d->ref)
- delete d;
- }
- QRhiBufferData(const QRhiBufferData &other)
- : d(other.d)
- {
- if (d)
- d->ref += 1;
- }
- QRhiBufferData &operator=(const QRhiBufferData &other)
- {
- if (d == other.d)
- return *this;
- if (other.d)
- other.d->ref += 1;
- if (d && !--d->ref)
- delete d;
- d = other.d;
- return *this;
- }
- const char *constData() const
- {
- return d->size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE ? d->data : d->largeData;
- }
- int size() const
- {
- return d->size;
- }
- void assign(const char *s, int size)
- {
- if (!d) {
- d = new QRhiBufferDataPrivate;
- } else if (d->ref != 1) {
- d->ref -= 1;
- d = new QRhiBufferDataPrivate;
- }
- d->size = size;
- if (size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE) {
- memcpy(d->data, s, size);
- } else {
- if (d->largeAlloc < size) {
- delete[] d->largeData;
- d->largeAlloc = size;
- d->largeData = new char[size];
- }
- memcpy(d->largeData, s, size);
- }
- }
-private:
- QRhiBufferDataPrivate *d = nullptr;
-};
-
-Q_DECLARE_TYPEINFO(QRhiBufferData, Q_RELOCATABLE_TYPE);
-
-class QRhiResourceUpdateBatchPrivate
-{
-public:
- struct BufferOp {
- enum Type {
- DynamicUpdate,
- StaticUpload,
- Read
- };
- Type type;
- QRhiBuffer *buf;
- int offset;
- QRhiBufferData data;
- int readSize;
- QRhiBufferReadbackResult *result;
-
- static BufferOp dynamicUpdate(QRhiBuffer *buf, int offset, int size, const void *data)
- {
- BufferOp op = {};
- op.type = DynamicUpdate;
- op.buf = buf;
- op.offset = offset;
- const int effectiveSize = size ? size : buf->size();
- op.data.assign(reinterpret_cast<const char *>(data), effectiveSize);
- return op;
- }
-
- static void changeToDynamicUpdate(BufferOp *op, QRhiBuffer *buf, int offset, int size, const void *data)
- {
- op->type = DynamicUpdate;
- op->buf = buf;
- op->offset = offset;
- const int effectiveSize = size ? size : buf->size();
- op->data.assign(reinterpret_cast<const char *>(data), effectiveSize);
- }
-
- static BufferOp staticUpload(QRhiBuffer *buf, int offset, int size, const void *data)
- {
- BufferOp op = {};
- op.type = StaticUpload;
- op.buf = buf;
- op.offset = offset;
- const int effectiveSize = size ? size : buf->size();
- op.data.assign(reinterpret_cast<const char *>(data), effectiveSize);
- return op;
- }
-
- static void changeToStaticUpload(BufferOp *op, QRhiBuffer *buf, int offset, int size, const void *data)
- {
- op->type = StaticUpload;
- op->buf = buf;
- op->offset = offset;
- const int effectiveSize = size ? size : buf->size();
- op->data.assign(reinterpret_cast<const char *>(data), effectiveSize);
- }
-
- static BufferOp read(QRhiBuffer *buf, int offset, int size, QRhiBufferReadbackResult *result)
- {
- BufferOp op = {};
- op.type = Read;
- op.buf = buf;
- op.offset = offset;
- op.readSize = size;
- op.result = result;
- return op;
- }
- };
-
- struct TextureOp {
- enum Type {
- Upload,
- Copy,
- Read,
- GenMips
- };
- Type type;
- QRhiTexture *dst;
- // 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.
- using MipLevelUploadList = std::array<QVector<QRhiTextureSubresourceUploadDescription>, QRhi::MAX_MIP_LEVELS>;
- QVarLengthArray<MipLevelUploadList, 6> subresDesc;
- QRhiTexture *src;
- QRhiTextureCopyDescription desc;
- QRhiReadbackDescription rb;
- QRhiReadbackResult *result;
-
- static TextureOp upload(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
- {
- TextureOp op = {};
- op.type = Upload;
- op.dst = tex;
- int maxLayer = -1;
- for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it) {
- if (it->layer() > maxLayer)
- maxLayer = it->layer();
- }
- op.subresDesc.resize(maxLayer + 1);
- for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it)
- op.subresDesc[it->layer()][it->level()].append(it->description());
- return op;
- }
-
- static TextureOp copy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc)
- {
- TextureOp op = {};
- op.type = Copy;
- op.dst = dst;
- op.src = src;
- op.desc = desc;
- return op;
- }
-
- static TextureOp read(const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
- {
- TextureOp op = {};
- op.type = Read;
- op.rb = rb;
- op.result = result;
- return op;
- }
-
- static TextureOp genMips(QRhiTexture *tex)
- {
- TextureOp op = {};
- op.type = GenMips;
- op.dst = tex;
- return op;
- }
- };
-
- int activeBufferOpCount = 0; // this is the real number of used elements in bufferOps, not bufferOps.count()
- static const int BUFFER_OPS_STATIC_ALLOC = 1024;
- QVarLengthArray<BufferOp, BUFFER_OPS_STATIC_ALLOC> bufferOps;
-
- int activeTextureOpCount = 0; // this is the real number of used elements in textureOps, not textureOps.count()
- static const int TEXTURE_OPS_STATIC_ALLOC = 256;
- QVarLengthArray<TextureOp, TEXTURE_OPS_STATIC_ALLOC> textureOps;
-
- QRhiResourceUpdateBatch *q = nullptr;
- QRhiImplementation *rhi = nullptr;
- int poolIndex = -1;
-
- void free();
- void merge(QRhiResourceUpdateBatchPrivate *other);
- bool hasOptimalCapacity() const;
- void trimOpLists();
-
- static QRhiResourceUpdateBatchPrivate *get(QRhiResourceUpdateBatch *b) { return b->d; }
-};
-
-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;
- }
-
- bool finish() {
- if (!curBatch.resources.isEmpty())
- batches.append(curBatch);
- return !batches.isEmpty();
- }
-
- 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();
-};
-
-class QRhiPassResourceTracker
-{
-public:
- bool isEmpty() const;
- void reset();
-
- struct UsageState {
- int layout;
- int access;
- int stage;
- };
-
- enum BufferStage {
- BufVertexInputStage,
- BufVertexStage,
- BufTCStage,
- BufTEStage,
- BufFragmentStage,
- BufComputeStage,
- BufGeometryStage
- };
-
- enum BufferAccess {
- BufVertexInput,
- BufIndexRead,
- BufUniformRead,
- BufStorageLoad,
- BufStorageStore,
- BufStorageLoadStore
- };
-
- void registerBuffer(QRhiBuffer *buf, int slot, BufferAccess *access, BufferStage *stage,
- const UsageState &state);
-
- enum TextureStage {
- TexVertexStage,
- TexTCStage,
- TexTEStage,
- TexFragmentStage,
- TexColorOutputStage,
- TexDepthOutputStage,
- TexComputeStage,
- TexGeometryStage
- };
-
- enum TextureAccess {
- TexSample,
- TexColorOutput,
- TexDepthOutput,
- TexStorageLoad,
- TexStorageStore,
- TexStorageLoadStore
- };
-
- void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage,
- const UsageState &state);
-
- struct Buffer {
- int slot;
- BufferAccess access;
- BufferStage stage;
- UsageState stateAtPassBegin;
- };
-
- using BufferIterator = QHash<QRhiBuffer *, Buffer>::const_iterator;
- BufferIterator cbeginBuffers() const { return m_buffers.cbegin(); }
- BufferIterator cendBuffers() const { return m_buffers.cend(); }
-
- struct Texture {
- TextureAccess access;
- TextureStage stage;
- UsageState stateAtPassBegin;
- };
-
- using TextureIterator = QHash<QRhiTexture *, Texture>::const_iterator;
- TextureIterator cbeginTextures() const { return m_textures.cbegin(); }
- TextureIterator cendTextures() const { return m_textures.cend(); }
-
- static BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages);
- static TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages);
-
-private:
- QHash<QRhiBuffer *, Buffer> m_buffers;
- QHash<QRhiTexture *, Texture> m_textures;
-};
-
-Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Buffer, Q_RELOCATABLE_TYPE);
-Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Texture, Q_RELOCATABLE_TYPE);
-
-template<typename T, int GROW = 1024>
-class QRhiBackendCommandList
-{
-public:
- QRhiBackendCommandList() = default;
- ~QRhiBackendCommandList() { delete[] v; }
- inline void reset() { p = 0; }
- inline bool isEmpty() const { return p == 0; }
- inline T &get() {
- if (p == a) {
- a += GROW;
- T *nv = new T[a];
- if (v) {
- memcpy(nv, v, p * sizeof(T));
- delete[] v;
- }
- v = nv;
- }
- return v[p++];
- }
- inline void unget() { --p; }
- inline T *cbegin() const { return v; }
- inline T *cend() const { return v + p; }
- inline T *begin() { return v; }
- inline T *end() { return v + p; }
-private:
- Q_DISABLE_COPY(QRhiBackendCommandList)
- T *v = nullptr;
- int a = 0;
- int p = 0;
-};
-
-struct QRhiRenderTargetAttachmentTracker
-{
- struct ResId { quint64 id; uint generation; };
- using ResIdList = QVarLengthArray<ResId, 8 * 2 + 1>; // color, resolve, ds
-
- template<typename TexType, typename RenderBufferType>
- static void updateResIdList(const QRhiTextureRenderTargetDescription &desc, ResIdList *dst);
-
- template<typename TexType, typename RenderBufferType>
- static bool isUpToDate(const QRhiTextureRenderTargetDescription &desc, const ResIdList &currentResIdList);
-};
-
-inline bool operator==(const QRhiRenderTargetAttachmentTracker::ResId &a, const QRhiRenderTargetAttachmentTracker::ResId &b)
-{
- return a.id == b.id && a.generation == b.generation;
-}
-
-inline bool operator!=(const QRhiRenderTargetAttachmentTracker::ResId &a, const QRhiRenderTargetAttachmentTracker::ResId &b)
-{
- return !(a == b);
-}
-
-template<typename TexType, typename RenderBufferType>
-void QRhiRenderTargetAttachmentTracker::updateResIdList(const QRhiTextureRenderTargetDescription &desc, ResIdList *dst)
-{
- const quintptr colorAttCount = desc.cendColorAttachments() - desc.cbeginColorAttachments();
- const bool hasDepthStencil = desc.depthStencilBuffer() || desc.depthTexture();
- dst->resize(colorAttCount * 2 + (hasDepthStencil ? 1 : 0));
- int n = 0;
- for (auto it = desc.cbeginColorAttachments(), itEnd = desc.cendColorAttachments(); it != itEnd; ++it, ++n) {
- const QRhiColorAttachment &colorAtt(*it);
- if (colorAtt.texture()) {
- TexType *texD = QRHI_RES(TexType, colorAtt.texture());
- (*dst)[n] = { texD->globalResourceId(), texD->generation };
- } else if (colorAtt.renderBuffer()) {
- RenderBufferType *rbD = QRHI_RES(RenderBufferType, colorAtt.renderBuffer());
- (*dst)[n] = { rbD->globalResourceId(), rbD->generation };
- } else {
- (*dst)[n] = { 0, 0 };
- }
- ++n;
- if (colorAtt.resolveTexture()) {
- TexType *texD = QRHI_RES(TexType, colorAtt.resolveTexture());
- (*dst)[n] = { texD->globalResourceId(), texD->generation };
- } else {
- (*dst)[n] = { 0, 0 };
- }
- }
- if (hasDepthStencil) {
- if (desc.depthTexture()) {
- TexType *depthTexD = QRHI_RES(TexType, desc.depthTexture());
- (*dst)[n] = { depthTexD->globalResourceId(), depthTexD->generation };
- } else if (desc.depthStencilBuffer()) {
- RenderBufferType *depthRbD = QRHI_RES(RenderBufferType, desc.depthStencilBuffer());
- (*dst)[n] = { depthRbD->globalResourceId(), depthRbD->generation };
- } else {
- (*dst)[n] = { 0, 0 };
- }
- }
-}
-
-template<typename TexType, typename RenderBufferType>
-bool QRhiRenderTargetAttachmentTracker::isUpToDate(const QRhiTextureRenderTargetDescription &desc, const ResIdList &currentResIdList)
-{
- // Just as setShaderResources() recognizes if an srb's referenced
- // resources have been rebuilt (got a create() since the srb's
- // create()), we should do the same for the textures and renderbuffers
- // referenced from the rendertarget. It is not uncommon that a texture
- // or ds buffer gets resized due to following a window size in some
- // form, which involves a create() on them. It is then nice if the
- // render target auto-rebuilds in beginPass().
-
- ResIdList resIdList;
- updateResIdList<TexType, RenderBufferType>(desc, &resIdList);
- return resIdList == currentResIdList;
-}
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/gui/rhi/qrhi_platform.h b/src/gui/rhi/qrhi_platform.h
new file mode 100644
index 0000000000..e7be522c52
--- /dev/null
+++ b/src/gui/rhi/qrhi_platform.h
@@ -0,0 +1,175 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRHIPLATFORM_H
+#define QRHIPLATFORM_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is part of the RHI API, with limited compatibility guarantees.
+// Usage of this API may make your code source and binary incompatible with
+// future versions of Qt.
+//
+
+#include <rhi/qrhi.h>
+
+#if QT_CONFIG(opengl)
+#include <QtGui/qsurfaceformat.h>
+#endif
+
+#if QT_CONFIG(vulkan)
+#include <QtGui/qvulkaninstance.h>
+#endif
+
+#if QT_CONFIG(metal) || defined(Q_QDOC)
+Q_FORWARD_DECLARE_OBJC_CLASS(MTLDevice);
+Q_FORWARD_DECLARE_OBJC_CLASS(MTLCommandQueue);
+Q_FORWARD_DECLARE_OBJC_CLASS(MTLCommandBuffer);
+Q_FORWARD_DECLARE_OBJC_CLASS(MTLRenderCommandEncoder);
+#endif
+
+QT_BEGIN_NAMESPACE
+
+struct Q_GUI_EXPORT QRhiNullInitParams : public QRhiInitParams
+{
+};
+
+struct Q_GUI_EXPORT QRhiNullNativeHandles : public QRhiNativeHandles
+{
+};
+
+#if QT_CONFIG(opengl) || defined(Q_QDOC)
+
+class QOpenGLContext;
+class QOffscreenSurface;
+class QSurface;
+class QWindow;
+
+struct Q_GUI_EXPORT QRhiGles2InitParams : public QRhiInitParams
+{
+ QRhiGles2InitParams();
+
+ QSurfaceFormat format;
+ QSurface *fallbackSurface = nullptr;
+ QWindow *window = nullptr;
+ QOpenGLContext *shareContext = nullptr;
+
+ static QOffscreenSurface *newFallbackSurface(const QSurfaceFormat &format = QSurfaceFormat::defaultFormat());
+};
+
+struct Q_GUI_EXPORT QRhiGles2NativeHandles : public QRhiNativeHandles
+{
+ QOpenGLContext *context = nullptr;
+};
+
+#endif // opengl/qdoc
+
+#if (QT_CONFIG(vulkan) && __has_include(<vulkan/vulkan.h>)) || defined(Q_QDOC)
+
+struct Q_GUI_EXPORT QRhiVulkanInitParams : public QRhiInitParams
+{
+ QVulkanInstance *inst = nullptr;
+ QWindow *window = nullptr;
+ QByteArrayList deviceExtensions;
+
+ static QByteArrayList preferredInstanceExtensions();
+ static QByteArrayList preferredExtensionsForImportedDevice();
+};
+
+struct Q_GUI_EXPORT QRhiVulkanNativeHandles : public QRhiNativeHandles
+{
+ // to import a physical device (always required)
+ VkPhysicalDevice physDev = VK_NULL_HANDLE;
+ // to import a device and queue
+ VkDevice dev = VK_NULL_HANDLE;
+ quint32 gfxQueueFamilyIdx = 0;
+ quint32 gfxQueueIdx = 0;
+ // and optionally, the mem allocator
+ void *vmemAllocator = nullptr;
+
+ // only for querying (rhi->nativeHandles())
+ VkQueue gfxQueue = VK_NULL_HANDLE;
+ QVulkanInstance *inst = nullptr;
+};
+
+struct Q_GUI_EXPORT QRhiVulkanCommandBufferNativeHandles : public QRhiNativeHandles
+{
+ VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
+};
+
+struct Q_GUI_EXPORT QRhiVulkanRenderPassNativeHandles : public QRhiNativeHandles
+{
+ VkRenderPass renderPass = VK_NULL_HANDLE;
+};
+
+#endif // vulkan/qdoc
+
+#if defined(Q_OS_WIN) || defined(Q_QDOC)
+
+// no d3d includes here, to prevent precompiled header mess due to COM, hence the void pointers
+
+struct Q_GUI_EXPORT QRhiD3D11InitParams : public QRhiInitParams
+{
+ bool enableDebugLayer = false;
+};
+
+struct Q_GUI_EXPORT QRhiD3D11NativeHandles : public QRhiNativeHandles
+{
+ // to import a device and a context
+ void *dev = nullptr;
+ void *context = nullptr;
+ // alternatively, to specify the device feature level and/or the adapter to use
+ int featureLevel = 0;
+ quint32 adapterLuidLow = 0;
+ qint32 adapterLuidHigh = 0;
+};
+
+struct Q_GUI_EXPORT QRhiD3D12InitParams : public QRhiInitParams
+{
+ bool enableDebugLayer = false;
+};
+
+struct Q_GUI_EXPORT QRhiD3D12NativeHandles : public QRhiNativeHandles
+{
+ // to import a device
+ void *dev = nullptr;
+ int minimumFeatureLevel = 0;
+ // to just specify the adapter to use, set these and leave dev set to null
+ quint32 adapterLuidLow = 0;
+ qint32 adapterLuidHigh = 0;
+ // in addition, can specify the command queue to use
+ void *commandQueue = nullptr;
+};
+
+struct Q_GUI_EXPORT QRhiD3D12CommandBufferNativeHandles : public QRhiNativeHandles
+{
+ void *commandList = nullptr; // ID3D12GraphicsCommandList1
+};
+
+#endif // WIN/QDOC
+
+#if QT_CONFIG(metal) || defined(Q_QDOC)
+
+struct Q_GUI_EXPORT QRhiMetalInitParams : public QRhiInitParams
+{
+};
+
+struct Q_GUI_EXPORT QRhiMetalNativeHandles : public QRhiNativeHandles
+{
+ MTLDevice *dev = nullptr;
+ MTLCommandQueue *cmdQueue = nullptr;
+};
+
+struct Q_GUI_EXPORT QRhiMetalCommandBufferNativeHandles : public QRhiNativeHandles
+{
+ MTLCommandBuffer *commandBuffer = nullptr;
+ MTLRenderCommandEncoder *encoder = nullptr;
+};
+
+#endif // MACOS/IOS/QDOC
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
index 0ac92e2266..b09baf57b2 100644
--- a/src/gui/rhi/qrhid3d11.cpp
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -1,17 +1,14 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qrhid3d11_p_p.h"
-#include "qshader_p.h"
+#include "qrhid3d11_p.h"
+#include "qshader.h"
#include "vs_test_p.h"
-#include "cs_tdr_p.h"
#include <QWindow>
#include <qmath.h>
-#include <private/qsystemlibrary_p.h>
#include <QtCore/qcryptographichash.h>
-
-#include <d3dcompiler.h>
-#include <comdef.h>
+#include <QtCore/private/qsystemerror_p.h>
+#include "qrhid3dhelpers_p.h"
QT_BEGIN_NAMESPACE
@@ -29,10 +26,13 @@ using namespace Qt::StringLiterals;
/*!
\class QRhiD3D11InitParams
- \internal
\inmodule QtGui
+ \since 6.6
\brief Direct3D 11 specific initialization parameters.
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
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
@@ -45,9 +45,7 @@ using namespace Qt::StringLiterals;
\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.
+ instances that have their surface type set to QSurface::Direct3DSurface.
\section2 Working with existing Direct3D 11 devices
@@ -73,16 +71,71 @@ using namespace Qt::StringLiterals;
*/
/*!
+ \variable QRhiD3D11InitParams::enableDebugLayer
+
+ When set to true, a debug device is created, assuming the debug layer is
+ available. The default value is false.
+*/
+
+/*!
\class QRhiD3D11NativeHandles
- \internal
\inmodule QtGui
+ \since 6.6
\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 *}.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
+/*!
+ \variable QRhiD3D11NativeHandles::dev
+
+ Points to a
+ \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nn-d3d11-id3d11device}{ID3D11Device}
+ or left set to \nullptr if no existing device is to be imported.
+
+ \note When importing a device, both the device and the device context must be set to valid objects.
+*/
+
+/*!
+ \variable QRhiD3D11NativeHandles::context
+
+ Points to a \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nn-d3d11-id3d11devicecontext}{ID3D11DeviceContext}
+ or left set to \nullptr if no existing device context is to be imported.
+
+ \note When importing a device, both the device and the device context must be set to valid objects.
+*/
+
+/*!
+ \variable QRhiD3D11NativeHandles::featureLevel
+
+ Specifies the feature level passed to
+ \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-d3d11createdevice}{D3D11CreateDevice()}.
+ Relevant only when QRhi creates the device, ignored when importing a device
+ and device context. When not set, the default rules outlined in the D3D
+ documentation apply.
+*/
+
+/*!
+ \variable QRhiD3D11NativeHandles::adapterLuidLow
+
+ The low part of the local identifier (LUID) of the DXGI adapter to use.
+ Relevant only when QRhi creates the device, ignored when importing a device
+ and device context.
+*/
+
+/*!
+ \variable QRhiD3D11NativeHandles::adapterLuidHigh
+
+ The high part of the local identifier (LUID) of the DXGI adapter to use.
+ Relevant only when QRhi creates the device, ignored when importing a device
+ and device context.
+*/
+
// help mingw with its ancient sdk headers
#ifndef DXGI_ADAPTER_FLAG_SOFTWARE
#define DXGI_ADAPTER_FLAG_SOFTWARE 2
@@ -97,14 +150,10 @@ using namespace Qt::StringLiterals;
#endif
QRhiD3D11::QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importParams)
- : ofr(this),
- deviceCurse(this)
+ : ofr(this)
{
debugLayer = params->enableDebugLayer;
- deviceCurse.framesToActivate = params->framesUntilKillingDeviceViaTdr;
- deviceCurse.permanent = params->repeatDeviceKill;
-
if (importParams) {
if (importParams->dev && importParams->context) {
dev = reinterpret_cast<ID3D11Device *>(importParams->dev);
@@ -123,15 +172,6 @@ QRhiD3D11::QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *import
}
}
-static QString comErrorMessage(HRESULT hr)
-{
- const _com_error comError(hr);
- QString result = "Error 0x"_L1 + QString::number(ulong(hr), 16);
- if (const wchar_t *msg = comError.ErrorMessage())
- result += ": "_L1 + QString::fromWCharArray(msg);
- return result;
-}
-
template <class Int>
inline Int aligned(Int v, Int byteAlign)
{
@@ -143,18 +183,8 @@ static IDXGIFactory1 *createDXGIFactory2()
IDXGIFactory1 *result = nullptr;
const HRESULT hr = CreateDXGIFactory2(0, __uuidof(IDXGIFactory2), reinterpret_cast<void **>(&result));
if (FAILED(hr)) {
- qWarning("CreateDXGIFactory2() failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr)));
- result = nullptr;
- }
- return result;
-}
-
-static IDXGIFactory1 *createDXGIFactory1()
-{
- IDXGIFactory1 *result = nullptr;
- const HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast<void **>(&result));
- if (FAILED(hr)) {
- qWarning("CreateDXGIFactory1() failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("CreateDXGIFactory2() failed to create DXGI factory: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
result = nullptr;
}
return result;
@@ -169,40 +199,32 @@ bool QRhiD3D11::create(QRhi::Flags flags)
devFlags |= D3D11_CREATE_DEVICE_DEBUG;
dxgiFactory = createDXGIFactory2();
- if (dxgiFactory != nullptr) {
- supportsFlipSwapchain = !qEnvironmentVariableIntValue("QT_D3D_NO_FLIP");
- } else {
- dxgiFactory = createDXGIFactory1();
- supportsFlipSwapchain = false;
- }
-
- if (dxgiFactory == nullptr)
+ if (!dxgiFactory)
return false;
+ // For a FLIP_* swapchain Present(0, 0) is not necessarily
+ // sufficient to get non-blocking behavior, try using ALLOW_TEARING
+ // when available.
supportsAllowTearing = false;
- forceFlipDiscard = false;
- if (supportsFlipSwapchain) {
- // For a FLIP_* swapchain Present(0, 0) is not necessarily
- // sufficient to get non-blocking behavior, try using ALLOW_TEARING
- // when available.
- IDXGIFactory5 *factory5 = nullptr;
- if (SUCCEEDED(dxgiFactory->QueryInterface(__uuidof(IDXGIFactory5), reinterpret_cast<void **>(&factory5)))) {
- BOOL allowTearing = false;
- if (SUCCEEDED(factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing))))
- supportsAllowTearing = allowTearing;
- factory5->Release();
- }
- // if we default to FLIP_SEQUENTIAL, have a way to request FLIP_DISCARD
- forceFlipDiscard = qEnvironmentVariableIntValue("QT_D3D_FLIP_DISCARD");
+ IDXGIFactory5 *factory5 = nullptr;
+ if (SUCCEEDED(dxgiFactory->QueryInterface(__uuidof(IDXGIFactory5), reinterpret_cast<void **>(&factory5)))) {
+ BOOL allowTearing = false;
+ if (SUCCEEDED(factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing))))
+ supportsAllowTearing = allowTearing;
+ factory5->Release();
}
- qCDebug(QRHI_LOG_INFO, "FLIP_* swapchain supported = %s, ALLOW_TEARING supported = %s",
- supportsFlipSwapchain ? "true" : "false",
- supportsAllowTearing ? "true" : "false");
+ if (qEnvironmentVariableIntValue("QT_D3D_FLIP_DISCARD"))
+ qWarning("The default swap effect is FLIP_DISCARD, QT_D3D_FLIP_DISCARD is now ignored");
+
+ // Support for flip model swapchains is required now (since we are
+ // targeting Windows 10+), but the option for using the old model is still
+ // there. (some features are not supported then, however)
+ useLegacySwapchainModel = qEnvironmentVariableIntValue("QT_D3D_NO_FLIP");
- qCDebug(QRHI_LOG_INFO, "Default swap effect: %s",
- supportsFlipSwapchain ? (forceFlipDiscard ? "FLIP_DISCARD" : "FLIP_SEQUENTIAL")
- : "DISCARD");
+ qCDebug(QRHI_LOG_INFO, "FLIP_* swapchain supported = true, ALLOW_TEARING supported = %s, use legacy (non-FLIP) model = %s",
+ supportsAllowTearing ? "true" : "false",
+ useLegacySwapchainModel ? "true" : "false");
if (!importedDeviceAndContext) {
IDXGIAdapter1 *adapter;
@@ -251,9 +273,7 @@ bool QRhiD3D11::create(QRhi::Flags flags)
if (!activeAdapter && (requestedAdapterIndex < 0 || requestedAdapterIndex == adapterIndex)) {
activeAdapter = adapter;
adapterLuid = desc.AdapterLuid;
- driverInfoStruct.deviceName = name.toUtf8();
- driverInfoStruct.deviceId = desc.DeviceId;
- driverInfoStruct.vendorId = desc.VendorId;
+ QRhiD3D::fillDriverInfo(&driverInfoStruct, desc);
qCDebug(QRHI_LOG_INFO, " using this adapter");
} else {
adapter->Release();
@@ -291,7 +311,15 @@ bool QRhiD3D11::create(QRhi::Flags flags)
&dev, &featureLevel, &ctx);
}
if (FAILED(hr)) {
- qWarning("Failed to create D3D11 device and context: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create D3D11 device and context: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+
+ const bool supports11_1 = SUCCEEDED(ctx->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast<void **>(&context)));
+ ctx->Release();
+ if (!supports11_1) {
+ qWarning("ID3D11DeviceContext1 not supported");
return false;
}
@@ -301,15 +329,33 @@ bool QRhiD3D11::create(QRhi::Flags flags)
if (SUCCEEDED(dev->CreateVertexShader(g_testVertexShader, sizeof(g_testVertexShader), nullptr, &testShader))) {
testShader->Release();
} else {
- qWarning("D3D11 smoke test failed (failed to create vertex shader)");
- ctx->Release();
+ static const char *msg = "D3D11 smoke test: Failed to create vertex shader";
+ if (flags.testFlag(QRhi::SuppressSmokeTestWarnings))
+ qCDebug(QRHI_LOG_INFO, "%s", msg);
+ else
+ qWarning("%s", msg);
return false;
}
- const bool supports11_1 = SUCCEEDED(ctx->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast<void **>(&context)));
- ctx->Release();
- if (!supports11_1) {
- qWarning("ID3D11DeviceContext1 not supported");
+ D3D11_FEATURE_DATA_D3D11_OPTIONS features = {};
+ if (SUCCEEDED(dev->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &features, sizeof(features)))) {
+ // The D3D _runtime_ may be 11.1, but the underlying _driver_ may
+ // still not support this D3D_FEATURE_LEVEL_11_1 feature. (e.g.
+ // because it only does 11_0)
+ if (!features.ConstantBufferOffsetting) {
+ static const char *msg = "D3D11 smoke test: Constant buffer offsetting is not supported by the driver";
+ if (flags.testFlag(QRhi::SuppressSmokeTestWarnings))
+ qCDebug(QRHI_LOG_INFO, "%s", msg);
+ else
+ qWarning("%s", msg);
+ return false;
+ }
+ } else {
+ static const char *msg = "D3D11 smoke test: Failed to query D3D11_FEATURE_D3D11_OPTIONS";
+ if (flags.testFlag(QRhi::SuppressSmokeTestWarnings))
+ qCDebug(QRHI_LOG_INFO, "%s", msg);
+ else
+ qWarning("%s", msg);
return false;
}
} else {
@@ -319,12 +365,14 @@ bool QRhiD3D11::create(QRhi::Flags flags)
if (SUCCEEDED(dev->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void **>(&dxgiDev)))) {
IDXGIAdapter *adapter = nullptr;
if (SUCCEEDED(dxgiDev->GetAdapter(&adapter))) {
- DXGI_ADAPTER_DESC desc;
- adapter->GetDesc(&desc);
- adapterLuid = desc.AdapterLuid;
- driverInfoStruct.deviceName = QString::fromUtf16(reinterpret_cast<char16_t *>(desc.Description)).toUtf8();
- driverInfoStruct.deviceId = desc.DeviceId;
- driverInfoStruct.vendorId = desc.VendorId;
+ IDXGIAdapter1 *adapter1 = nullptr;
+ if (SUCCEEDED(adapter->QueryInterface(__uuidof(IDXGIAdapter1), reinterpret_cast<void **>(&adapter1)))) {
+ DXGI_ADAPTER_DESC1 desc;
+ adapter1->GetDesc1(&desc);
+ adapterLuid = desc.AdapterLuid;
+ QRhiD3D::fillDriverInfo(&driverInfoStruct, desc);
+ adapter1->Release();
+ }
adapter->Release();
}
dxgiDev->Release();
@@ -343,9 +391,6 @@ bool QRhiD3D11::create(QRhi::Flags flags)
nativeHandlesStruct.adapterLuidLow = adapterLuid.LowPart;
nativeHandlesStruct.adapterLuidHigh = adapterLuid.HighPart;
- if (deviceCurse.framesToActivate > 0)
- deviceCurse.initResources();
-
return true;
}
@@ -363,7 +408,16 @@ void QRhiD3D11::destroy()
clearShaderCache();
- deviceCurse.releaseResources();
+ if (ofr.tsDisjointQuery) {
+ ofr.tsDisjointQuery->Release();
+ ofr.tsDisjointQuery = nullptr;
+ }
+ for (int i = 0; i < 2; ++i) {
+ if (ofr.tsQueries[i]) {
+ ofr.tsQueries[i]->Release();
+ ofr.tsQueries[i] = nullptr;
+ }
+ }
if (annotations) {
annotations->Release();
@@ -381,6 +435,11 @@ void QRhiD3D11::destroy()
}
}
+ if (dcompDevice) {
+ dcompDevice->Release();
+ dcompDevice = nullptr;
+ }
+
if (activeAdapter) {
activeAdapter->Release();
activeAdapter = nullptr;
@@ -407,19 +466,13 @@ QList<int> QRhiD3D11::supportedSampleCounts() const
return { 1, 2, 4, 8 };
}
-DXGI_SAMPLE_DESC QRhiD3D11::effectiveSampleCount(int sampleCount) const
+DXGI_SAMPLE_DESC QRhiD3D11::effectiveSampleDesc(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;
- }
+ const int s = effectiveSampleCount(sampleCount);
desc.Count = UINT(s);
if (s > 1)
@@ -435,7 +488,7 @@ QRhiSwapChain *QRhiD3D11::createSwapChain()
return new QD3D11SwapChain(this);
}
-QRhiBuffer *QRhiD3D11::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
+QRhiBuffer *QRhiD3D11::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
{
return new QD3D11Buffer(this, type, usage, size);
}
@@ -551,13 +604,29 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
case QRhi::TextureArrays:
return true;
case QRhi::Tessellation:
- return false;
+ return true;
case QRhi::GeometryShader:
- return false;
+ return true;
case QRhi::TextureArrayRange:
return true;
case QRhi::NonFillPolygonMode:
return true;
+ case QRhi::OneDimensionalTextures:
+ return true;
+ case QRhi::OneDimensionalTextureMipmaps:
+ return true;
+ case QRhi::HalfAttributes:
+ return true;
+ case QRhi::RenderToOneDimensionalTexture:
+ return true;
+ case QRhi::ThreeDimensionalTextureMipmaps:
+ return true;
+ case QRhi::MultiView:
+ return false;
+ case QRhi::TextureViewFormat:
+ return false; // because we use fully typed formats for textures and relaxed casting is a D3D12 thing
+ case QRhi::ResolveDepthStencil:
+ return false;
default:
Q_UNREACHABLE();
return false;
@@ -615,9 +684,11 @@ QRhiDriverInfo QRhiD3D11::driverInfo() const
return driverInfoStruct;
}
-QRhiMemAllocStats QRhiD3D11::graphicsMemoryAllocationStatistics()
+QRhiStats QRhiD3D11::statistics()
{
- return {};
+ QRhiStats result;
+ result.totalPipelineCreationTime = totalPipelineCreationTime();
+ return result;
}
bool QRhiD3D11::makeThreadLocalNativeContextCurrent()
@@ -720,7 +791,7 @@ void QRhiD3D11::setPipelineCacheData(const QByteArray &data)
const size_t headerSize = sizeof(QD3D11PipelineCacheDataHeader);
if (data.size() < qsizetype(headerSize)) {
- qWarning("setPipelineCacheData: Invalid blob size (header incomplete)");
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (header incomplete)");
return;
}
const size_t dataOffset = headerSize;
@@ -729,21 +800,21 @@ void QRhiD3D11::setPipelineCacheData(const QByteArray &data)
const quint32 rhiId = pipelineCacheRhiId();
if (header.rhiId != rhiId) {
- qWarning("setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
- rhiId, header.rhiId);
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
+ rhiId, header.rhiId);
return;
}
const quint32 arch = quint32(sizeof(void*));
if (header.arch != arch) {
- qWarning("setPipelineCacheData: Architecture does not match (%u, %u)",
- arch, header.arch);
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
+ arch, header.arch);
return;
}
if (header.count == 0)
return;
if (data.size() < qsizetype(dataOffset + header.dataSize)) {
- qWarning("setPipelineCacheData: Invalid blob size (data incomplete)");
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (data incomplete)");
return;
}
@@ -852,10 +923,13 @@ void QRhiD3D11::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline
}
}
-static const int RBM_SUPPORTED_STAGES = 3;
+static const int RBM_SUPPORTED_STAGES = 6;
static const int RBM_VERTEX = 0;
-static const int RBM_FRAGMENT = 1;
-static const int RBM_COMPUTE = 2;
+static const int RBM_HULL = 1;
+static const int RBM_DOMAIN = 2;
+static const int RBM_GEOMETRY = 3;
+static const int RBM_FRAGMENT = 4;
+static const int RBM_COMPUTE = 5;
void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
int dynamicOffsetCount,
@@ -877,7 +951,7 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
bool srbUpdate = false;
for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
- const QRhiShaderResourceBinding::Data *b = srbD->sortedBindings.at(i).data();
+ const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
QD3D11ShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]);
switch (b->type) {
case QRhiShaderResourceBinding::UniformBuffer:
@@ -964,6 +1038,9 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
memset(resBindMaps, 0, sizeof(resBindMaps));
if (gfxPsD) {
resBindMaps[RBM_VERTEX] = &gfxPsD->vs.nativeResourceBindingMap;
+ resBindMaps[RBM_HULL] = &gfxPsD->hs.nativeResourceBindingMap;
+ resBindMaps[RBM_DOMAIN] = &gfxPsD->ds.nativeResourceBindingMap;
+ resBindMaps[RBM_GEOMETRY] = &gfxPsD->gs.nativeResourceBindingMap;
resBindMaps[RBM_FRAGMENT] = &gfxPsD->fs.nativeResourceBindingMap;
} else {
resBindMaps[RBM_COMPUTE] = &compPsD->cs.nativeResourceBindingMap;
@@ -998,8 +1075,8 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
for (int i = 0; i < dynamicOffsetCount; ++i) {
const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
const uint binding = uint(dynOfs.first);
- Q_ASSERT(aligned(dynOfs.second, quint32(256)) == dynOfs.second);
- const uint offsetInConstants = dynOfs.second / 16;
+ Q_ASSERT(aligned(dynOfs.second, 256u) == dynOfs.second);
+ const quint32 offsetInConstants = dynOfs.second / 16;
*p++ = binding;
*p++ = offsetInConstants;
}
@@ -1090,7 +1167,7 @@ void QRhiD3D11::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
// d3d expects top-left, QRhiViewport is bottom-left
float x, y, w, h;
- if (!qrhi_toTopLeftRenderTargetRect(outputSize, viewport.viewport(), &x, &y, &w, &h))
+ if (!qrhi_toTopLeftRenderTargetRect<UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
return;
QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
@@ -1112,7 +1189,7 @@ void QRhiD3D11::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
// d3d expects top-left, QRhiScissor is bottom-left
int x, y, w, h;
- if (!qrhi_toTopLeftRenderTargetRect(outputSize, scissor.scissor(), &x, &y, &w, &h))
+ if (!qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
return;
QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
@@ -1220,7 +1297,6 @@ const QRhiNativeHandles *QRhiD3D11::nativeHandles(QRhiCommandBuffer *cb)
void QRhiD3D11::beginExternal(QRhiCommandBuffer *cb)
{
QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
- // no timestampSwapChain, in order to avoid timestamp mess
executeCommandBuffer(cbD);
cbD->resetCommands();
}
@@ -1237,6 +1313,25 @@ void QRhiD3D11::endExternal(QRhiCommandBuffer *cb)
}
}
+double QRhiD3D11::lastCompletedGpuTime(QRhiCommandBuffer *cb)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ return cbD->lastGpuTime;
+}
+
+static inline QD3D11RenderTargetData *rtData(QRhiRenderTarget *rt)
+{
+ switch (rt->resourceType()) {
+ case QRhiResource::SwapChainRenderTarget:
+ return &QRHI_RES(QD3D11SwapChainRenderTarget, rt)->d;
+ case QRhiResource::TextureRenderTarget:
+ return &QRHI_RES(QD3D11TextureRenderTarget, rt)->d;
+ default:
+ Q_UNREACHABLE();
+ return nullptr;
+ }
+}
+
QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
{
Q_UNUSED(flags);
@@ -1245,30 +1340,6 @@ QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF
contextState.currentSwapChain = swapChainD;
const int currentFrameSlot = swapChainD->currentFrameSlot;
- 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;
- runGpuFrameTimeCallbacks(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 ?
@@ -1277,6 +1348,22 @@ QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF
finishActiveReadbacks();
+ if (swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex]) {
+ double elapsedSec = 0;
+ if (swapChainD->timestamps.tryQueryTimestamps(swapChainD->currentTimestampPairIndex, context, &elapsedSec))
+ swapChainD->cb.lastGpuTime = elapsedSec;
+ }
+
+ ID3D11Query *tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2];
+ ID3D11Query *tsDisjoint = swapChainD->timestamps.disjointQuery[swapChainD->currentTimestampPairIndex];
+ const bool recordTimestamps = tsStart && tsDisjoint && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
+
+ QD3D11CommandBuffer::Command &cmd(swapChainD->cb.commands.get());
+ cmd.cmd = QD3D11CommandBuffer::Command::BeginFrame;
+ cmd.args.beginFrame.tsQuery = recordTimestamps ? tsStart : nullptr;
+ cmd.args.beginFrame.tsDisjointQuery = recordTimestamps ? tsDisjoint : nullptr;
+ cmd.args.beginFrame.swapchainData = rtData(&swapChainD->rt);
+
return QRhi::FrameOpSuccess;
}
@@ -1286,17 +1373,13 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
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];
+ QD3D11CommandBuffer::Command &cmd(swapChainD->cb.commands.get());
+ cmd.cmd = QD3D11CommandBuffer::Command::EndFrame;
+ cmd.args.endFrame.tsQuery = nullptr; // done later manually, see below
+ cmd.args.endFrame.tsDisjointQuery = nullptr;
// send all commands to the context
- if (recordTimestamps)
- executeCommandBuffer(&swapChainD->cb, swapChainD);
- else
- executeCommandBuffer(&swapChainD->cb);
+ executeCommandBuffer(&swapChainD->cb);
if (swapChainD->sampleDesc.Count > 1) {
context->ResolveSubresource(swapChainD->backBufferTex, 0,
@@ -1304,27 +1387,39 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
swapChainD->colorFormat);
}
- // this is here because we want to include the time spent on the resolve as well
+ // this is here because we want to include the time spent on the ResolveSubresource as well
+ ID3D11Query *tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1];
+ ID3D11Query *tsDisjoint = swapChainD->timestamps.disjointQuery[swapChainD->currentTimestampPairIndex];
+ const bool recordTimestamps = tsEnd && tsDisjoint && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
if (recordTimestamps) {
context->End(tsEnd);
context->End(tsDisjoint);
- swapChainD->timestampActive[currentFrameSlot] = true;
+ swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex] = true;
+ swapChainD->currentTimestampPairIndex = (swapChainD->currentTimestampPairIndex + 1) % QD3D11SwapChainTimestamps::TIMESTAMP_PAIRS;
}
if (!flags.testFlag(QRhi::SkipPresent)) {
UINT presentFlags = 0;
if (swapChainD->swapInterval == 0 && (swapChainD->swapChainFlags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING))
presentFlags |= DXGI_PRESENT_ALLOW_TEARING;
+ if (!swapChainD->swapChain) {
+ qWarning("Failed to present: IDXGISwapChain is unavailable");
+ return QRhi::FrameOpError;
+ }
HRESULT hr = swapChainD->swapChain->Present(swapChainD->swapInterval, presentFlags);
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
qWarning("Device loss detected in Present()");
deviceLost = true;
return QRhi::FrameOpDeviceLost;
} else if (FAILED(hr)) {
- qWarning("Failed to present: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to present: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return QRhi::FrameOpError;
}
+ if (dcompDevice && swapChainD->dcompTarget && swapChainD->dcompVisual)
+ dcompDevice->Commit();
+
// move on to the next buffer
swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QD3D11SwapChain::BUFFER_COUNT;
} else {
@@ -1334,19 +1429,6 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
swapChainD->frameCount += 1;
contextState.currentSwapChain = nullptr;
- if (deviceCurse.framesToActivate > 0) {
- deviceCurse.framesLeft -= 1;
- if (deviceCurse.framesLeft == 0) {
- deviceCurse.framesLeft = deviceCurse.framesToActivate;
- if (!deviceCurse.permanent)
- deviceCurse.framesToActivate = -1;
-
- deviceCurse.activate();
- } else if (deviceCurse.framesLeft % 100 == 0) {
- qDebug("Impending doom: %d frames left", deviceCurse.framesLeft);
- }
- }
-
return QRhi::FrameOpSuccess;
}
@@ -1358,6 +1440,36 @@ QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi:
ofr.cbWrapper.resetState();
*cb = &ofr.cbWrapper;
+ if (rhiFlags.testFlag(QRhi::EnableTimestamps)) {
+ D3D11_QUERY_DESC queryDesc = {};
+ if (!ofr.tsDisjointQuery) {
+ queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;
+ HRESULT hr = dev->CreateQuery(&queryDesc, &ofr.tsDisjointQuery);
+ if (FAILED(hr)) {
+ qWarning("Failed to create timestamp disjoint query: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return QRhi::FrameOpError;
+ }
+ }
+ queryDesc.Query = D3D11_QUERY_TIMESTAMP;
+ for (int i = 0; i < 2; ++i) {
+ if (!ofr.tsQueries[i]) {
+ HRESULT hr = dev->CreateQuery(&queryDesc, &ofr.tsQueries[i]);
+ if (FAILED(hr)) {
+ qWarning("Failed to create timestamp query: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return QRhi::FrameOpError;
+ }
+ }
+ }
+ }
+
+ QD3D11CommandBuffer::Command &cmd(ofr.cbWrapper.commands.get());
+ cmd.cmd = QD3D11CommandBuffer::Command::BeginFrame;
+ cmd.args.beginFrame.tsQuery = ofr.tsQueries[0] ? ofr.tsQueries[0] : nullptr;
+ cmd.args.beginFrame.tsDisjointQuery = ofr.tsDisjointQuery ? ofr.tsDisjointQuery : nullptr;
+ cmd.args.beginFrame.swapchainData = nullptr;
+
return QRhi::FrameOpSuccess;
}
@@ -1366,10 +1478,41 @@ QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame(QRhi::EndFrameFlags flags)
Q_UNUSED(flags);
ofr.active = false;
+ QD3D11CommandBuffer::Command &cmd(ofr.cbWrapper.commands.get());
+ cmd.cmd = QD3D11CommandBuffer::Command::EndFrame;
+ cmd.args.endFrame.tsQuery = ofr.tsQueries[1] ? ofr.tsQueries[1] : nullptr;
+ cmd.args.endFrame.tsDisjointQuery = ofr.tsDisjointQuery ? ofr.tsDisjointQuery : nullptr;
+
executeCommandBuffer(&ofr.cbWrapper);
+ context->Flush();
finishActiveReadbacks();
+ if (ofr.tsQueries[0]) {
+ quint64 timestamps[2];
+ D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dj;
+ HRESULT hr;
+ bool ok = true;
+ do {
+ hr = context->GetData(ofr.tsDisjointQuery, &dj, sizeof(dj), 0);
+ } while (hr == S_FALSE);
+ ok &= hr == S_OK;
+ do {
+ hr = context->GetData(ofr.tsQueries[1], &timestamps[1], sizeof(quint64), 0);
+ } while (hr == S_FALSE);
+ ok &= hr == S_OK;
+ do {
+ hr = context->GetData(ofr.tsQueries[0], &timestamps[0], sizeof(quint64), 0);
+ } while (hr == S_FALSE);
+ ok &= hr == S_OK;
+ if (ok) {
+ if (!dj.Disjoint && dj.Frequency) {
+ const float elapsedMs = (timestamps[1] - timestamps[0]) / float(dj.Frequency) * 1000.0f;
+ ofr.cbWrapper.lastGpuTime = elapsedMs / 1000.0;
+ }
+ }
+ }
+
return QRhi::FrameOpSuccess;
}
@@ -1407,9 +1550,9 @@ static inline DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTex
case QRhiTexture::D16:
return DXGI_FORMAT_R16_TYPELESS;
case QRhiTexture::D24:
- return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
+ return DXGI_FORMAT_R24G8_TYPELESS;
case QRhiTexture::D24S8:
- return DXGI_FORMAT_D24_UNORM_S8_UINT;
+ return DXGI_FORMAT_R24G8_TYPELESS;
case QRhiTexture::D32F:
return DXGI_FORMAT_R32_TYPELESS;
@@ -1510,7 +1653,7 @@ QRhi::FrameOpResult QRhiD3D11::finish()
} else {
Q_ASSERT(contextState.currentSwapChain);
Q_ASSERT(contextState.currentSwapChain->cb.recordingPass == QD3D11CommandBuffer::NoPass);
- executeCommandBuffer(&contextState.currentSwapChain->cb); // no timestampSwapChain, in order to avoid timestamp mess
+ executeCommandBuffer(&contextState.currentSwapChain->cb);
contextState.currentSwapChain->cb.resetCommands();
}
}
@@ -1626,10 +1769,10 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
// 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 = UINT(u.offset);
+ box.left = u.offset;
box.top = box.front = 0;
box.back = box.bottom = 1;
- box.right = UINT(u.offset + u.data.size()); // no -1: right, bottom, back are exclusive, see D3D11_BOX doc
+ 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;
} else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) {
@@ -1637,19 +1780,21 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
if (bufD->m_type == QRhiBuffer::Dynamic) {
u.result->data.resize(u.readSize);
memcpy(u.result->data.data(), bufD->dynBuf + u.offset, size_t(u.readSize));
+ if (u.result->completed)
+ u.result->completed();
} else {
BufferReadback readback;
readback.result = u.result;
readback.byteSize = u.readSize;
- D3D11_BUFFER_DESC desc;
- memset(&desc, 0, sizeof(desc));
+ D3D11_BUFFER_DESC desc = {};
desc.ByteWidth = readback.byteSize;
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
HRESULT hr = dev->CreateBuffer(&desc, nullptr, &readback.stagingBuf);
if (FAILED(hr)) {
- qWarning("Failed to create buffer: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create buffer: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
continue;
}
@@ -1664,16 +1809,14 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
cmd.args.copySubRes.srcSubRes = 0;
cmd.args.copySubRes.hasSrcBox = true;
D3D11_BOX box;
- box.left = UINT(u.offset);
+ box.left = u.offset;
box.top = box.front = 0;
box.back = box.bottom = 1;
- box.right = UINT(u.offset + u.readSize);
+ box.right = u.offset + u.readSize;
cmd.args.copySubRes.srcBox = box;
activeBufferReadbacks.append(readback);
}
- if (u.result->completed)
- u.result->completed();
}
}
for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
@@ -1682,7 +1825,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.dst);
for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
- for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
}
}
@@ -1767,8 +1910,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
quint32 bpl = 0;
textureFormatInfo(format, pixelSize, &bpl, &byteSize, nullptr);
- D3D11_TEXTURE2D_DESC desc;
- memset(&desc, 0, sizeof(desc));
+ D3D11_TEXTURE2D_DESC desc = {};
desc.Width = UINT(pixelSize.width());
desc.Height = UINT(pixelSize.height());
desc.MipLevels = 1;
@@ -1780,7 +1922,8 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
ID3D11Texture2D *stagingTex;
HRESULT hr = dev->CreateTexture2D(&desc, nullptr, &stagingTex);
if (FAILED(hr)) {
- qWarning("Failed to create readback staging texture: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create readback staging texture: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return;
}
@@ -1794,8 +1937,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
cmd.args.copySubRes.src = src;
cmd.args.copySubRes.srcSubRes = subres;
if (is3D) {
- D3D11_BOX srcBox;
- memset(&srcBox, 0, sizeof(srcBox));
+ D3D11_BOX srcBox = {};
srcBox.front = UINT(u.rb.layer());
srcBox.right = desc.Width; // exclusive
srcBox.bottom = desc.Height;
@@ -1848,7 +1990,8 @@ void QRhiD3D11::finishActiveReadbacks()
}
context->Unmap(readback.stagingTex, 0);
} else {
- qWarning("Failed to map readback staging texture: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to map readback staging texture: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
}
readback.stagingTex->Release();
@@ -1869,7 +2012,8 @@ void QRhiD3D11::finishActiveReadbacks()
memcpy(readback.result->data.data(), mp.pData, readback.byteSize);
context->Unmap(readback.stagingBuf, 0);
} else {
- qWarning("Failed to map readback staging texture: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to map readback staging texture: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
}
readback.stagingBuf->Release();
@@ -1884,19 +2028,6 @@ void QRhiD3D11::finishActiveReadbacks()
f();
}
-static inline QD3D11RenderTargetData *rtData(QRhiRenderTarget *rt)
-{
- switch (rt->resourceType()) {
- case QRhiResource::SwapChainRenderTarget:
- return &QRHI_RES(QD3D11SwapChainRenderTarget, 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);
@@ -2015,6 +2146,8 @@ void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource
cmd.args.resolveSubRes.srcSubRes = D3D11CalcSubresource(0, UINT(colorAtt.layer()), 1);
cmd.args.resolveSubRes.format = dstTexD->dxgiFormat;
}
+ if (rtTex->m_desc.depthResolveTexture())
+ qWarning("Resolving multisample depth-stencil buffers is not supported with D3D");
}
cbD->recordingPass = QD3D11CommandBuffer::NoPass;
@@ -2089,7 +2222,7 @@ static inline QPair<int, int> mapBinding(int binding,
{
const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
if (!map || map->isEmpty())
- return { binding, binding }; // old QShader versions do not have this map, assume 1:1 mapping then
+ return { binding, binding }; // assume 1:1 mapping
auto it = map->constFind(binding);
if (it != map->cend())
@@ -2104,31 +2237,21 @@ static inline QPair<int, int> mapBinding(int binding,
void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[])
{
- srbD->vsubufs.clear();
- srbD->vsubuforigbindings.clear();
- srbD->vsubufoffsets.clear();
- srbD->vsubufsizes.clear();
-
- srbD->fsubufs.clear();
- srbD->fsubuforigbindings.clear();
- srbD->fsubufoffsets.clear();
- srbD->fsubufsizes.clear();
-
- srbD->csubufs.clear();
- srbD->csubuforigbindings.clear();
- srbD->csubufoffsets.clear();
- srbD->csubufsizes.clear();
+ srbD->vsUniformBufferBatches.clear();
+ srbD->hsUniformBufferBatches.clear();
+ srbD->dsUniformBufferBatches.clear();
+ srbD->gsUniformBufferBatches.clear();
+ srbD->fsUniformBufferBatches.clear();
+ srbD->csUniformBufferBatches.clear();
- srbD->vssamplers.clear();
- srbD->vsshaderresources.clear();
+ srbD->vsSamplerBatches.clear();
+ srbD->hsSamplerBatches.clear();
+ srbD->dsSamplerBatches.clear();
+ srbD->gsSamplerBatches.clear();
+ srbD->fsSamplerBatches.clear();
+ srbD->csSamplerBatches.clear();
- srbD->fssamplers.clear();
- srbD->fsshaderresources.clear();
-
- srbD->cssamplers.clear();
- srbD->csshaderresources.clear();
-
- srbD->csUAVs.clear();
+ srbD->csUavBatches.clear();
struct Stage {
struct Buffer {
@@ -2154,16 +2277,40 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
QVarLengthArray<Texture, 8> textures;
QVarLengthArray<Sampler, 8> samplers;
QVarLengthArray<Uav, 8> uavs;
+ void buildBufferBatches(QD3D11ShaderResourceBindings::StageUniformBufferBatches &batches) const
+ {
+ for (const Buffer &buf : buffers) {
+ batches.ubufs.feed(buf.breg, buf.buffer);
+ batches.ubuforigbindings.feed(buf.breg, UINT(buf.binding));
+ batches.ubufoffsets.feed(buf.breg, buf.offsetInConstants);
+ batches.ubufsizes.feed(buf.breg, buf.sizeInConstants);
+ }
+ batches.finish();
+ }
+ void buildSamplerBatches(QD3D11ShaderResourceBindings::StageSamplerBatches &batches) const
+ {
+ for (const Texture &t : textures)
+ batches.shaderresources.feed(t.treg, t.srv);
+ for (const Sampler &s : samplers)
+ batches.samplers.feed(s.sreg, s.sampler);
+ batches.finish();
+ }
+ void buildUavBatches(QD3D11ShaderResourceBindings::StageUavBatches &batches) const
+ {
+ for (const Stage::Uav &u : uavs)
+ batches.uavs.feed(u.ureg, u.uav);
+ batches.finish();
+ }
} res[RBM_SUPPORTED_STAGES];
for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
- const QRhiShaderResourceBinding::Data *b = srbD->sortedBindings.at(i).data();
+ const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(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);
+ Q_ASSERT(aligned(b->u.ubuf.offset, 256u) == 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
@@ -2172,16 +2319,31 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
// Metal) are different in this respect since those do not store
// per-srb vsubufoffsets etc. data so life's a bit easier for them.
// But here we have to defer baking in the dynamic offset.
- const uint offsetInConstants = uint(b->u.ubuf.offset) / 16;
+ const quint32 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 = uint(aligned(b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size, 256) / 16);
+ const quint32 sizeInConstants = aligned(b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size, 256u) / 16;
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps);
if (nativeBinding.first >= 0)
res[RBM_VERTEX].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
}
+ if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationControlStage)) {
+ QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_HULL, nativeResourceBindingMaps);
+ if (nativeBinding.first >= 0)
+ res[RBM_HULL].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationEvaluationStage)) {
+ QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_DOMAIN, nativeResourceBindingMaps);
+ if (nativeBinding.first >= 0)
+ res[RBM_DOMAIN].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::GeometryStage)) {
+ QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_GEOMETRY, nativeResourceBindingMaps);
+ if (nativeBinding.first >= 0)
+ res[RBM_GEOMETRY].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
+ }
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
if (nativeBinding.first >= 0)
@@ -2201,6 +2363,9 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
bd.stex.count = data->count;
const QPair<int, int> nativeBindingVert = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps);
+ const QPair<int, int> nativeBindingHull = mapBinding(b->binding, RBM_HULL, nativeResourceBindingMaps);
+ const QPair<int, int> nativeBindingDomain = mapBinding(b->binding, RBM_DOMAIN, nativeResourceBindingMaps);
+ const QPair<int, int> nativeBindingGeom = mapBinding(b->binding, RBM_GEOMETRY, nativeResourceBindingMaps);
const QPair<int, int> nativeBindingFrag = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
const QPair<int, int> nativeBindingComp = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
// if SPIR-V binding b is mapped to tN and sN in HLSL, and it
@@ -2213,11 +2378,11 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
bd.stex.d[elem].texGeneration = texD ? texD->generation : 0;
bd.stex.d[elem].samplerId = samplerD ? samplerD->m_id : 0;
bd.stex.d[elem].samplerGeneration = samplerD ? samplerD->generation : 0;
+ // Must handle all three cases (combined, separate, separate):
+ // first = texture binding, second = sampler binding
+ // first = texture binding
+ // first = sampler binding
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
- // Must handle all three cases (combined, separate, separate):
- // first = texture binding, second = sampler binding
- // first = texture binding
- // first = sampler binding
const int samplerBinding = texD && samplerD ? nativeBindingVert.second
: (samplerD ? nativeBindingVert.first : -1);
if (nativeBindingVert.first >= 0 && texD)
@@ -2225,9 +2390,33 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
if (samplerBinding >= 0)
res[RBM_VERTEX].samplers.append({ samplerBinding + elem, samplerD->samplerState });
}
+ if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationControlStage)) {
+ const int samplerBinding = texD && samplerD ? nativeBindingHull.second
+ : (samplerD ? nativeBindingHull.first : -1);
+ if (nativeBindingHull.first >= 0 && texD)
+ res[RBM_HULL].textures.append({ nativeBindingHull.first + elem, texD->srv });
+ if (samplerBinding >= 0)
+ res[RBM_HULL].samplers.append({ samplerBinding + elem, samplerD->samplerState });
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::TessellationEvaluationStage)) {
+ const int samplerBinding = texD && samplerD ? nativeBindingDomain.second
+ : (samplerD ? nativeBindingDomain.first : -1);
+ if (nativeBindingDomain.first >= 0 && texD)
+ res[RBM_DOMAIN].textures.append({ nativeBindingDomain.first + elem, texD->srv });
+ if (samplerBinding >= 0)
+ res[RBM_DOMAIN].samplers.append({ samplerBinding + elem, samplerD->samplerState });
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::GeometryStage)) {
+ const int samplerBinding = texD && samplerD ? nativeBindingGeom.second
+ : (samplerD ? nativeBindingGeom.first : -1);
+ if (nativeBindingGeom.first >= 0 && texD)
+ res[RBM_GEOMETRY].textures.append({ nativeBindingGeom.first + elem, texD->srv });
+ if (samplerBinding >= 0)
+ res[RBM_GEOMETRY].samplers.append({ samplerBinding + elem, samplerD->samplerState });
+ }
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
const int samplerBinding = texD && samplerD ? nativeBindingFrag.second
- : (samplerD ? nativeBindingVert.first : -1);
+ : (samplerD ? nativeBindingFrag.first : -1);
if (nativeBindingFrag.first >= 0 && texD)
res[RBM_FRAGMENT].textures.append({ nativeBindingFrag.first + elem, texD->srv });
if (samplerBinding >= 0)
@@ -2235,7 +2424,7 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
}
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
const int samplerBinding = texD && samplerD ? nativeBindingComp.second
- : (samplerD ? nativeBindingVert.first : -1);
+ : (samplerD ? nativeBindingComp.first : -1);
if (nativeBindingComp.first >= 0 && texD)
res[RBM_COMPUTE].textures.append({ nativeBindingComp.first + elem, texD->srv });
if (samplerBinding >= 0)
@@ -2273,7 +2462,7 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
if (nativeBinding.first >= 0) {
- ID3D11UnorderedAccessView *uav = bufD->unorderedAccessView();
+ ID3D11UnorderedAccessView *uav = bufD->unorderedAccessView(b->u.sbuf.offset);
if (uav)
res[RBM_COMPUTE].uavs.append({ nativeBinding.first, uav });
}
@@ -2307,63 +2496,21 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
});
}
- for (const Stage::Buffer &buf : qAsConst(res[RBM_VERTEX].buffers)) {
- srbD->vsubufs.feed(buf.breg, buf.buffer);
- srbD->vsubuforigbindings.feed(buf.breg, UINT(buf.binding));
- srbD->vsubufoffsets.feed(buf.breg, buf.offsetInConstants);
- srbD->vsubufsizes.feed(buf.breg, buf.sizeInConstants);
- }
- srbD->vsubufsPresent = srbD->vsubufs.finish();
- srbD->vsubuforigbindings.finish();
- srbD->vsubufoffsets.finish();
- srbD->vsubufsizes.finish();
-
- for (const Stage::Buffer &buf : qAsConst(res[RBM_FRAGMENT].buffers)) {
- srbD->fsubufs.feed(buf.breg, buf.buffer);
- srbD->fsubuforigbindings.feed(buf.breg, UINT(buf.binding));
- srbD->fsubufoffsets.feed(buf.breg, buf.offsetInConstants);
- srbD->fsubufsizes.feed(buf.breg, buf.sizeInConstants);
- }
- srbD->fsubufsPresent = srbD->fsubufs.finish();
- srbD->fsubuforigbindings.finish();
- srbD->fsubufoffsets.finish();
- srbD->fsubufsizes.finish();
-
- for (const Stage::Buffer &buf : qAsConst(res[RBM_COMPUTE].buffers)) {
- srbD->csubufs.feed(buf.breg, buf.buffer);
- srbD->csubuforigbindings.feed(buf.breg, UINT(buf.binding));
- srbD->csubufoffsets.feed(buf.breg, buf.offsetInConstants);
- srbD->csubufsizes.feed(buf.breg, buf.sizeInConstants);
- }
- srbD->csubufsPresent = srbD->csubufs.finish();
- srbD->csubuforigbindings.finish();
- srbD->csubufoffsets.finish();
- srbD->csubufsizes.finish();
-
- for (const Stage::Texture &t : qAsConst(res[RBM_VERTEX].textures))
- srbD->vsshaderresources.feed(t.treg, t.srv);
- for (const Stage::Sampler &s : qAsConst(res[RBM_VERTEX].samplers))
- srbD->vssamplers.feed(s.sreg, s.sampler);
- srbD->vssamplersPresent = srbD->vssamplers.finish();
- srbD->vsshaderresources.finish();
-
- for (const Stage::Texture &t : qAsConst(res[RBM_FRAGMENT].textures))
- srbD->fsshaderresources.feed(t.treg, t.srv);
- for (const Stage::Sampler &s : qAsConst(res[RBM_FRAGMENT].samplers))
- srbD->fssamplers.feed(s.sreg, s.sampler);
- srbD->fssamplersPresent = srbD->fssamplers.finish();
- srbD->fsshaderresources.finish();
-
- for (const Stage::Texture &t : qAsConst(res[RBM_COMPUTE].textures))
- srbD->csshaderresources.feed(t.treg, t.srv);
- for (const Stage::Sampler &s : qAsConst(res[RBM_COMPUTE].samplers))
- srbD->cssamplers.feed(s.sreg, s.sampler);
- srbD->cssamplersPresent = srbD->cssamplers.finish();
- srbD->csshaderresources.finish();
-
- for (const Stage::Uav &u : qAsConst(res[RBM_COMPUTE].uavs))
- srbD->csUAVs.feed(u.ureg, u.uav);
- srbD->csUAVsPresent = srbD->csUAVs.finish();
+ res[RBM_VERTEX].buildBufferBatches(srbD->vsUniformBufferBatches);
+ res[RBM_HULL].buildBufferBatches(srbD->hsUniformBufferBatches);
+ res[RBM_DOMAIN].buildBufferBatches(srbD->dsUniformBufferBatches);
+ res[RBM_GEOMETRY].buildBufferBatches(srbD->gsUniformBufferBatches);
+ res[RBM_FRAGMENT].buildBufferBatches(srbD->fsUniformBufferBatches);
+ res[RBM_COMPUTE].buildBufferBatches(srbD->csUniformBufferBatches);
+
+ res[RBM_VERTEX].buildSamplerBatches(srbD->vsSamplerBatches);
+ res[RBM_HULL].buildSamplerBatches(srbD->hsSamplerBatches);
+ res[RBM_DOMAIN].buildSamplerBatches(srbD->dsSamplerBatches);
+ res[RBM_GEOMETRY].buildSamplerBatches(srbD->gsSamplerBatches);
+ res[RBM_FRAGMENT].buildSamplerBatches(srbD->fsSamplerBatches);
+ res[RBM_COMPUTE].buildSamplerBatches(srbD->csSamplerBatches);
+
+ res[RBM_COMPUTE].buildUavBatches(srbD->csUavBatches);
}
void QRhiD3D11::executeBufferHostWrites(QD3D11Buffer *bufD)
@@ -2376,17 +2523,18 @@ void QRhiD3D11::executeBufferHostWrites(QD3D11Buffer *bufD)
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, size_t(bufD->m_size));
+ memcpy(mp.pData, bufD->dynBuf, bufD->m_size);
context->Unmap(bufD->buffer, 0);
} else {
- qWarning("Failed to map buffer: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to map buffer: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
}
}
static void applyDynamicOffsets(UINT *offsets,
int batchIndex,
- QRhiBatchedBindings<UINT> *originalBindings,
- QRhiBatchedBindings<UINT> *staticOffsets,
+ const QRhiBatchedBindings<UINT> *originalBindings,
+ const QRhiBatchedBindings<UINT> *staticOffsets,
const uint *dynOfsPairs, int dynOfsPairCount)
{
const int count = staticOffsets->batches[batchIndex].resources.count();
@@ -2417,162 +2565,92 @@ static inline uint clampedResourceCount(uint startSlot, int countSlots, uint max
return countSlots;
}
+#define SETUBUFBATCH(stagePrefixL, stagePrefixU) \
+ if (srbD->stagePrefixL##UniformBufferBatches.present) { \
+ const QD3D11ShaderResourceBindings::StageUniformBufferBatches &batches(srbD->stagePrefixL##UniformBufferBatches); \
+ for (int i = 0, ie = batches.ubufs.batches.count(); i != ie; ++i) { \
+ const uint count = clampedResourceCount(batches.ubufs.batches[i].startBinding, \
+ batches.ubufs.batches[i].resources.count(), \
+ D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT, \
+ #stagePrefixU " cbuf"); \
+ if (count) { \
+ if (!dynOfsPairCount) { \
+ context->stagePrefixU##SetConstantBuffers1(batches.ubufs.batches[i].startBinding, \
+ count, \
+ batches.ubufs.batches[i].resources.constData(), \
+ batches.ubufoffsets.batches[i].resources.constData(), \
+ batches.ubufsizes.batches[i].resources.constData()); \
+ } else { \
+ applyDynamicOffsets(offsets, i, \
+ &batches.ubuforigbindings, &batches.ubufoffsets, \
+ dynOfsPairs, dynOfsPairCount); \
+ context->stagePrefixU##SetConstantBuffers1(batches.ubufs.batches[i].startBinding, \
+ count, \
+ batches.ubufs.batches[i].resources.constData(), \
+ offsets, \
+ batches.ubufsizes.batches[i].resources.constData()); \
+ } \
+ } \
+ } \
+ }
+
+#define SETSAMPLERBATCH(stagePrefixL, stagePrefixU) \
+ if (srbD->stagePrefixL##SamplerBatches.present) { \
+ for (const auto &batch : srbD->stagePrefixL##SamplerBatches.samplers.batches) { \
+ const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), \
+ D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, #stagePrefixU " sampler"); \
+ if (count) \
+ context->stagePrefixU##SetSamplers(batch.startBinding, count, batch.resources.constData()); \
+ } \
+ for (const auto &batch : srbD->stagePrefixL##SamplerBatches.shaderresources.batches) { \
+ const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), \
+ D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, #stagePrefixU " SRV"); \
+ if (count) { \
+ context->stagePrefixU##SetShaderResources(batch.startBinding, count, batch.resources.constData()); \
+ contextState.stagePrefixL##HighestActiveSrvBinding = qMax(contextState.stagePrefixL##HighestActiveSrvBinding, \
+ int(batch.startBinding + count) - 1); \
+ } \
+ } \
+ }
+
+#define SETUAVBATCH(stagePrefixL, stagePrefixU) \
+ if (srbD->stagePrefixL##UavBatches.present) { \
+ for (const auto &batch : srbD->stagePrefixL##UavBatches.uavs.batches) { \
+ const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), \
+ D3D11_1_UAV_SLOT_COUNT, #stagePrefixU " UAV"); \
+ if (count) { \
+ context->stagePrefixU##SetUnorderedAccessViews(batch.startBinding, \
+ count, \
+ batch.resources.constData(), \
+ nullptr); \
+ contextState.stagePrefixL##HighestActiveUavBinding = qMax(contextState.stagePrefixL##HighestActiveUavBinding, \
+ int(batch.startBinding + count) - 1); \
+ } \
+ } \
+ }
+
void QRhiD3D11::bindShaderResources(QD3D11ShaderResourceBindings *srbD,
const uint *dynOfsPairs, int dynOfsPairCount,
bool offsetOnlyChange)
{
UINT offsets[QD3D11CommandBuffer::MAX_DYNAMIC_OFFSET_COUNT];
- if (srbD->vsubufsPresent) {
- for (int i = 0, ie = srbD->vsubufs.batches.count(); i != ie; ++i) {
- const uint count = clampedResourceCount(srbD->vsubufs.batches[i].startBinding,
- srbD->vsubufs.batches[i].resources.count(),
- D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT,
- "VS cbuf");
- if (count) {
- if (!dynOfsPairCount) {
- context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding,
- count,
- srbD->vsubufs.batches[i].resources.constData(),
- srbD->vsubufoffsets.batches[i].resources.constData(),
- srbD->vsubufsizes.batches[i].resources.constData());
- } else {
- applyDynamicOffsets(offsets, i, &srbD->vsubuforigbindings, &srbD->vsubufoffsets,
- dynOfsPairs, dynOfsPairCount);
- context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding,
- count,
- srbD->vsubufs.batches[i].resources.constData(),
- offsets,
- srbD->vsubufsizes.batches[i].resources.constData());
- }
- }
- }
- }
-
- if (srbD->fsubufsPresent) {
- for (int i = 0, ie = srbD->fsubufs.batches.count(); i != ie; ++i) {
- const uint count = clampedResourceCount(srbD->fsubufs.batches[i].startBinding,
- srbD->fsubufs.batches[i].resources.count(),
- D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT,
- "PS cbuf");
- if (count) {
- if (!dynOfsPairCount) {
- context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding,
- count,
- srbD->fsubufs.batches[i].resources.constData(),
- srbD->fsubufoffsets.batches[i].resources.constData(),
- srbD->fsubufsizes.batches[i].resources.constData());
- } else {
- applyDynamicOffsets(offsets, i, &srbD->fsubuforigbindings, &srbD->fsubufoffsets,
- dynOfsPairs, dynOfsPairCount);
- context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding,
- count,
- srbD->fsubufs.batches[i].resources.constData(),
- offsets,
- srbD->fsubufsizes.batches[i].resources.constData());
- }
- }
- }
- }
-
- if (srbD->csubufsPresent) {
- for (int i = 0, ie = srbD->csubufs.batches.count(); i != ie; ++i) {
- const uint count = clampedResourceCount(srbD->csubufs.batches[i].startBinding,
- srbD->csubufs.batches[i].resources.count(),
- D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT,
- "CS cbuf");
- if (count) {
- if (!dynOfsPairCount) {
- context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding,
- count,
- srbD->csubufs.batches[i].resources.constData(),
- srbD->csubufoffsets.batches[i].resources.constData(),
- srbD->csubufsizes.batches[i].resources.constData());
- } else {
- applyDynamicOffsets(offsets, i, &srbD->csubuforigbindings, &srbD->csubufoffsets,
- dynOfsPairs, dynOfsPairCount);
- context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding,
- count,
- srbD->csubufs.batches[i].resources.constData(),
- offsets,
- srbD->csubufsizes.batches[i].resources.constData());
- }
- }
- }
- }
+ SETUBUFBATCH(vs, VS)
+ SETUBUFBATCH(hs, HS)
+ SETUBUFBATCH(ds, DS)
+ SETUBUFBATCH(gs, GS)
+ SETUBUFBATCH(fs, PS)
+ SETUBUFBATCH(cs, CS)
if (!offsetOnlyChange) {
- if (srbD->vssamplersPresent) {
- for (const auto &batch : srbD->vssamplers.batches) {
- const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
- D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, "VS sampler");
- if (count)
- context->VSSetSamplers(batch.startBinding, count, batch.resources.constData());
- }
-
- for (const auto &batch : srbD->vsshaderresources.batches) {
- const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
- D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, "VS SRV");
- if (count) {
- context->VSSetShaderResources(batch.startBinding, count, batch.resources.constData());
- contextState.vsHighestActiveSrvBinding = qMax(contextState.vsHighestActiveSrvBinding,
- int(batch.startBinding + count) - 1);
- }
- }
- }
-
- if (srbD->fssamplersPresent) {
- for (const auto &batch : srbD->fssamplers.batches) {
- const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
- D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, "PS sampler");
- if (count)
- context->PSSetSamplers(batch.startBinding, count, batch.resources.constData());
- }
-
- for (const auto &batch : srbD->fsshaderresources.batches) {
- const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
- D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, "PS SRV");
- if (count) {
- context->PSSetShaderResources(batch.startBinding, count, batch.resources.constData());
- contextState.fsHighestActiveSrvBinding = qMax(contextState.fsHighestActiveSrvBinding,
- int(batch.startBinding + count) - 1);
- }
- }
- }
-
- if (srbD->cssamplersPresent) {
- for (const auto &batch : srbD->cssamplers.batches) {
- const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
- D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, "CS sampler");
- if (count)
- context->CSSetSamplers(batch.startBinding, count, batch.resources.constData());
- }
+ SETSAMPLERBATCH(vs, VS)
+ SETSAMPLERBATCH(hs, HS)
+ SETSAMPLERBATCH(ds, DS)
+ SETSAMPLERBATCH(gs, GS)
+ SETSAMPLERBATCH(fs, PS)
+ SETSAMPLERBATCH(cs, CS)
- for (const auto &batch : srbD->csshaderresources.batches) {
- const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
- D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, "CS SRV");
- if (count) {
- context->CSSetShaderResources(batch.startBinding, count, batch.resources.constData());
- contextState.csHighestActiveSrvBinding = qMax(contextState.csHighestActiveSrvBinding,
- int(batch.startBinding + count) - 1);
- }
- }
- }
-
- if (srbD->csUAVsPresent) {
- for (const auto &batch : srbD->csUAVs.batches) {
- const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(),
- D3D11_1_UAV_SLOT_COUNT, "CS UAV");
- if (count) {
- context->CSSetUnorderedAccessViews(batch.startBinding,
- count,
- batch.resources.constData(),
- nullptr);
- contextState.csHighestActiveUavBinding = qMax(contextState.csHighestActiveUavBinding,
- int(batch.startBinding + count) - 1);
- }
- }
- }
+ SETUAVBATCH(cs, CS)
}
}
@@ -2601,6 +2679,9 @@ void QRhiD3D11::resetShaderResources()
}
int nullsrvCount = qMax(contextState.vsHighestActiveSrvBinding, contextState.fsHighestActiveSrvBinding);
+ nullsrvCount = qMax(nullsrvCount, contextState.hsHighestActiveSrvBinding);
+ nullsrvCount = qMax(nullsrvCount, contextState.dsHighestActiveSrvBinding);
+ nullsrvCount = qMax(nullsrvCount, contextState.gsHighestActiveSrvBinding);
nullsrvCount = qMax(nullsrvCount, contextState.csHighestActiveSrvBinding);
nullsrvCount += 1;
if (nullsrvCount > 0) {
@@ -2612,6 +2693,18 @@ void QRhiD3D11::resetShaderResources()
context->VSSetShaderResources(0, UINT(contextState.vsHighestActiveSrvBinding + 1), nullsrvs.constData());
contextState.vsHighestActiveSrvBinding = -1;
}
+ if (contextState.hsHighestActiveSrvBinding >= 0) {
+ context->HSSetShaderResources(0, UINT(contextState.hsHighestActiveSrvBinding + 1), nullsrvs.constData());
+ contextState.hsHighestActiveSrvBinding = -1;
+ }
+ if (contextState.dsHighestActiveSrvBinding >= 0) {
+ context->DSSetShaderResources(0, UINT(contextState.dsHighestActiveSrvBinding + 1), nullsrvs.constData());
+ contextState.dsHighestActiveSrvBinding = -1;
+ }
+ if (contextState.gsHighestActiveSrvBinding >= 0) {
+ context->GSSetShaderResources(0, UINT(contextState.gsHighestActiveSrvBinding + 1), nullsrvs.constData());
+ contextState.gsHighestActiveSrvBinding = -1;
+ }
if (contextState.fsHighestActiveSrvBinding >= 0) {
context->PSSetShaderResources(0, UINT(contextState.fsHighestActiveSrvBinding + 1), nullsrvs.constData());
contextState.fsHighestActiveSrvBinding = -1;
@@ -2633,31 +2726,52 @@ void QRhiD3D11::resetShaderResources()
}
}
-void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain)
+#define SETSHADER(StageL, StageU) \
+ if (psD->StageL.shader) { \
+ context->StageU##SetShader(psD->StageL.shader, nullptr, 0); \
+ currentShaderMask |= StageU##MaskBit; \
+ } else if (currentShaderMask & StageU##MaskBit) { \
+ context->StageU##SetShader(nullptr, nullptr, 0); \
+ currentShaderMask &= ~StageU##MaskBit; \
+ }
+
+void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD)
{
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(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv);
- context->End(tsStart); // just record a timestamp, no Begin needed
- }
- }
+ enum ActiveShaderMask {
+ VSMaskBit = 0x01,
+ HSMaskBit = 0x02,
+ DSMaskBit = 0x04,
+ GSMaskBit = 0x08,
+ PSMaskBit = 0x10
+ };
+ int currentShaderMask = 0xFF;
for (auto it = cbD->commands.cbegin(), end = cbD->commands.cend(); it != end; ++it) {
const QD3D11CommandBuffer::Command &cmd(*it);
switch (cmd.cmd) {
+ case QD3D11CommandBuffer::Command::BeginFrame:
+ if (cmd.args.beginFrame.tsDisjointQuery)
+ context->Begin(cmd.args.beginFrame.tsDisjointQuery);
+ if (cmd.args.beginFrame.tsQuery) {
+ if (cmd.args.beginFrame.swapchainData) {
+ // 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.
+ QD3D11RenderTargetData *rtD = cmd.args.beginFrame.swapchainData;
+ context->OMSetRenderTargets(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv);
+ }
+ context->End(cmd.args.beginFrame.tsQuery); // no Begin() for D3D11_QUERY_TIMESTAMP
+ }
+ break;
+ case QD3D11CommandBuffer::Command::EndFrame:
+ if (cmd.args.endFrame.tsQuery)
+ context->End(cmd.args.endFrame.tsQuery);
+ if (cmd.args.endFrame.tsDisjointQuery)
+ context->End(cmd.args.endFrame.tsDisjointQuery);
+ break;
case QD3D11CommandBuffer::Command::ResetShaderResources:
resetShaderResources();
break;
@@ -2725,8 +2839,11 @@ void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *
case QD3D11CommandBuffer::Command::BindGraphicsPipeline:
{
QD3D11GraphicsPipeline *psD = cmd.args.bindGraphicsPipeline.ps;
- context->VSSetShader(psD->vs.shader, nullptr, 0);
- context->PSSetShader(psD->fs.shader, nullptr, 0);
+ SETSHADER(vs, VS)
+ SETSHADER(hs, HS)
+ SETSHADER(ds, DS)
+ SETSHADER(gs, GS)
+ SETSHADER(fs, PS)
context->IASetPrimitiveTopology(psD->d3dTopology);
context->IASetInputLayout(psD->inputLayout); // may be null, that's ok
context->OMSetDepthStencilState(psD->dsState, stencilRef);
@@ -2812,7 +2929,7 @@ void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *
}
}
-QD3D11Buffer::QD3D11Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
+QD3D11Buffer::QD3D11Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
: QRhiBuffer(rhi, type, usage, size)
{
}
@@ -2833,10 +2950,9 @@ void QD3D11Buffer::destroy()
delete[] dynBuf;
dynBuf = nullptr;
- if (uav) {
- uav->Release();
- uav = nullptr;
- }
+ for (auto it = uavs.begin(), end = uavs.end(); it != end; ++it)
+ it.value()->Release();
+ uavs.clear();
QRHI_RES_RHI(QRhiD3D11);
if (rhiD)
@@ -2872,12 +2988,11 @@ bool QD3D11Buffer::create()
return false;
}
- const int nonZeroSize = m_size <= 0 ? 256 : m_size;
- const int roundedSize = aligned(nonZeroSize, m_usage.testFlag(QRhiBuffer::UniformBuffer) ? 256 : 4);
+ const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
+ const quint32 roundedSize = aligned(nonZeroSize, m_usage.testFlag(QRhiBuffer::UniformBuffer) ? 256u : 4u);
- D3D11_BUFFER_DESC desc;
- memset(&desc, 0, sizeof(desc));
- desc.ByteWidth = UINT(roundedSize);
+ D3D11_BUFFER_DESC 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;
@@ -2886,7 +3001,8 @@ bool QD3D11Buffer::create()
QRHI_RES_RHI(QRhiD3D11);
HRESULT hr = rhiD->dev->CreateBuffer(&desc, nullptr, &buffer);
if (FAILED(hr)) {
- qWarning("Failed to create buffer: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create buffer: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
@@ -2925,7 +3041,8 @@ char *QD3D11Buffer::beginFullDynamicBufferUpdateForCurrentFrame()
QRHI_RES_RHI(QRhiD3D11);
HRESULT hr = rhiD->context->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mp);
if (FAILED(hr)) {
- qWarning("Failed to map buffer: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to map buffer: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return nullptr;
}
return static_cast<char *>(mp.pData);
@@ -2937,27 +3054,30 @@ void QD3D11Buffer::endFullDynamicBufferUpdateForCurrentFrame()
rhiD->context->Unmap(buffer, 0);
}
-ID3D11UnorderedAccessView *QD3D11Buffer::unorderedAccessView()
+ID3D11UnorderedAccessView *QD3D11Buffer::unorderedAccessView(quint32 offset)
{
- if (uav)
- return uav;
+ auto it = uavs.find(offset);
+ if (it != uavs.end())
+ return it.value();
// SPIRV-Cross generated HLSL uses RWByteAddressBuffer
- D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
- memset(&desc, 0, sizeof(desc));
+ D3D11_UNORDERED_ACCESS_VIEW_DESC desc = {};
desc.Format = DXGI_FORMAT_R32_TYPELESS;
desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
- desc.Buffer.FirstElement = 0;
- desc.Buffer.NumElements = UINT(aligned(m_size, 4) / 4);
+ desc.Buffer.FirstElement = offset / 4u;
+ desc.Buffer.NumElements = aligned(m_size - offset, 4u) / 4u;
desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;
QRHI_RES_RHI(QRhiD3D11);
+ ID3D11UnorderedAccessView *uav = nullptr;
HRESULT hr = rhiD->dev->CreateUnorderedAccessView(buffer, &desc, &uav);
if (FAILED(hr)) {
- qWarning("Failed to create UAV: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create UAV: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return nullptr;
}
+ uavs[offset] = uav;
return uav;
}
@@ -3005,10 +3125,9 @@ bool QD3D11RenderBuffer::create()
return false;
QRHI_RES_RHI(QRhiD3D11);
- sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
+ sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount);
- D3D11_TEXTURE2D_DESC desc;
- memset(&desc, 0, sizeof(desc));
+ D3D11_TEXTURE2D_DESC desc = {};
desc.Width = UINT(m_pixelSize.width());
desc.Height = UINT(m_pixelSize.height());
desc.MipLevels = 1;
@@ -3023,17 +3142,18 @@ bool QD3D11RenderBuffer::create()
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)));
+ qWarning("Failed to create color renderbuffer: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
- D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
- memset(&rtvDesc, 0, sizeof(rtvDesc));
+ D3D11_RENDER_TARGET_VIEW_DESC 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)));
+ qWarning("Failed to create rtv: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
} else if (m_type == DepthStencil) {
@@ -3042,17 +3162,18 @@ bool QD3D11RenderBuffer::create()
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)));
+ qWarning("Failed to create depth-stencil buffer: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
- D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
- memset(&dsvDesc, 0, sizeof(dsvDesc));
+ D3D11_DEPTH_STENCIL_VIEW_DESC 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)));
+ qWarning("Failed to create dsv: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
} else {
@@ -3090,7 +3211,7 @@ QD3D11Texture::~QD3D11Texture()
void QD3D11Texture::destroy()
{
- if (!tex && !tex3D)
+ if (!tex && !tex3D && !tex1D)
return;
if (srv) {
@@ -3110,10 +3231,13 @@ void QD3D11Texture::destroy()
tex->Release();
if (tex3D)
tex3D->Release();
+ if (tex1D)
+ tex1D->Release();
}
tex = nullptr;
tex3D = nullptr;
+ tex1D = nullptr;
QRHI_RES_RHI(QRhiD3D11);
if (rhiD)
@@ -3143,7 +3267,7 @@ static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format)
case QRhiTexture::Format::D16:
return DXGI_FORMAT_D16_UNORM;
case QRhiTexture::Format::D24:
- return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
+ return DXGI_FORMAT_D24_UNORM_S8_UINT;
case QRhiTexture::Format::D24S8:
return DXGI_FORMAT_D24_UNORM_S8_UINT;
case QRhiTexture::Format::D32F:
@@ -3156,20 +3280,23 @@ static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format)
bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
{
- if (tex || tex3D)
+ if (tex || tex3D || tex1D)
destroy();
- 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 is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ const bool is1D = m_flags.testFlag(OneDimensional);
+
+ const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
+ : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
QRHI_RES_RHI(QRhiD3D11);
dxgiFormat = toD3DTextureFormat(m_format, m_flags);
mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
- sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
+ sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount);
if (sampleDesc.Count > 1) {
if (isCube) {
qWarning("Cubemap texture cannot be multisample");
@@ -3196,12 +3323,18 @@ bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both array and 3D");
return false;
}
- m_depth = qMax(1, m_depth);
+ if (isCube && is1D) {
+ qWarning("Texture cannot be both cube and 1D");
+ return false;
+ }
+ if (is1D && is3D) {
+ qWarning("Texture cannot be both 1D and 3D");
+ return false;
+ }
if (m_depth > 1 && !is3D) {
qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
return false;
}
- m_arraySize = qMax(0, m_arraySize);
if (m_arraySize > 0 && !isArray) {
qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
return false;
@@ -3224,15 +3357,30 @@ bool QD3D11Texture::finishCreate()
const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
+ const bool is1D = m_flags.testFlag(OneDimensional);
- D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
- memset(&srvDesc, 0, sizeof(srvDesc));
+ D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Format = isDepth ? toD3DDepthTextureSRVFormat(m_format) : dxgiFormat;
if (isCube) {
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
srvDesc.TextureCube.MipLevels = mipLevelCount;
} else {
- if (isArray) {
+ if (is1D) {
+ if (isArray) {
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY;
+ srvDesc.Texture1DArray.MipLevels = mipLevelCount;
+ if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
+ srvDesc.Texture1DArray.FirstArraySlice = UINT(m_arrayRangeStart);
+ srvDesc.Texture1DArray.ArraySize = UINT(m_arrayRangeLength);
+ } else {
+ srvDesc.Texture1DArray.FirstArraySlice = 0;
+ srvDesc.Texture1DArray.ArraySize = UINT(qMax(0, m_arraySize));
+ }
+ } else {
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D;
+ srvDesc.Texture1D.MipLevels = mipLevelCount;
+ }
+ } else if (isArray) {
if (sampleDesc.Count > 1) {
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY;
if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
@@ -3240,7 +3388,7 @@ bool QD3D11Texture::finishCreate()
srvDesc.Texture2DMSArray.ArraySize = UINT(m_arrayRangeLength);
} else {
srvDesc.Texture2DMSArray.FirstArraySlice = 0;
- srvDesc.Texture2DMSArray.ArraySize = UINT(m_arraySize);
+ srvDesc.Texture2DMSArray.ArraySize = UINT(qMax(0, m_arraySize));
}
} else {
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
@@ -3250,7 +3398,7 @@ bool QD3D11Texture::finishCreate()
srvDesc.Texture2DArray.ArraySize = UINT(m_arrayRangeLength);
} else {
srvDesc.Texture2DArray.FirstArraySlice = 0;
- srvDesc.Texture2DArray.ArraySize = UINT(m_arraySize);
+ srvDesc.Texture2DArray.ArraySize = UINT(qMax(0, m_arraySize));
}
}
} else {
@@ -3268,7 +3416,8 @@ bool QD3D11Texture::finishCreate()
HRESULT hr = rhiD->dev->CreateShaderResourceView(textureResource(), &srvDesc, &srv);
if (FAILED(hr)) {
- qWarning("Failed to create srv: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create srv: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
@@ -3286,6 +3435,7 @@ bool QD3D11Texture::create()
const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
+ const bool is1D = m_flags.testFlag(OneDimensional);
uint bindFlags = D3D11_BIND_SHADER_RESOURCE;
uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
@@ -3307,13 +3457,31 @@ bool QD3D11Texture::create()
bindFlags |= D3D11_BIND_UNORDERED_ACCESS;
QRHI_RES_RHI(QRhiD3D11);
- if (!is3D) {
- D3D11_TEXTURE2D_DESC desc;
- memset(&desc, 0, sizeof(desc));
+ if (is1D) {
+ D3D11_TEXTURE1D_DESC desc = {};
+ desc.Width = UINT(size.width());
+ desc.MipLevels = mipLevelCount;
+ desc.ArraySize = isArray ? UINT(qMax(0, m_arraySize)) : 1;
+ desc.Format = dxgiFormat;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = bindFlags;
+ desc.MiscFlags = miscFlags;
+
+ HRESULT hr = rhiD->dev->CreateTexture1D(&desc, nullptr, &tex1D);
+ if (FAILED(hr)) {
+ qWarning("Failed to create 1D texture: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ if (!m_objectName.isEmpty())
+ tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()),
+ m_objectName.constData());
+ } else if (!is3D) {
+ D3D11_TEXTURE2D_DESC desc = {};
desc.Width = UINT(size.width());
desc.Height = UINT(size.height());
desc.MipLevels = mipLevelCount;
- desc.ArraySize = isCube ? 6 : (isArray ? UINT(m_arraySize) : 1);
+ desc.ArraySize = isCube ? 6 : (isArray ? UINT(qMax(0, m_arraySize)) : 1);
desc.Format = dxgiFormat;
desc.SampleDesc = sampleDesc;
desc.Usage = D3D11_USAGE_DEFAULT;
@@ -3322,17 +3490,17 @@ bool QD3D11Texture::create()
HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
if (FAILED(hr)) {
- qWarning("Failed to create 2D texture: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create 2D texture: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
if (!m_objectName.isEmpty())
tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
} else {
- D3D11_TEXTURE3D_DESC desc;
- memset(&desc, 0, sizeof(desc));
+ D3D11_TEXTURE3D_DESC desc = {};
desc.Width = UINT(size.width());
desc.Height = UINT(size.height());
- desc.Depth = UINT(m_depth);
+ desc.Depth = UINT(qMax(1, m_depth));
desc.MipLevels = mipLevelCount;
desc.Format = dxgiFormat;
desc.Usage = D3D11_USAGE_DEFAULT;
@@ -3341,7 +3509,8 @@ bool QD3D11Texture::create()
HRESULT hr = rhiD->dev->CreateTexture3D(&desc, nullptr, &tex3D);
if (FAILED(hr)) {
- qWarning("Failed to create 3D texture: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create 3D texture: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
if (!m_objectName.isEmpty())
@@ -3366,6 +3535,8 @@ bool QD3D11Texture::createFrom(QRhiTexture::NativeTexture src)
if (m_flags.testFlag(ThreeDimensional))
tex3D = reinterpret_cast<ID3D11Texture3D *>(src.object);
+ else if (m_flags.testFlags(OneDimensional))
+ tex1D = reinterpret_cast<ID3D11Texture1D *>(src.object);
else
tex = reinterpret_cast<ID3D11Texture2D *>(src.object);
@@ -3391,8 +3562,7 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
- D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
- memset(&desc, 0, sizeof(desc));
+ D3D11_UNORDERED_ACCESS_VIEW_DESC desc = {};
desc.Format = dxgiFormat;
if (isCube) {
desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
@@ -3403,7 +3573,7 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
desc.Texture2DArray.MipSlice = UINT(level);
desc.Texture2DArray.FirstArraySlice = 0;
- desc.Texture2DArray.ArraySize = UINT(m_arraySize);
+ desc.Texture2DArray.ArraySize = UINT(qMax(0, m_arraySize));
} else if (is3D) {
desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;
desc.Texture3D.MipSlice = UINT(level);
@@ -3416,7 +3586,8 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
ID3D11UnorderedAccessView *uav = nullptr;
HRESULT hr = rhiD->dev->CreateUnorderedAccessView(textureResource(), &desc, &uav);
if (FAILED(hr)) {
- qWarning("Failed to create UAV: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create UAV: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return nullptr;
}
@@ -3525,8 +3696,7 @@ bool QD3D11Sampler::create()
if (samplerState)
destroy();
- D3D11_SAMPLER_DESC desc;
- memset(&desc, 0, sizeof(desc));
+ D3D11_SAMPLER_DESC desc = {};
desc.Filter = toD3DFilter(m_minFilter, m_magFilter, m_mipmapMode);
if (m_compareOp != Never)
desc.Filter = D3D11_FILTER(desc.Filter | 0x80);
@@ -3540,7 +3710,8 @@ bool QD3D11Sampler::create()
QRHI_RES_RHI(QRhiD3D11);
HRESULT hr = rhiD->dev->CreateSamplerState(&desc, &samplerState);
if (FAILED(hr)) {
- qWarning("Failed to create sampler state: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create sampler state: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
@@ -3562,7 +3733,9 @@ QD3D11RenderPassDescriptor::~QD3D11RenderPassDescriptor()
void QD3D11RenderPassDescriptor::destroy()
{
- // nothing to do here
+ QRHI_RES_RHI(QRhiD3D11);
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
bool QD3D11RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
@@ -3573,7 +3746,10 @@ bool QD3D11RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *ot
QRhiRenderPassDescriptor *QD3D11RenderPassDescriptor::newCompatibleRenderPassDescriptor() const
{
- return new QD3D11RenderPassDescriptor(m_rhi);
+ QD3D11RenderPassDescriptor *rpD = new QD3D11RenderPassDescriptor(m_rhi);
+ QRHI_RES_RHI(QRhiD3D11);
+ rhiD->registerResource(rpD, false);
+ return rpD;
}
QVector<quint32> QD3D11RenderPassDescriptor::serializedFormat() const
@@ -3655,7 +3831,10 @@ void QD3D11TextureRenderTarget::destroy()
QRhiRenderPassDescriptor *QD3D11TextureRenderTarget::newCompatibleRenderPassDescriptor()
{
- return new QD3D11RenderPassDescriptor(m_rhi);
+ QD3D11RenderPassDescriptor *rpD = new QD3D11RenderPassDescriptor(m_rhi);
+ QRHI_RES_RHI(QRhiD3D11);
+ rhiD->registerResource(rpD, false);
+ return rpD;
}
bool QD3D11TextureRenderTarget::create()
@@ -3663,8 +3842,7 @@ bool QD3D11TextureRenderTarget::create()
if (rtv[0] || dsv)
destroy();
- const bool hasColorAttachments = m_desc.cbeginColorAttachments() != m_desc.cendColorAttachments();
- Q_ASSERT(hasColorAttachments || m_desc.depthTexture());
+ Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
@@ -3680,14 +3858,23 @@ bool QD3D11TextureRenderTarget::create()
Q_ASSERT(texture || rb);
if (texture) {
QD3D11Texture *texD = QRHI_RES(QD3D11Texture, texture);
- D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
- memset(&rtvDesc, 0, sizeof(rtvDesc));
+ D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
rtvDesc.Format = toD3DTextureFormat(texD->format(), texD->flags());
if (texD->flags().testFlag(QRhiTexture::CubeMap)) {
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
rtvDesc.Texture2DArray.ArraySize = 1;
+ } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) {
+ if (texD->flags().testFlag(QRhiTexture::TextureArray)) {
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1DARRAY;
+ rtvDesc.Texture1DArray.MipSlice = UINT(colorAtt.level());
+ rtvDesc.Texture1DArray.FirstArraySlice = UINT(colorAtt.layer());
+ rtvDesc.Texture1DArray.ArraySize = 1;
+ } else {
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1D;
+ rtvDesc.Texture1D.MipSlice = UINT(colorAtt.level());
+ }
} else if (texD->flags().testFlag(QRhiTexture::TextureArray)) {
if (texD->sampleDesc.Count > 1) {
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY;
@@ -3714,7 +3901,8 @@ bool QD3D11TextureRenderTarget::create()
}
HRESULT hr = rhiD->dev->CreateRenderTargetView(texD->textureResource(), &rtvDesc, &rtv[attIndex]);
if (FAILED(hr)) {
- qWarning("Failed to create rtv: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create rtv: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
ownsRtv[attIndex] = true;
@@ -3738,14 +3926,35 @@ bool QD3D11TextureRenderTarget::create()
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));
+ D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
dsvDesc.Format = toD3DDepthTextureDSVFormat(depthTexD->format());
dsvDesc.ViewDimension = depthTexD->sampleDesc.Count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS
: D3D11_DSV_DIMENSION_TEXTURE2D;
+ if (depthTexD->flags().testFlag(QRhiTexture::TextureArray)) {
+ if (depthTexD->sampleDesc.Count > 1) {
+ dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY;
+ if (depthTexD->arrayRangeStart() >= 0 && depthTexD->arrayRangeLength() >= 0) {
+ dsvDesc.Texture2DMSArray.FirstArraySlice = UINT(depthTexD->arrayRangeStart());
+ dsvDesc.Texture2DMSArray.ArraySize = UINT(depthTexD->arrayRangeLength());
+ } else {
+ dsvDesc.Texture2DMSArray.FirstArraySlice = 0;
+ dsvDesc.Texture2DMSArray.ArraySize = UINT(qMax(0, depthTexD->arraySize()));
+ }
+ } else {
+ dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
+ if (depthTexD->arrayRangeStart() >= 0 && depthTexD->arrayRangeLength() >= 0) {
+ dsvDesc.Texture2DArray.FirstArraySlice = UINT(depthTexD->arrayRangeStart());
+ dsvDesc.Texture2DArray.ArraySize = UINT(depthTexD->arrayRangeLength());
+ } else {
+ dsvDesc.Texture2DArray.FirstArraySlice = 0;
+ dsvDesc.Texture2DArray.ArraySize = UINT(qMax(0, depthTexD->arraySize()));
+ }
+ }
+ }
HRESULT hr = rhiD->dev->CreateDepthStencilView(depthTexD->tex, &dsvDesc, &dsv);
if (FAILED(hr)) {
- qWarning("Failed to create dsv: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create dsv: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
if (d.colorAttCount == 0) {
@@ -3810,6 +4019,10 @@ void QD3D11ShaderResourceBindings::destroy()
{
sortedBindings.clear();
boundResourceData.clear();
+
+ QRHI_RES_RHI(QRhiD3D11);
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
bool QD3D11ShaderResourceBindings::create()
@@ -3824,11 +4037,7 @@ bool QD3D11ShaderResourceBindings::create()
rhiD->updateLayoutDesc(this);
std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
- std::sort(sortedBindings.begin(), sortedBindings.end(),
- [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
- {
- return a.data()->binding < b.data()->binding;
- });
+ std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
boundResourceData.resize(sortedBindings.count());
@@ -3837,7 +4046,7 @@ bool QD3D11ShaderResourceBindings::create()
hasDynamicOffset = false;
for (const QRhiShaderResourceBinding &b : sortedBindings) {
- const QRhiShaderResourceBinding::Data *bd = b.data();
+ const QRhiShaderResourceBinding::Data *bd = QRhiImplementation::shaderResourceBindingData(b);
if (bd->type == QRhiShaderResourceBinding::UniformBuffer && bd->u.ubuf.hasDynamicOffset) {
hasDynamicOffset = true;
break;
@@ -3845,6 +4054,7 @@ bool QD3D11ShaderResourceBindings::create()
}
generation += 1;
+ rhiD->registerResource(this, false);
return true;
}
@@ -3852,13 +4062,8 @@ void QD3D11ShaderResourceBindings::updateResources(UpdateFlags flags)
{
sortedBindings.clear();
std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
- if (!flags.testFlag(BindingsAreSorted)) {
- std::sort(sortedBindings.begin(), sortedBindings.end(),
- [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
- {
- return a.data()->binding < b.data()->binding;
- });
- }
+ if (!flags.testFlag(BindingsAreSorted))
+ std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
Q_ASSERT(boundResourceData.count() == sortedBindings.count());
for (BoundResourceData &bd : boundResourceData)
@@ -3877,6 +4082,16 @@ QD3D11GraphicsPipeline::~QD3D11GraphicsPipeline()
destroy();
}
+template<typename T>
+inline void releasePipelineShader(T &s)
+{
+ if (s.shader) {
+ s.shader->Release();
+ s.shader = nullptr;
+ }
+ s.nativeResourceBindingMap.clear();
+}
+
void QD3D11GraphicsPipeline::destroy()
{
if (!dsState)
@@ -3900,17 +4115,11 @@ void QD3D11GraphicsPipeline::destroy()
rastState = nullptr;
}
- if (vs.shader) {
- vs.shader->Release();
- vs.shader = nullptr;
- }
- vs.nativeResourceBindingMap.clear();
-
- if (fs.shader) {
- fs.shader->Release();
- fs.shader = nullptr;
- }
- fs.nativeResourceBindingMap.clear();
+ releasePipelineShader(vs);
+ releasePipelineShader(hs);
+ releasePipelineShader(ds);
+ releasePipelineShader(gs);
+ releasePipelineShader(fs);
QRHI_RES_RHI(QRhiD3D11);
if (rhiD)
@@ -4028,13 +4237,37 @@ static inline DXGI_FORMAT toD3DAttributeFormat(QRhiVertexInputAttribute::Format
return DXGI_FORMAT_R32G32_SINT;
case QRhiVertexInputAttribute::SInt:
return DXGI_FORMAT_R32_SINT;
+ case QRhiVertexInputAttribute::Half4:
+ // Note: D3D does not support half3. Pass through half3 as half4.
+ case QRhiVertexInputAttribute::Half3:
+ return DXGI_FORMAT_R16G16B16A16_FLOAT;
+ case QRhiVertexInputAttribute::Half2:
+ return DXGI_FORMAT_R16G16_FLOAT;
+ case QRhiVertexInputAttribute::Half:
+ return DXGI_FORMAT_R16_FLOAT;
+ case QRhiVertexInputAttribute::UShort4:
+ // Note: D3D does not support UShort3. Pass through UShort3 as UShort4.
+ case QRhiVertexInputAttribute::UShort3:
+ return DXGI_FORMAT_R16G16B16A16_UINT;
+ case QRhiVertexInputAttribute::UShort2:
+ return DXGI_FORMAT_R16G16_UINT;
+ case QRhiVertexInputAttribute::UShort:
+ return DXGI_FORMAT_R16_UINT;
+ case QRhiVertexInputAttribute::SShort4:
+ // Note: D3D does not support SShort3. Pass through SShort3 as SShort4.
+ case QRhiVertexInputAttribute::SShort3:
+ return DXGI_FORMAT_R16G16B16A16_SINT;
+ case QRhiVertexInputAttribute::SShort2:
+ return DXGI_FORMAT_R16G16_SINT;
+ case QRhiVertexInputAttribute::SShort:
+ return DXGI_FORMAT_R16_SINT;
default:
Q_UNREACHABLE();
return DXGI_FORMAT_R32G32B32A32_FLOAT;
}
}
-static inline D3D11_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topology t)
+static inline D3D11_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topology t, int patchControlPointCount)
{
switch (t) {
case QRhiGraphicsPipeline::Triangles:
@@ -4047,6 +4280,9 @@ static inline D3D11_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topol
return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP;
case QRhiGraphicsPipeline::Points:
return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
+ case QRhiGraphicsPipeline::Patches:
+ Q_ASSERT(patchControlPointCount >= 1 && patchControlPointCount <= 32);
+ return D3D11_PRIMITIVE_TOPOLOGY(D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + (patchControlPointCount - 1));
default:
Q_UNREACHABLE();
return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
@@ -4137,18 +4373,6 @@ static inline D3D11_BLEND_OP toD3DBlendOp(QRhiGraphicsPipeline::BlendOp op)
}
}
-static pD3DCompile resolveD3DCompile()
-{
- for (const wchar_t *libraryName : {L"D3DCompiler_47", L"D3DCompiler_43"}) {
- QSystemLibrary library(libraryName);
- if (library.load()) {
- if (auto symbol = library.resolve("D3DCompile"))
- return reinterpret_cast<pD3DCompile>(symbol);
- }
- }
- return nullptr;
-}
-
static inline QByteArray sourceHash(const QByteArray &source)
{
// taken from the GL backend, use the same mechanism to get a key
@@ -4214,7 +4438,7 @@ QByteArray QRhiD3D11::compileHlslShaderSource(const QShader &shader, QShader::Va
return cacheIt.value();
}
- static const pD3DCompile d3dCompile = resolveD3DCompile();
+ static const pD3DCompile d3dCompile = QRhiD3D::resolveD3DCompile();
if (d3dCompile == nullptr) {
qWarning("Unable to resolve function D3DCompile()");
return QByteArray();
@@ -4252,11 +4476,11 @@ bool QD3D11GraphicsPipeline::create()
destroy();
QRHI_RES_RHI(QRhiD3D11);
+ rhiD->pipelineCreationStart();
if (!rhiD->sanityCheckGraphicsPipeline(this))
return false;
- D3D11_RASTERIZER_DESC rastDesc;
- memset(&rastDesc, 0, sizeof(rastDesc));
+ D3D11_RASTERIZER_DESC rastDesc = {};
rastDesc.FillMode = toD3DFillMode(m_polygonMode);
rastDesc.CullMode = toD3DCullMode(m_cullMode);
rastDesc.FrontCounterClockwise = m_frontFace == CCW;
@@ -4264,15 +4488,15 @@ bool QD3D11GraphicsPipeline::create()
rastDesc.SlopeScaledDepthBias = m_slopeScaledDepthBias;
rastDesc.DepthClipEnable = true;
rastDesc.ScissorEnable = m_flags.testFlag(UsesScissor);
- rastDesc.MultisampleEnable = rhiD->effectiveSampleCount(m_sampleCount).Count > 1;
+ rastDesc.MultisampleEnable = rhiD->effectiveSampleDesc(m_sampleCount).Count > 1;
HRESULT hr = rhiD->dev->CreateRasterizerState(&rastDesc, &rastState);
if (FAILED(hr)) {
- qWarning("Failed to create rasterizer state: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create rasterizer state: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
- D3D11_DEPTH_STENCIL_DESC dsDesc;
- memset(&dsDesc, 0, sizeof(dsDesc));
+ D3D11_DEPTH_STENCIL_DESC dsDesc = {};
dsDesc.DepthEnable = m_depthTest;
dsDesc.DepthWriteMask = m_depthWrite ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
dsDesc.DepthFunc = toD3DCompareOp(m_depthOp);
@@ -4291,17 +4515,16 @@ bool QD3D11GraphicsPipeline::create()
}
hr = rhiD->dev->CreateDepthStencilState(&dsDesc, &dsState);
if (FAILED(hr)) {
- qWarning("Failed to create depth-stencil state: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create depth-stencil state: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
- D3D11_BLEND_DESC blendDesc;
- memset(&blendDesc, 0, sizeof(blendDesc));
+ D3D11_BLEND_DESC 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));
+ D3D11_RENDER_TARGET_BLEND_DESC blend = {};
blend.BlendEnable = b.enable;
blend.SrcBlend = toD3DBlendFactor(b.srcColor, true);
blend.DestBlend = toD3DBlendFactor(b.dstColor, true);
@@ -4313,19 +4536,19 @@ bool QD3D11GraphicsPipeline::create()
blendDesc.RenderTarget[i] = blend;
}
if (m_targetBlends.isEmpty()) {
- D3D11_RENDER_TARGET_BLEND_DESC blend;
- memset(&blend, 0, sizeof(blend));
+ D3D11_RENDER_TARGET_BLEND_DESC 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)));
+ qWarning("Failed to create blend state: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
QByteArray vsByteCode;
- for (const QRhiShaderStage &shaderStage : qAsConst(m_shaderStages)) {
+ for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
auto cacheIt = rhiD->m_shaderCache.constFind(shaderStage);
if (cacheIt != rhiD->m_shaderCache.constEnd()) {
switch (shaderStage.type()) {
@@ -4335,6 +4558,21 @@ bool QD3D11GraphicsPipeline::create()
vsByteCode = cacheIt->bytecode;
vs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
break;
+ case QRhiShaderStage::TessellationControl:
+ hs.shader = static_cast<ID3D11HullShader *>(cacheIt->s);
+ hs.shader->AddRef();
+ hs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
+ break;
+ case QRhiShaderStage::TessellationEvaluation:
+ ds.shader = static_cast<ID3D11DomainShader *>(cacheIt->s);
+ ds.shader->AddRef();
+ ds.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
+ break;
+ case QRhiShaderStage::Geometry:
+ gs.shader = static_cast<ID3D11GeometryShader *>(cacheIt->s);
+ gs.shader->AddRef();
+ gs.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
+ break;
case QRhiShaderStage::Fragment:
fs.shader = static_cast<ID3D11PixelShader *>(cacheIt->s);
fs.shader->AddRef();
@@ -4366,7 +4604,8 @@ bool QD3D11GraphicsPipeline::create()
case QRhiShaderStage::Vertex:
hr = rhiD->dev->CreateVertexShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &vs.shader);
if (FAILED(hr)) {
- qWarning("Failed to create vertex shader: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create vertex shader: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
vsByteCode = bytecode;
@@ -4374,10 +4613,44 @@ bool QD3D11GraphicsPipeline::create()
rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(vs.shader, bytecode, vs.nativeResourceBindingMap));
vs.shader->AddRef();
break;
+ case QRhiShaderStage::TessellationControl:
+ hr = rhiD->dev->CreateHullShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &hs.shader);
+ if (FAILED(hr)) {
+ qWarning("Failed to create hull shader: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ hs.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
+ rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(hs.shader, bytecode, hs.nativeResourceBindingMap));
+ hs.shader->AddRef();
+ break;
+ case QRhiShaderStage::TessellationEvaluation:
+ hr = rhiD->dev->CreateDomainShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &ds.shader);
+ if (FAILED(hr)) {
+ qWarning("Failed to create domain shader: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ ds.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
+ rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(ds.shader, bytecode, ds.nativeResourceBindingMap));
+ ds.shader->AddRef();
+ break;
+ case QRhiShaderStage::Geometry:
+ hr = rhiD->dev->CreateGeometryShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &gs.shader);
+ if (FAILED(hr)) {
+ qWarning("Failed to create geometry shader: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ gs.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
+ rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(gs.shader, bytecode, gs.nativeResourceBindingMap));
+ gs.shader->AddRef();
+ break;
case QRhiShaderStage::Fragment:
hr = rhiD->dev->CreatePixelShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &fs.shader);
if (FAILED(hr)) {
- qWarning("Failed to create pixel shader: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create pixel shader: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
fs.nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
@@ -4390,7 +4663,7 @@ bool QD3D11GraphicsPipeline::create()
}
}
- d3dTopology = toD3DTopology(m_topology);
+ d3dTopology = toD3DTopology(m_topology, m_patchControlPointCount);
if (!vsByteCode.isEmpty()) {
QByteArrayList matrixSliceSemantics;
@@ -4398,8 +4671,7 @@ bool QD3D11GraphicsPipeline::create()
for (auto it = m_vertexInputLayout.cbeginAttributes(), itEnd = m_vertexInputLayout.cendAttributes();
it != itEnd; ++it)
{
- D3D11_INPUT_ELEMENT_DESC desc;
- memset(&desc, 0, sizeof(desc));
+ D3D11_INPUT_ELEMENT_DESC desc = {};
// The output from SPIRV-Cross uses TEXCOORD<location> as the
// semantic, except for matrices that are unrolled into consecutive
// vec2/3/4s attributes and need TEXCOORD<location>_ as
@@ -4422,7 +4694,7 @@ bool QD3D11GraphicsPipeline::create()
const QRhiVertexInputBinding *inputBinding = m_vertexInputLayout.bindingAt(it->binding());
if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance) {
desc.InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA;
- desc.InstanceDataStepRate = UINT(inputBinding->instanceStepRate());
+ desc.InstanceDataStepRate = inputBinding->instanceStepRate();
} else {
desc.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
}
@@ -4432,12 +4704,14 @@ bool QD3D11GraphicsPipeline::create()
hr = rhiD->dev->CreateInputLayout(inputDescs.constData(), UINT(inputDescs.count()),
vsByteCode, SIZE_T(vsByteCode.size()), &inputLayout);
if (FAILED(hr)) {
- qWarning("Failed to create input layout: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create input layout: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
} // else leave inputLayout set to nullptr; that's valid and it avoids a debug layer warning about an input layout with 0 elements
}
+ rhiD->pipelineCreationEnd();
generation += 1;
rhiD->registerResource(this);
return true;
@@ -4473,6 +4747,7 @@ bool QD3D11ComputePipeline::create()
destroy();
QRHI_RES_RHI(QRhiD3D11);
+ rhiD->pipelineCreationStart();
auto cacheIt = rhiD->m_shaderCache.constFind(m_shaderStage);
if (cacheIt != rhiD->m_shaderCache.constEnd()) {
@@ -4494,7 +4769,8 @@ bool QD3D11ComputePipeline::create()
HRESULT hr = rhiD->dev->CreateComputeShader(bytecode.constData(), SIZE_T(bytecode.size()), nullptr, &cs.shader);
if (FAILED(hr)) {
- qWarning("Failed to create compute shader: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create compute shader: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
@@ -4508,6 +4784,7 @@ bool QD3D11ComputePipeline::create()
cs.shader->AddRef();
+ rhiD->pipelineCreationEnd();
generation += 1;
rhiD->registerResource(this);
return true;
@@ -4529,20 +4806,93 @@ void QD3D11CommandBuffer::destroy()
// nothing to do here
}
+bool QD3D11SwapChainTimestamps::prepare(QRhiD3D11 *rhiD)
+{
+ // Creates the query objects if not yet done, but otherwise calling this
+ // function is expected to be a no-op.
+
+ D3D11_QUERY_DESC queryDesc = {};
+ for (int i = 0; i < TIMESTAMP_PAIRS; ++i) {
+ if (!disjointQuery[i]) {
+ queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;
+ HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, &disjointQuery[i]);
+ if (FAILED(hr)) {
+ qWarning("Failed to create timestamp disjoint query: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ }
+ queryDesc.Query = D3D11_QUERY_TIMESTAMP;
+ for (int j = 0; j < 2; ++j) {
+ const int idx = 2 * i + j;
+ if (!query[idx]) {
+ HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, &query[idx]);
+ if (FAILED(hr)) {
+ qWarning("Failed to create timestamp query: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void QD3D11SwapChainTimestamps::destroy()
+{
+ for (int i = 0; i < TIMESTAMP_PAIRS; ++i) {
+ active[i] = false;
+ if (disjointQuery[i]) {
+ disjointQuery[i]->Release();
+ disjointQuery[i] = nullptr;
+ }
+ for (int j = 0; j < 2; ++j) {
+ const int idx = TIMESTAMP_PAIRS * i + j;
+ if (query[idx]) {
+ query[idx]->Release();
+ query[idx] = nullptr;
+ }
+ }
+ }
+}
+
+bool QD3D11SwapChainTimestamps::tryQueryTimestamps(int pairIndex, ID3D11DeviceContext *context, double *elapsedSec)
+{
+ bool result = false;
+ if (!active[pairIndex])
+ return result;
+
+ ID3D11Query *tsDisjoint = disjointQuery[pairIndex];
+ ID3D11Query *tsStart = query[pairIndex * 2];
+ ID3D11Query *tsEnd = query[pairIndex * 2 + 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;
+ 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;
+ *elapsedSec = elapsedMs / 1000.0;
+ result = true;
+ }
+ active[pairIndex] = false;
+ } // else leave active set, will retry in a subsequent beginFrame
+
+ return result;
+}
+
QD3D11SwapChain::QD3D11SwapChain(QRhiImplementation *rhi)
- : QRhiSwapChain(rhi),
- rt(rhi, this),
- cb(rhi)
+ : QRhiSwapChain(rhi), rt(rhi, this), rtRight(rhi, this), cb(rhi)
{
backBufferTex = nullptr;
backBufferRtv = nullptr;
for (int i = 0; i < BUFFER_COUNT; ++i) {
msaaTex[i] = nullptr;
msaaRtv[i] = nullptr;
- timestampActive[i] = false;
- timestampDisjointQuery[i] = nullptr;
- timestampQuery[2 * i] = nullptr;
- timestampQuery[2 * i + 1] = nullptr;
}
}
@@ -4557,6 +4907,10 @@ void QD3D11SwapChain::releaseBuffers()
backBufferRtv->Release();
backBufferRtv = nullptr;
}
+ if (backBufferRtvRight) {
+ backBufferRtvRight->Release();
+ backBufferRtvRight = nullptr;
+ }
if (backBufferTex) {
backBufferTex->Release();
backBufferTex = nullptr;
@@ -4580,26 +4934,28 @@ void QD3D11SwapChain::destroy()
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;
- }
- }
- }
+ timestamps.destroy();
swapChain->Release();
swapChain = nullptr;
+ if (dcompVisual) {
+ dcompVisual->Release();
+ dcompVisual = nullptr;
+ }
+
+ if (dcompTarget) {
+ dcompTarget->Release();
+ dcompTarget = nullptr;
+ }
+
QRHI_RES_RHI(QRhiD3D11);
- if (rhiD)
+ if (rhiD) {
rhiD->unregisterResource(this);
+ // See Deferred Destruction Issues with Flip Presentation Swap Chains in
+ // https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-flush
+ rhiD->context->Flush();
+ }
}
QRhiCommandBuffer *QD3D11SwapChain::currentFrameCommandBuffer()
@@ -4612,48 +4968,15 @@ QRhiRenderTarget *QD3D11SwapChain::currentFrameRenderTarget()
return &rt;
}
-QSize QD3D11SwapChain::surfacePixelSize()
+QRhiRenderTarget *QD3D11SwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer)
{
- Q_ASSERT(m_window);
- return m_window->size() * m_window->devicePixelRatio();
+ return targetBuffer == StereoTargetBuffer::LeftBuffer? &rt: &rtRight;
}
-static bool output6ForWindow(QWindow *w, IDXGIAdapter1 *adapter, IDXGIOutput6 **result)
-{
- bool ok = false;
- QRect wr = w->geometry();
- wr = QRect(wr.topLeft() * w->devicePixelRatio(), wr.size() * w->devicePixelRatio());
- const QPoint center = wr.center();
- IDXGIOutput *currentOutput = nullptr;
- IDXGIOutput *output = nullptr;
- for (UINT i = 0; adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; ++i) {
- DXGI_OUTPUT_DESC desc;
- output->GetDesc(&desc);
- const RECT r = desc.DesktopCoordinates;
- const QRect dr(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1));
- if (dr.contains(center)) {
- currentOutput = output;
- break;
- } else {
- output->Release();
- }
- }
- if (currentOutput) {
- ok = SUCCEEDED(currentOutput->QueryInterface(__uuidof(IDXGIOutput6), reinterpret_cast<void **>(result)));
- currentOutput->Release();
- }
- return ok;
-}
-
-static bool outputDesc1ForWindow(QWindow *w, IDXGIAdapter1 *adapter, DXGI_OUTPUT_DESC1 *result)
+QSize QD3D11SwapChain::surfacePixelSize()
{
- bool ok = false;
- IDXGIOutput6 *out6 = nullptr;
- if (output6ForWindow(w, adapter, &out6)) {
- ok = SUCCEEDED(out6->GetDesc1(result));
- out6->Release();
- }
- return ok;
+ Q_ASSERT(m_window);
+ return m_window->size() * m_window->devicePixelRatio();
}
bool QD3D11SwapChain::isFormatSupported(Format f)
@@ -4668,8 +4991,10 @@ bool QD3D11SwapChain::isFormatSupported(Format f)
QRHI_RES_RHI(QRhiD3D11);
DXGI_OUTPUT_DESC1 desc1;
- if (outputDesc1ForWindow(m_window, rhiD->activeAdapter, &desc1))
- return desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
+ if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &desc1)) {
+ if (desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020)
+ return f == QRhiSwapChain::HDRExtendedSrgbLinear || f == QRhiSwapChain::HDR10;
+ }
return false;
}
@@ -4677,14 +5002,16 @@ bool QD3D11SwapChain::isFormatSupported(Format f)
QRhiSwapChainHdrInfo QD3D11SwapChain::hdrInfo()
{
QRhiSwapChainHdrInfo info = QRhiSwapChain::hdrInfo();
- if (m_format != QRhiSwapChain::SDR && m_window) {
+ // Must use m_window, not window, given this may be called before createOrResize().
+ if (m_window) {
QRHI_RES_RHI(QRhiD3D11);
DXGI_OUTPUT_DESC1 hdrOutputDesc;
- if (outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc)) {
- info.isHardCodedDefaults = false;
+ if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc)) {
info.limitsType = QRhiSwapChainHdrInfo::LuminanceInNits;
info.limits.luminanceInNits.minLuminance = hdrOutputDesc.MinLuminance;
info.limits.luminanceInNits.maxLuminance = hdrOutputDesc.MaxLuminance;
+ info.luminanceBehavior = QRhiSwapChainHdrInfo::SceneReferred; // 1.0 = 80 nits
+ info.sdrWhiteLevel = QRhiD3D::sdrWhiteLevelInNits(hdrOutputDesc);
}
}
return info;
@@ -4692,14 +5019,16 @@ QRhiSwapChainHdrInfo QD3D11SwapChain::hdrInfo()
QRhiRenderPassDescriptor *QD3D11SwapChain::newCompatibleRenderPassDescriptor()
{
- return new QD3D11RenderPassDescriptor(m_rhi);
+ QD3D11RenderPassDescriptor *rpD = new QD3D11RenderPassDescriptor(m_rhi);
+ QRHI_RES_RHI(QRhiD3D11);
+ rhiD->registerResource(rpD, false);
+ return rpD;
}
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));
+ D3D11_TEXTURE2D_DESC desc = {};
desc.Width = UINT(size.width());
desc.Height = UINT(size.height());
desc.MipLevels = 1;
@@ -4712,17 +5041,18 @@ bool QD3D11SwapChain::newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI
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)));
+ qWarning("Failed to create color buffer texture: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
- D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
- memset(&rtvDesc, 0, sizeof(rtvDesc));
+ D3D11_RENDER_TARGET_VIEW_DESC 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)));
+ qWarning("Failed to create color buffer rtv: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
(*tex)->Release();
*tex = nullptr;
return false;
@@ -4731,6 +5061,16 @@ bool QD3D11SwapChain::newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI
return true;
}
+bool QRhiD3D11::ensureDirectCompositionDevice()
+{
+ if (dcompDevice)
+ return true;
+
+ qCDebug(QRHI_LOG_INFO, "Creating Direct Composition device (needed for semi-transparent windows)");
+ dcompDevice = QRhiD3D::createDirectCompositionDevice();
+ return dcompDevice ? true : false;
+}
+
static const DXGI_FORMAT DEFAULT_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM;
static const DXGI_FORMAT DEFAULT_SRGB_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
@@ -4741,6 +5081,7 @@ bool QD3D11SwapChain::createOrResize()
// resize the buffers then.
const bool needsRegistration = !window || window != m_window;
+ const bool stereo = m_window->format().stereo();
// except if the window actually changes
if (window && window != m_window)
@@ -4753,16 +5094,29 @@ bool QD3D11SwapChain::createOrResize()
if (pixelSize.isEmpty())
return false;
+ HWND hwnd = reinterpret_cast<HWND>(window->winId());
+ HRESULT hr;
+
QRHI_RES_RHI(QRhiD3D11);
- bool useFlipModel = rhiD->supportsFlipSwapchain;
- // Take a shortcut for alpha: whatever the platform plugin does to enable
- // transparency for our QWindow will be sufficient on the legacy (DISCARD)
- // path. For FLIP_* we'd need to use DirectComposition (create a
- // IDCompositionDevice/Target/Visual), avoid that for now. (this though
- // means HDR and semi-transparent windows cannot be combined)
if (m_flags.testFlag(SurfaceHasPreMulAlpha) || m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
- useFlipModel = false;
+ if (!rhiD->useLegacySwapchainModel && rhiD->ensureDirectCompositionDevice()) {
+ if (!dcompTarget) {
+ hr = rhiD->dcompDevice->CreateTargetForHwnd(hwnd, false, &dcompTarget);
+ if (FAILED(hr)) {
+ qWarning("Failed to create Direct Compsition target for the window: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ }
+ }
+ if (dcompTarget && !dcompVisual) {
+ hr = rhiD->dcompDevice->CreateVisual(&dcompVisual);
+ if (FAILED(hr)) {
+ qWarning("Failed to create DirectComposition visual: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ }
+ }
+ }
+ // simple consistency check
if (window->requestedFormat().alphaBufferSize() <= 0)
qWarning("Swapchain says surface has alpha but the window has no alphaBufferSize set. "
"This may lead to problems.");
@@ -4775,145 +5129,143 @@ bool QD3D11SwapChain::createOrResize()
// ALLOW_TEARING, and ALLOW_TEARING is not compatible with it at all so the
// flag must not be set then. Whereas for flip we should use it, if
// supported, to get better results for 'unthrottled' presentation.
- if (swapInterval == 0 && useFlipModel && rhiD->supportsAllowTearing)
+ if (swapInterval == 0 && rhiD->supportsAllowTearing)
swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
if (!swapChain) {
- HWND hwnd = reinterpret_cast<HWND>(window->winId());
- sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
+ sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount);
colorFormat = DEFAULT_FORMAT;
srgbAdjustedColorFormat = m_flags.testFlag(sRGB) ? DEFAULT_SRGB_FORMAT : DEFAULT_FORMAT;
- HRESULT hr;
- if (useFlipModel) {
- DXGI_COLOR_SPACE_TYPE hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // SDR
- DXGI_OUTPUT_DESC1 hdrOutputDesc;
- if (outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc) && m_format != SDR) {
- // https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range
- if (hdrOutputDesc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
- switch (m_format) {
- case HDRExtendedSrgbLinear:
- colorFormat = DXGI_FORMAT_R16G16B16A16_FLOAT;
- hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
- srgbAdjustedColorFormat = colorFormat;
- break;
- case HDR10:
- colorFormat = DXGI_FORMAT_R10G10B10A2_UNORM;
- hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
- srgbAdjustedColorFormat = colorFormat;
- break;
- default:
- break;
- }
- } else {
- // This happens also when Use HDR is set to Off in the Windows
- // Display settings. Show a helpful warning, but continue with the
- // default non-HDR format.
- qWarning("The output associated with the window is not HDR capable "
- "(or Use HDR is Off in the Display Settings), ignoring HDR format request");
+ DXGI_COLOR_SPACE_TYPE hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // SDR
+ DXGI_OUTPUT_DESC1 hdrOutputDesc;
+ if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc) && m_format != SDR) {
+ // https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range
+ if (hdrOutputDesc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
+ switch (m_format) {
+ case HDRExtendedSrgbLinear:
+ colorFormat = DXGI_FORMAT_R16G16B16A16_FLOAT;
+ hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
+ srgbAdjustedColorFormat = colorFormat;
+ break;
+ case HDR10:
+ colorFormat = DXGI_FORMAT_R10G10B10A2_UNORM;
+ hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
+ srgbAdjustedColorFormat = colorFormat;
+ break;
+ default:
+ break;
}
- }
-
- // We use a FLIP model swapchain 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.
-
- DXGI_SWAP_CHAIN_DESC1 desc;
- memset(&desc, 0, sizeof(desc));
- desc.Width = UINT(pixelSize.width());
- desc.Height = UINT(pixelSize.height());
- desc.Format = colorFormat;
- desc.SampleDesc.Count = 1;
- desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
- desc.BufferCount = BUFFER_COUNT;
-
- // Normally we'd want FLIP_DISCARD, but that comes with the default
- // SCALING_STRETCH, as SCALING_NONE is documented to be only
- // available for FLIP_SEQUENTIAl. The problem with stretch is that
- // Qt Quick and similar apps typically running in resizable windows
- // will not like how that looks in practice: the content will
- // appear to be "jumping" around during a window resize. So choose
- // sequential/none by default.
- if (rhiD->forceFlipDiscard) {
- desc.Scaling = DXGI_SCALING_STRETCH;
- desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
} else {
- desc.Scaling = DXGI_SCALING_NONE;
- desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+ // This happens also when Use HDR is set to Off in the Windows
+ // Display settings. Show a helpful warning, but continue with the
+ // default non-HDR format.
+ qWarning("The output associated with the window is not HDR capable "
+ "(or Use HDR is Off in the Display Settings), ignoring HDR format request");
}
+ }
- // Do not bother with AlphaMode, if won't work unless we go through
- // DirectComposition. Instead, we just take the other (DISCARD)
- // path for now when alpha is requested.
- desc.Flags = swapChainFlags;
+ // We use a FLIP model swapchain 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.
+
+ DXGI_SWAP_CHAIN_DESC1 desc = {};
+ desc.Width = UINT(pixelSize.width());
+ desc.Height = UINT(pixelSize.height());
+ desc.Format = colorFormat;
+ desc.SampleDesc.Count = 1;
+ desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ desc.BufferCount = BUFFER_COUNT;
+ desc.Flags = swapChainFlags;
+ desc.Scaling = rhiD->useLegacySwapchainModel ? DXGI_SCALING_STRETCH : DXGI_SCALING_NONE;
+ desc.SwapEffect = rhiD->useLegacySwapchainModel ? DXGI_SWAP_EFFECT_DISCARD : DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ desc.Stereo = stereo;
+
+ if (dcompVisual) {
+ // With DirectComposition setting AlphaMode to STRAIGHT fails the
+ // swapchain creation, whereas the result seems to be identical
+ // with any of the other values, including IGNORE. (?)
+ desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
+
+ // DirectComposition has its own limitations, cannot use
+ // SCALING_NONE. So with semi-transparency requested we are forced
+ // to SCALING_STRETCH.
+ desc.Scaling = DXGI_SCALING_STRETCH;
+ }
+
+ IDXGIFactory2 *fac = static_cast<IDXGIFactory2 *>(rhiD->dxgiFactory);
+ IDXGISwapChain1 *sc1;
- IDXGIFactory2 *fac = static_cast<IDXGIFactory2 *>(rhiD->dxgiFactory);
- IDXGISwapChain1 *sc1;
+ if (dcompVisual)
+ hr = fac->CreateSwapChainForComposition(rhiD->dev, &desc, nullptr, &sc1);
+ else
hr = fac->CreateSwapChainForHwnd(rhiD->dev, hwnd, &desc, nullptr, nullptr, &sc1);
- // If failed and we tried a HDR format, then try with SDR. This
- // matches other backends, such as Vulkan where if the format is
- // not supported, the default one is used instead.
- if (FAILED(hr) && m_format != SDR) {
- colorFormat = DEFAULT_FORMAT;
- desc.Format = DEFAULT_FORMAT;
+ // If failed and we tried a HDR format, then try with SDR. This
+ // matches other backends, such as Vulkan where if the format is
+ // not supported, the default one is used instead.
+ if (FAILED(hr) && m_format != SDR) {
+ colorFormat = DEFAULT_FORMAT;
+ desc.Format = DEFAULT_FORMAT;
+ if (dcompVisual)
+ hr = fac->CreateSwapChainForComposition(rhiD->dev, &desc, nullptr, &sc1);
+ else
hr = fac->CreateSwapChainForHwnd(rhiD->dev, hwnd, &desc, nullptr, nullptr, &sc1);
- }
+ }
- if (SUCCEEDED(hr)) {
- swapChain = sc1;
- if (m_format != SDR) {
- IDXGISwapChain3 *sc3 = nullptr;
- if (SUCCEEDED(sc1->QueryInterface(__uuidof(IDXGISwapChain3), reinterpret_cast<void **>(&sc3)))) {
- hr = sc3->SetColorSpace1(hdrColorSpace);
- if (FAILED(hr))
- qWarning("Failed to set color space on swapchain: %s", qPrintable(comErrorMessage(hr)));
- sc3->Release();
- } else {
- qWarning("IDXGISwapChain3 not available, HDR swapchain will not work as expected");
+ if (SUCCEEDED(hr)) {
+ swapChain = sc1;
+ if (m_format != SDR) {
+ IDXGISwapChain3 *sc3 = nullptr;
+ if (SUCCEEDED(sc1->QueryInterface(__uuidof(IDXGISwapChain3), reinterpret_cast<void **>(&sc3)))) {
+ hr = sc3->SetColorSpace1(hdrColorSpace);
+ if (FAILED(hr))
+ qWarning("Failed to set color space on swapchain: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ sc3->Release();
+ } else {
+ qWarning("IDXGISwapChain3 not available, HDR swapchain will not work as expected");
+ }
+ }
+ if (dcompVisual) {
+ hr = dcompVisual->SetContent(sc1);
+ if (SUCCEEDED(hr)) {
+ hr = dcompTarget->SetRoot(dcompVisual);
+ if (FAILED(hr)) {
+ qWarning("Failed to associate Direct Composition visual with the target: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
}
+ } else {
+ qWarning("Failed to set content for Direct Composition visual: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
}
+ } else {
+ // disable Alt+Enter; not relevant when using DirectComposition
+ rhiD->dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
}
- } else {
- // Fallback: use DISCARD mode. Regardless, keep on using our manual
- // resolve for symmetry with the FLIP_* code path when MSAA is
- // requested. This has no HDR support.
-
- DXGI_SWAP_CHAIN_DESC desc;
- memset(&desc, 0, sizeof(desc));
- desc.BufferDesc.Width = UINT(pixelSize.width());
- desc.BufferDesc.Height = UINT(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 = 1;
- desc.OutputWindow = hwnd;
- desc.Windowed = true;
- desc.SwapEffect = DXGI_SWAP_EFFECT_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)));
+ qWarning("Failed to create D3D11 swapchain: %s"
+ " (Width=%u Height=%u Format=%u SampleCount=%u BufferCount=%u Scaling=%u SwapEffect=%u Stereo=%u)",
+ qPrintable(QSystemError::windowsComString(hr)),
+ desc.Width, desc.Height, UINT(desc.Format), desc.SampleDesc.Count,
+ desc.BufferCount, UINT(desc.Scaling), UINT(desc.SwapEffect), UINT(desc.Stereo));
return false;
}
- rhiD->dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
} else {
releaseBuffers();
- const UINT count = useFlipModel ? BUFFER_COUNT : 1;
- HRESULT hr = swapChain->ResizeBuffers(count, UINT(pixelSize.width()), UINT(pixelSize.height()),
- colorFormat, swapChainFlags);
+ // flip model -> buffer count is the real buffer count, not 1 like with the legacy modes
+ hr = swapChain->ResizeBuffers(UINT(BUFFER_COUNT), UINT(pixelSize.width()), UINT(pixelSize.height()),
+ colorFormat, swapChainFlags);
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
qWarning("Device loss detected in ResizeBuffers()");
rhiD->deviceLost = true;
return false;
} else if (FAILED(hr)) {
- qWarning("Failed to resize D3D11 swapchain: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to resize D3D11 swapchain: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
}
@@ -4932,21 +5284,35 @@ bool QD3D11SwapChain::createOrResize()
// swapchain."
// So just query index 0 once (per resize) and be done with it.
- HRESULT hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&backBufferTex));
+ hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&backBufferTex));
if (FAILED(hr)) {
- qWarning("Failed to query swapchain backbuffer: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to query swapchain backbuffer: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
- D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
- memset(&rtvDesc, 0, sizeof(rtvDesc));
+ D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
rtvDesc.Format = srgbAdjustedColorFormat;
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
hr = rhiD->dev->CreateRenderTargetView(backBufferTex, &rtvDesc, &backBufferRtv);
if (FAILED(hr)) {
- qWarning("Failed to create rtv for swapchain backbuffer: %s", qPrintable(comErrorMessage(hr)));
+ qWarning("Failed to create rtv for swapchain backbuffer: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
return false;
}
+ if (stereo) {
+ // Create a second render target view for the right eye
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+ rtvDesc.Texture2DArray.FirstArraySlice = 1;
+ rtvDesc.Texture2DArray.ArraySize = 1;
+ hr = rhiD->dev->CreateRenderTargetView(backBufferTex, &rtvDesc, &backBufferRtvRight);
+ if (FAILED(hr)) {
+ qWarning("Failed to create rtv for swapchain backbuffer (right eye): %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ }
+
// Try to reduce stalls by having a dedicated MSAA texture per swapchain buffer.
for (int i = 0; i < BUFFER_COUNT; ++i) {
if (sampleDesc.Count > 1) {
@@ -4985,30 +5351,20 @@ bool QD3D11SwapChain::createOrResize()
rtD->d.colorAttCount = 1;
rtD->d.dsAttCount = m_depthStencil ? 1 : 0;
- if (rhiD->hasGpuFrameTimeCallback()) {
- 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;
- }
- }
- }
- }
+ if (stereo) {
+ rtD = QRHI_RES(QD3D11SwapChainRenderTarget, &rtRight);
+ rtD->d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc);
+ rtD->d.pixelSize = pixelSize;
+ rtD->d.dpr = float(window->devicePixelRatio());
+ rtD->d.sampleCount = int(sampleDesc.Count);
+ rtD->d.colorAttCount = 1;
+ rtD->d.dsAttCount = m_depthStencil ? 1 : 0;
+ rtD->d.rtv[0] = backBufferRtvRight;
+ rtD->d.dsv = ds ? ds->dsv : nullptr;
+ }
+
+ if (rhiD->rhiFlags.testFlag(QRhi::EnableTimestamps)) {
+ timestamps.prepare(rhiD);
// timestamp queries are optional so we can go on even if they failed
}
@@ -5018,34 +5374,4 @@ bool QD3D11SwapChain::createOrResize()
return true;
}
-void QRhiD3D11::DeviceCurse::initResources()
-{
- framesLeft = framesToActivate;
-
- HRESULT hr = q->dev->CreateComputeShader(g_killDeviceByTimingOut, sizeof(g_killDeviceByTimingOut), nullptr, &cs);
- if (FAILED(hr)) {
- qWarning("Failed to create compute shader: %s", qPrintable(comErrorMessage(hr)));
- return;
- }
-}
-
-void QRhiD3D11::DeviceCurse::releaseResources()
-{
- if (cs) {
- cs->Release();
- cs = nullptr;
- }
-}
-
-void QRhiD3D11::DeviceCurse::activate()
-{
- if (!cs)
- return;
-
- qDebug("Activating Curse. Goodbye Cruel World.");
-
- q->context->CSSetShader(cs, nullptr, 0);
- q->context->Dispatch(256, 1, 1);
-}
-
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhid3d11_p.h b/src/gui/rhi/qrhid3d11_p.h
index 31aa58a68a..7644748407 100644
--- a/src/gui/rhi/qrhid3d11_p.h
+++ b/src/gui/rhi/qrhid3d11_p.h
@@ -1,8 +1,8 @@
-// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QRHID3D11_H
-#define QRHID3D11_H
+#ifndef QRHID3D11_P_H
+#define QRHID3D11_P_H
//
// W A R N I N G
@@ -15,31 +15,852 @@
// We mean it.
//
-#include <private/qrhi_p.h>
+#include "qrhi_p.h"
+#include <rhi/qshaderdescription.h>
+#include <QWindow>
-// no d3d includes here, to prevent precompiled header mess due to COM
+#include <d3d11_1.h>
+#include <dxgi1_6.h>
+#include <dcomp.h>
QT_BEGIN_NAMESPACE
-struct Q_GUI_EXPORT QRhiD3D11InitParams : public QRhiInitParams
+class QRhiD3D11;
+
+struct QD3D11Buffer : public QRhiBuffer
+{
+ QD3D11Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size);
+ ~QD3D11Buffer();
+ void destroy() override;
+ bool create() override;
+ QRhiBuffer::NativeBuffer nativeBuffer() override;
+ char *beginFullDynamicBufferUpdateForCurrentFrame() override;
+ void endFullDynamicBufferUpdateForCurrentFrame() override;
+
+ ID3D11UnorderedAccessView *unorderedAccessView(quint32 offset);
+
+ ID3D11Buffer *buffer = nullptr;
+ char *dynBuf = nullptr;
+ bool hasPendingDynamicUpdates = false;
+ QHash<quint32, ID3D11UnorderedAccessView *> uavs;
+ uint generation = 0;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11RenderBuffer : public QRhiRenderBuffer
+{
+ QD3D11RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags,
+ QRhiTexture::Format backingFormatHint);
+ ~QD3D11RenderBuffer();
+ void destroy() override;
+ bool create() override;
+ QRhiTexture::Format backingFormat() const override;
+
+ ID3D11Texture2D *tex = nullptr;
+ ID3D11DepthStencilView *dsv = nullptr;
+ ID3D11RenderTargetView *rtv = nullptr;
+ DXGI_FORMAT dxgiFormat;
+ DXGI_SAMPLE_DESC sampleDesc;
+ uint generation = 0;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11Texture : public QRhiTexture
+{
+ QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
+ int arraySize, int sampleCount, Flags flags);
+ ~QD3D11Texture();
+ void destroy() override;
+ bool create() override;
+ bool createFrom(NativeTexture src) override;
+ NativeTexture nativeTexture() override;
+
+ bool prepareCreate(QSize *adjustedSize = nullptr);
+ bool finishCreate();
+ ID3D11UnorderedAccessView *unorderedAccessViewForLevel(int level);
+ ID3D11Resource *textureResource() const
+ {
+ if (tex)
+ return tex;
+ else if (tex1D)
+ return tex1D;
+ return tex3D;
+ }
+
+ ID3D11Texture2D *tex = nullptr;
+ ID3D11Texture3D *tex3D = nullptr;
+ ID3D11Texture1D *tex1D = nullptr;
+ bool owns = true;
+ ID3D11ShaderResourceView *srv = nullptr;
+ DXGI_FORMAT dxgiFormat;
+ uint mipLevelCount = 0;
+ DXGI_SAMPLE_DESC sampleDesc;
+ ID3D11UnorderedAccessView *perLevelViews[QRhi::MAX_MIP_LEVELS];
+ uint generation = 0;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11Sampler : public QRhiSampler
+{
+ QD3D11Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v, AddressMode w);
+ ~QD3D11Sampler();
+ void destroy() override;
+ bool create() override;
+
+ ID3D11SamplerState *samplerState = nullptr;
+ uint generation = 0;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11RenderPassDescriptor : public QRhiRenderPassDescriptor
+{
+ QD3D11RenderPassDescriptor(QRhiImplementation *rhi);
+ ~QD3D11RenderPassDescriptor();
+ void destroy() override;
+ bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
+ QVector<quint32> serializedFormat() const 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;
+
+ QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList;
+};
+
+struct QD3D11SwapChainRenderTarget : public QRhiSwapChainRenderTarget
{
- bool enableDebugLayer = false;
+ QD3D11SwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain);
+ ~QD3D11SwapChainRenderTarget();
+ void destroy() override;
- int framesUntilKillingDeviceViaTdr = -1;
- bool repeatDeviceKill = false;
+ 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 destroy() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool create() 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 destroy() override;
+ bool create() override;
+ void updateResources(UpdateFlags flags) override;
+
+ bool hasDynamicOffset = false;
+ QVarLengthArray<QRhiShaderResourceBinding, 8> 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 {
+ int count;
+ struct {
+ quint64 texId;
+ uint texGeneration;
+ quint64 samplerId;
+ uint samplerGeneration;
+ } d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE];
+ };
+ struct BoundStorageImageData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundStorageBufferData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundResourceData {
+ union {
+ BoundUniformBufferData ubuf;
+ BoundSampledTextureData stex;
+ BoundStorageImageData simage;
+ BoundStorageBufferData sbuf;
+ };
+ };
+ QVarLengthArray<BoundResourceData, 8> boundResourceData;
+
+ struct StageUniformBufferBatches {
+ bool present = false;
+ QRhiBatchedBindings<ID3D11Buffer *> ubufs;
+ QRhiBatchedBindings<UINT> ubuforigbindings;
+ QRhiBatchedBindings<UINT> ubufoffsets;
+ QRhiBatchedBindings<UINT> ubufsizes;
+ void finish() {
+ present = ubufs.finish();
+ ubuforigbindings.finish();
+ ubufoffsets.finish();
+ ubufsizes.finish();
+ }
+ void clear() {
+ ubufs.clear();
+ ubuforigbindings.clear();
+ ubufoffsets.clear();
+ ubufsizes.clear();
+ }
+ };
+
+ struct StageSamplerBatches {
+ bool present = false;
+ QRhiBatchedBindings<ID3D11SamplerState *> samplers;
+ QRhiBatchedBindings<ID3D11ShaderResourceView *> shaderresources;
+ void finish() {
+ present = samplers.finish();
+ shaderresources.finish();
+ }
+ void clear() {
+ samplers.clear();
+ shaderresources.clear();
+ }
+ };
+
+ struct StageUavBatches {
+ bool present = false;
+ QRhiBatchedBindings<ID3D11UnorderedAccessView *> uavs;
+ void finish() {
+ present = uavs.finish();
+ }
+ void clear() {
+ uavs.clear();
+ }
+ };
+
+ StageUniformBufferBatches vsUniformBufferBatches;
+ StageUniformBufferBatches hsUniformBufferBatches;
+ StageUniformBufferBatches dsUniformBufferBatches;
+ StageUniformBufferBatches gsUniformBufferBatches;
+ StageUniformBufferBatches fsUniformBufferBatches;
+ StageUniformBufferBatches csUniformBufferBatches;
+
+ StageSamplerBatches vsSamplerBatches;
+ StageSamplerBatches hsSamplerBatches;
+ StageSamplerBatches dsSamplerBatches;
+ StageSamplerBatches gsSamplerBatches;
+ StageSamplerBatches fsSamplerBatches;
+ StageSamplerBatches csSamplerBatches;
+
+ StageUavBatches csUavBatches;
+
+ friend class QRhiD3D11;
+};
+
+Q_DECLARE_TYPEINFO(QD3D11ShaderResourceBindings::BoundResourceData, Q_RELOCATABLE_TYPE);
+
+struct QD3D11GraphicsPipeline : public QRhiGraphicsPipeline
+{
+ QD3D11GraphicsPipeline(QRhiImplementation *rhi);
+ ~QD3D11GraphicsPipeline();
+ void destroy() override;
+ bool create() override;
+
+ ID3D11DepthStencilState *dsState = nullptr;
+ ID3D11BlendState *blendState = nullptr;
+ struct {
+ ID3D11VertexShader *shader = nullptr;
+ QShader::NativeResourceBindingMap nativeResourceBindingMap;
+ } vs;
+ struct {
+ ID3D11HullShader *shader = nullptr;
+ QShader::NativeResourceBindingMap nativeResourceBindingMap;
+ } hs;
+ struct {
+ ID3D11DomainShader *shader = nullptr;
+ QShader::NativeResourceBindingMap nativeResourceBindingMap;
+ } ds;
+ struct {
+ ID3D11GeometryShader *shader = nullptr;
+ QShader::NativeResourceBindingMap nativeResourceBindingMap;
+ } gs;
+ struct {
+ ID3D11PixelShader *shader = nullptr;
+ QShader::NativeResourceBindingMap nativeResourceBindingMap;
+ } fs;
+ ID3D11InputLayout *inputLayout = nullptr;
+ D3D11_PRIMITIVE_TOPOLOGY d3dTopology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ ID3D11RasterizerState *rastState = nullptr;
+ uint generation = 0;
+ friend class QRhiD3D11;
};
-struct Q_GUI_EXPORT QRhiD3D11NativeHandles : public QRhiNativeHandles
+struct QD3D11ComputePipeline : public QRhiComputePipeline
{
- // to import a device and a context
- void *dev = nullptr;
- void *context = nullptr;
- // alternatively, to specify the device feature level and/or the adapter to use
- int featureLevel = 0;
- quint32 adapterLuidLow = 0;
- qint32 adapterLuidHigh = 0;
+ QD3D11ComputePipeline(QRhiImplementation *rhi);
+ ~QD3D11ComputePipeline();
+ void destroy() override;
+ bool create() override;
+
+ struct {
+ ID3D11ComputeShader *shader = nullptr;
+ QShader::NativeResourceBindingMap nativeResourceBindingMap;
+ } cs;
+ uint generation = 0;
+ friend class QRhiD3D11;
};
+struct QD3D11SwapChain;
+
+struct QD3D11CommandBuffer : public QRhiCommandBuffer
+{
+ QD3D11CommandBuffer(QRhiImplementation *rhi);
+ ~QD3D11CommandBuffer();
+ void destroy() override;
+
+ // these must be kept at a reasonably low value otherwise sizeof Command explodes
+ static const int MAX_DYNAMIC_OFFSET_COUNT = 8;
+ static const int MAX_VERTEX_BUFFER_BINDING_COUNT = 8;
+
+ struct Command {
+ enum Cmd {
+ BeginFrame,
+ EndFrame,
+ 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;
+
+ // QRhi*/QD3D11* references should be kept at minimum (so no
+ // QRhiTexture/Buffer/etc. pointers).
+ union Args {
+ struct {
+ ID3D11Query *tsQuery;
+ ID3D11Query *tsDisjointQuery;
+ QD3D11RenderTargetData *swapchainData;
+ } beginFrame;
+ struct {
+ ID3D11Query *tsQuery;
+ ID3D11Query *tsDisjointQuery;
+ } endFrame;
+ 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[MAX_VERTEX_BUFFER_BINDING_COUNT];
+ UINT offsets[MAX_VERTEX_BUFFER_BINDING_COUNT];
+ UINT strides[MAX_VERTEX_BUFFER_BINDING_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_DYNAMIC_OFFSET_COUNT * 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;
+ UINT dstZ;
+ 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
+ };
+
+ QRhiBackendCommandList<Command> commands;
+ PassType recordingPass;
+ double lastGpuTime = 0;
+ 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];
+
+ QVarLengthArray<QByteArray, 4> dataRetainPool;
+ QVarLengthArray<QRhiBufferData, 4> bufferDataRetainPool;
+ QVarLengthArray<QImage, 4> 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.last().constData());
+ }
+ const uchar *retainBufferData(const QRhiBufferData &data) {
+ bufferDataRetainPool.append(data);
+ return reinterpret_cast<const uchar *>(bufferDataRetainPool.last().constData());
+ }
+ const uchar *retainImage(const QImage &image) {
+ imageRetainPool.append(image);
+ return imageRetainPool.last().constBits();
+ }
+ void resetCommands() {
+ commands.reset();
+ dataRetainPool.clear();
+ bufferDataRetainPool.clear();
+ imageRetainPool.clear();
+ }
+ void resetState() {
+ recordingPass = NoPass;
+ // do not zero lastGpuTime
+ currentTarget = nullptr;
+ resetCommands();
+ 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));
+ }
+};
+
+struct QD3D11SwapChainTimestamps
+{
+ static const int TIMESTAMP_PAIRS = 2;
+
+ bool active[TIMESTAMP_PAIRS] = {};
+ ID3D11Query *disjointQuery[TIMESTAMP_PAIRS] = {};
+ ID3D11Query *query[TIMESTAMP_PAIRS * 2] = {};
+
+ bool prepare(QRhiD3D11 *rhiD);
+ void destroy();
+ bool tryQueryTimestamps(int idx, ID3D11DeviceContext *context, double *elapsedSec);
+};
+
+struct QD3D11SwapChain : public QRhiSwapChain
+{
+ QD3D11SwapChain(QRhiImplementation *rhi);
+ ~QD3D11SwapChain();
+ void destroy() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+ QRhiRenderTarget *currentFrameRenderTarget(StereoTargetBuffer targetBuffer) override;
+
+ QSize surfacePixelSize() override;
+ bool isFormatSupported(Format f) override;
+ QRhiSwapChainHdrInfo hdrInfo() override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool createOrResize() 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;
+ QD3D11SwapChainRenderTarget rt;
+ QD3D11SwapChainRenderTarget rtRight;
+ QD3D11CommandBuffer cb;
+ DXGI_FORMAT colorFormat;
+ DXGI_FORMAT srgbAdjustedColorFormat;
+ IDXGISwapChain *swapChain = nullptr;
+ UINT swapChainFlags = 0;
+ ID3D11Texture2D *backBufferTex;
+ ID3D11RenderTargetView *backBufferRtv;
+ ID3D11RenderTargetView *backBufferRtvRight = nullptr;
+ static const int BUFFER_COUNT = 2;
+ ID3D11Texture2D *msaaTex[BUFFER_COUNT];
+ ID3D11RenderTargetView *msaaRtv[BUFFER_COUNT];
+ DXGI_SAMPLE_DESC sampleDesc;
+ int currentFrameSlot = 0;
+ int frameCount = 0;
+ QD3D11RenderBuffer *ds = nullptr;
+ UINT swapInterval = 1;
+ IDCompositionTarget *dcompTarget = nullptr;
+ IDCompositionVisual *dcompVisual = nullptr;
+ QD3D11SwapChainTimestamps timestamps;
+ int currentTimestampPairIndex = 0;
+};
+
+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,
+ quint32 size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags,
+ QRhiTexture::Format backingFormatHint) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int depth,
+ int arraySize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
+ QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u,
+ QRhiSampler::AddressMode v,
+ QRhiSampler::AddressMode w) 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, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) 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,
+ QRhiCommandBuffer::BeginPassFlags flags) 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,
+ QRhiCommandBuffer::BeginPassFlags flags) 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;
+ double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
+
+ QList<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;
+ QRhiDriverInfo driverInfo() const override;
+ QRhiStats statistics() override;
+ bool makeThreadLocalNativeContextCurrent() override;
+ void releaseCachedResources() override;
+ bool isDeviceLost() const override;
+
+ QByteArray pipelineCacheData() override;
+ void setPipelineCacheData(const QByteArray &data) override;
+
+ void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
+ int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
+ void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
+ void updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
+ const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[]);
+ void executeBufferHostWrites(QD3D11Buffer *bufD);
+ void bindShaderResources(QD3D11ShaderResourceBindings *srbD,
+ const uint *dynOfsPairs, int dynOfsPairCount,
+ bool offsetOnlyChange);
+ void resetShaderResources();
+ void executeCommandBuffer(QD3D11CommandBuffer *cbD);
+ DXGI_SAMPLE_DESC effectiveSampleDesc(int sampleCount) const;
+ void finishActiveReadbacks();
+ void reportLiveObjects(ID3D11Device *device);
+ void clearShaderCache();
+ QByteArray compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, uint flags,
+ QString *error, QShaderKey *usedShaderKey);
+ bool ensureDirectCompositionDevice();
+
+ QRhi::Flags rhiFlags;
+ bool debugLayer = false;
+ bool importedDeviceAndContext = false;
+ ID3D11Device *dev = nullptr;
+ ID3D11DeviceContext1 *context = nullptr;
+ D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL(0);
+ LUID adapterLuid = {};
+ ID3DUserDefinedAnnotation *annotations = nullptr;
+ IDXGIAdapter1 *activeAdapter = nullptr;
+ IDXGIFactory1 *dxgiFactory = nullptr;
+ IDCompositionDevice *dcompDevice = nullptr;
+ bool supportsAllowTearing = false;
+ bool useLegacySwapchainModel = false;
+ bool deviceLost = false;
+ QRhiD3D11NativeHandles nativeHandlesStruct;
+ QRhiDriverInfo driverInfoStruct;
+
+ struct {
+ int vsHighestActiveVertexBufferBinding = -1;
+ bool vsHasIndexBufferBound = false;
+ int vsHighestActiveSrvBinding = -1;
+ int hsHighestActiveSrvBinding = -1;
+ int dsHighestActiveSrvBinding = -1;
+ int gsHighestActiveSrvBinding = -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;
+ ID3D11Query *tsQueries[2] = {};
+ ID3D11Query *tsDisjointQuery = nullptr;
+ } ofr;
+
+ struct TextureReadback {
+ QRhiReadbackDescription desc;
+ QRhiReadbackResult *result;
+ ID3D11Texture2D *stagingTex;
+ quint32 byteSize;
+ quint32 bpl;
+ QSize pixelSize;
+ QRhiTexture::Format format;
+ };
+ QVarLengthArray<TextureReadback, 2> activeTextureReadbacks;
+ struct BufferReadback {
+ QRhiReadbackResult *result;
+ quint32 byteSize;
+ ID3D11Buffer *stagingBuf;
+ };
+ QVarLengthArray<BufferReadback, 2> activeBufferReadbacks;
+
+ struct Shader {
+ Shader() = default;
+ Shader(IUnknown *s, const QByteArray &bytecode, const QShader::NativeResourceBindingMap &rbm)
+ : s(s), bytecode(bytecode), nativeResourceBindingMap(rbm) { }
+ IUnknown *s;
+ QByteArray bytecode;
+ QShader::NativeResourceBindingMap nativeResourceBindingMap;
+ };
+ QHash<QRhiShaderStage, Shader> m_shaderCache;
+
+ // This is what gets exposed as the "pipeline cache", not that that concept
+ // applies anyway. Here we are just storing the DX bytecode for a shader so
+ // we can skip the HLSL->DXBC compilation when the QShader has HLSL source
+ // code and the same shader source has already been compiled before.
+ // m_shaderCache seemingly does the same, but this here does not care about
+ // the ID3D11*Shader, this is just about the bytecode and about allowing
+ // the data to be serialized to persistent storage and then reloaded in
+ // future runs of the app, or when creating another QRhi, etc.
+ struct BytecodeCacheKey {
+ QByteArray sourceHash;
+ QByteArray target;
+ QByteArray entryPoint;
+ uint compileFlags;
+ };
+ QHash<BytecodeCacheKey, QByteArray> m_bytecodeCache;
+};
+
+Q_DECLARE_TYPEINFO(QRhiD3D11::TextureReadback, Q_RELOCATABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiD3D11::BufferReadback, Q_RELOCATABLE_TYPE);
+
+inline bool operator==(const QRhiD3D11::BytecodeCacheKey &a, const QRhiD3D11::BytecodeCacheKey &b) noexcept
+{
+ return a.sourceHash == b.sourceHash
+ && a.target == b.target
+ && a.entryPoint == b.entryPoint
+ && a.compileFlags == b.compileFlags;
+}
+
+inline bool operator!=(const QRhiD3D11::BytecodeCacheKey &a, const QRhiD3D11::BytecodeCacheKey &b) noexcept
+{
+ return !(a == b);
+}
+
+inline size_t qHash(const QRhiD3D11::BytecodeCacheKey &k, size_t seed = 0) noexcept
+{
+ return qHash(k.sourceHash, seed) ^ qHash(k.target) ^ qHash(k.entryPoint) ^ k.compileFlags;
+}
+
QT_END_NAMESPACE
#endif
diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h
deleted file mode 100644
index 76a8337f16..0000000000
--- a/src/gui/rhi/qrhid3d11_p_p.h
+++ /dev/null
@@ -1,798 +0,0 @@
-// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#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_6.h>
-
-QT_BEGIN_NAMESPACE
-
-struct QD3D11Buffer : public QRhiBuffer
-{
- QD3D11Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
- ~QD3D11Buffer();
- void destroy() override;
- bool create() override;
- QRhiBuffer::NativeBuffer nativeBuffer() override;
- char *beginFullDynamicBufferUpdateForCurrentFrame() override;
- void endFullDynamicBufferUpdateForCurrentFrame() override;
-
- ID3D11UnorderedAccessView *unorderedAccessView();
-
- ID3D11Buffer *buffer = nullptr;
- char *dynBuf = nullptr;
- 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,
- QRhiTexture::Format backingFormatHint);
- ~QD3D11RenderBuffer();
- void destroy() override;
- bool create() override;
- QRhiTexture::Format backingFormat() const override;
-
- ID3D11Texture2D *tex = nullptr;
- ID3D11DepthStencilView *dsv = nullptr;
- ID3D11RenderTargetView *rtv = nullptr;
- DXGI_FORMAT dxgiFormat;
- DXGI_SAMPLE_DESC sampleDesc;
- uint generation = 0;
- friend class QRhiD3D11;
-};
-
-struct QD3D11Texture : public QRhiTexture
-{
- QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
- int arraySize, int sampleCount, Flags flags);
- ~QD3D11Texture();
- void destroy() override;
- bool create() override;
- bool createFrom(NativeTexture src) override;
- NativeTexture nativeTexture() override;
-
- bool prepareCreate(QSize *adjustedSize = nullptr);
- bool finishCreate();
- ID3D11UnorderedAccessView *unorderedAccessViewForLevel(int level);
- ID3D11Resource *textureResource() const
- {
- if (tex)
- return tex;
- return tex3D;
- }
-
- ID3D11Texture2D *tex = nullptr;
- ID3D11Texture3D *tex3D = nullptr;
- bool owns = true;
- ID3D11ShaderResourceView *srv = nullptr;
- DXGI_FORMAT dxgiFormat;
- uint mipLevelCount = 0;
- DXGI_SAMPLE_DESC sampleDesc;
- ID3D11UnorderedAccessView *perLevelViews[QRhi::MAX_MIP_LEVELS];
- uint generation = 0;
- friend class QRhiD3D11;
-};
-
-struct QD3D11Sampler : public QRhiSampler
-{
- QD3D11Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
- AddressMode u, AddressMode v, AddressMode w);
- ~QD3D11Sampler();
- void destroy() override;
- bool create() override;
-
- ID3D11SamplerState *samplerState = nullptr;
- uint generation = 0;
- friend class QRhiD3D11;
-};
-
-struct QD3D11RenderPassDescriptor : public QRhiRenderPassDescriptor
-{
- QD3D11RenderPassDescriptor(QRhiImplementation *rhi);
- ~QD3D11RenderPassDescriptor();
- void destroy() override;
- bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
- QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
- QVector<quint32> serializedFormat() const 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;
-
- QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList;
-};
-
-struct QD3D11SwapChainRenderTarget : public QRhiSwapChainRenderTarget
-{
- QD3D11SwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain);
- ~QD3D11SwapChainRenderTarget();
- void destroy() 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 destroy() override;
-
- QSize pixelSize() const override;
- float devicePixelRatio() const override;
- int sampleCount() const override;
-
- QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
- bool create() 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 destroy() override;
- bool create() override;
- void updateResources(UpdateFlags flags) override;
-
- bool hasDynamicOffset = false;
- QVarLengthArray<QRhiShaderResourceBinding, 8> 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 {
- int count;
- struct {
- quint64 texId;
- uint texGeneration;
- quint64 samplerId;
- uint samplerGeneration;
- } d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE];
- };
- struct BoundStorageImageData {
- quint64 id;
- uint generation;
- };
- struct BoundStorageBufferData {
- quint64 id;
- uint generation;
- };
- struct BoundResourceData {
- union {
- BoundUniformBufferData ubuf;
- BoundSampledTextureData stex;
- BoundStorageImageData simage;
- BoundStorageBufferData sbuf;
- };
- };
- QVarLengthArray<BoundResourceData, 8> boundResourceData;
-
- bool vsubufsPresent = false;
- bool fsubufsPresent = false;
- bool csubufsPresent = false;
- bool vssamplersPresent = false;
- bool fssamplersPresent = false;
- bool cssamplersPresent = false;
- bool csUAVsPresent = false;
-
- QRhiBatchedBindings<ID3D11Buffer *> vsubufs;
- QRhiBatchedBindings<UINT> vsubuforigbindings;
- QRhiBatchedBindings<UINT> vsubufoffsets;
- QRhiBatchedBindings<UINT> vsubufsizes;
-
- QRhiBatchedBindings<ID3D11Buffer *> fsubufs;
- QRhiBatchedBindings<UINT> fsubuforigbindings;
- QRhiBatchedBindings<UINT> fsubufoffsets;
- QRhiBatchedBindings<UINT> fsubufsizes;
-
- QRhiBatchedBindings<ID3D11Buffer *> csubufs;
- QRhiBatchedBindings<UINT> csubuforigbindings;
- 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_RELOCATABLE_TYPE);
-
-struct QD3D11GraphicsPipeline : public QRhiGraphicsPipeline
-{
- QD3D11GraphicsPipeline(QRhiImplementation *rhi);
- ~QD3D11GraphicsPipeline();
- void destroy() override;
- bool create() override;
-
- ID3D11DepthStencilState *dsState = nullptr;
- ID3D11BlendState *blendState = nullptr;
- struct {
- ID3D11VertexShader *shader = nullptr;
- QShader::NativeResourceBindingMap nativeResourceBindingMap;
- } vs;
- struct {
- ID3D11PixelShader *shader = nullptr;
- QShader::NativeResourceBindingMap nativeResourceBindingMap;
- } fs;
- 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 destroy() override;
- bool create() override;
-
- struct {
- ID3D11ComputeShader *shader = nullptr;
- QShader::NativeResourceBindingMap nativeResourceBindingMap;
- } cs;
- uint generation = 0;
- friend class QRhiD3D11;
-};
-
-struct QD3D11SwapChain;
-
-struct QD3D11CommandBuffer : public QRhiCommandBuffer
-{
- QD3D11CommandBuffer(QRhiImplementation *rhi);
- ~QD3D11CommandBuffer();
- void destroy() override;
-
- // these must be kept at a reasonably low value otherwise sizeof Command explodes
- static const int MAX_DYNAMIC_OFFSET_COUNT = 8;
- static const int MAX_VERTEX_BUFFER_BINDING_COUNT = 8;
-
- 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;
-
- // QRhi*/QD3D11* references should be kept at minimum (so no
- // QRhiTexture/Buffer/etc. pointers).
- union Args {
- 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[MAX_VERTEX_BUFFER_BINDING_COUNT];
- UINT offsets[MAX_VERTEX_BUFFER_BINDING_COUNT];
- UINT strides[MAX_VERTEX_BUFFER_BINDING_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_DYNAMIC_OFFSET_COUNT * 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;
- UINT dstZ;
- 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
- };
-
- QRhiBackendCommandList<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];
-
- QVarLengthArray<QByteArray, 4> dataRetainPool;
- QVarLengthArray<QRhiBufferData, 4> bufferDataRetainPool;
- QVarLengthArray<QImage, 4> 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.last().constData());
- }
- const uchar *retainBufferData(const QRhiBufferData &data) {
- bufferDataRetainPool.append(data);
- return reinterpret_cast<const uchar *>(bufferDataRetainPool.last().constData());
- }
- const uchar *retainImage(const QImage &image) {
- imageRetainPool.append(image);
- return imageRetainPool.last().constBits();
- }
- void resetCommands() {
- commands.reset();
- dataRetainPool.clear();
- bufferDataRetainPool.clear();
- imageRetainPool.clear();
- }
- void resetState() {
- recordingPass = NoPass;
- currentTarget = nullptr;
- resetCommands();
- 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));
- }
-};
-
-struct QD3D11SwapChain : public QRhiSwapChain
-{
- QD3D11SwapChain(QRhiImplementation *rhi);
- ~QD3D11SwapChain();
- void destroy() override;
-
- QRhiCommandBuffer *currentFrameCommandBuffer() override;
- QRhiRenderTarget *currentFrameRenderTarget() override;
-
- QSize surfacePixelSize() override;
- bool isFormatSupported(Format f) override;
- QRhiSwapChainHdrInfo hdrInfo() override;
-
- QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
- bool createOrResize() 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;
- QD3D11SwapChainRenderTarget rt;
- QD3D11CommandBuffer cb;
- DXGI_FORMAT colorFormat;
- DXGI_FORMAT srgbAdjustedColorFormat;
- IDXGISwapChain *swapChain = nullptr;
- UINT swapChainFlags = 0;
- static const int BUFFER_COUNT = 2;
- ID3D11Texture2D *backBufferTex;
- ID3D11RenderTargetView *backBufferRtv;
- 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,
- QRhiTexture::Format backingFormatHint) override;
- QRhiTexture *createTexture(QRhiTexture::Format format,
- const QSize &pixelSize,
- int depth,
- int arraySize,
- int sampleCount,
- QRhiTexture::Flags flags) override;
- QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
- QRhiSampler::Filter minFilter,
- QRhiSampler::Filter mipmapMode,
- QRhiSampler:: AddressMode u,
- QRhiSampler::AddressMode v,
- QRhiSampler::AddressMode w) 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, QRhi::BeginFrameFlags flags) override;
- QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) 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,
- QRhiCommandBuffer::BeginPassFlags flags) 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,
- QRhiCommandBuffer::BeginPassFlags flags) 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;
-
- QList<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;
- QRhiDriverInfo driverInfo() const override;
- QRhiMemAllocStats graphicsMemoryAllocationStatistics() override;
- bool makeThreadLocalNativeContextCurrent() override;
- void releaseCachedResources() override;
- bool isDeviceLost() const override;
-
- QByteArray pipelineCacheData() override;
- void setPipelineCacheData(const QByteArray &data) override;
-
- void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
- int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
- void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
- void updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
- const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[]);
- void executeBufferHostWrites(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);
- void clearShaderCache();
- QByteArray compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, uint flags,
- QString *error, QShaderKey *usedShaderKey);
-
- QRhi::Flags rhiFlags;
- bool debugLayer = false;
- bool importedDeviceAndContext = false;
- ID3D11Device *dev = nullptr;
- ID3D11DeviceContext1 *context = nullptr;
- D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL(0);
- LUID adapterLuid = {};
- ID3DUserDefinedAnnotation *annotations = nullptr;
- IDXGIAdapter1 *activeAdapter = nullptr;
- IDXGIFactory1 *dxgiFactory = nullptr;
- bool supportsFlipSwapchain = false;
- bool supportsAllowTearing = false;
- bool forceFlipDiscard = false;
- bool deviceLost = false;
- QRhiD3D11NativeHandles nativeHandlesStruct;
- QRhiDriverInfo driverInfoStruct;
-
- 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 TextureReadback {
- QRhiReadbackDescription desc;
- QRhiReadbackResult *result;
- ID3D11Texture2D *stagingTex;
- quint32 byteSize;
- quint32 bpl;
- QSize pixelSize;
- QRhiTexture::Format format;
- };
- QVarLengthArray<TextureReadback, 2> activeTextureReadbacks;
- struct BufferReadback {
- QRhiBufferReadbackResult *result;
- quint32 byteSize;
- ID3D11Buffer *stagingBuf;
- };
- QVarLengthArray<BufferReadback, 2> activeBufferReadbacks;
-
- struct Shader {
- Shader() = default;
- Shader(IUnknown *s, const QByteArray &bytecode, const QShader::NativeResourceBindingMap &rbm)
- : s(s), bytecode(bytecode), nativeResourceBindingMap(rbm) { }
- IUnknown *s;
- QByteArray bytecode;
- QShader::NativeResourceBindingMap nativeResourceBindingMap;
- };
- QHash<QRhiShaderStage, Shader> m_shaderCache;
-
- struct DeviceCurse {
- DeviceCurse(QRhiD3D11 *impl) : q(impl) { }
- QRhiD3D11 *q;
- int framesToActivate = -1;
- bool permanent = false;
- int framesLeft = 0;
- ID3D11ComputeShader *cs = nullptr;
-
- void initResources();
- void releaseResources();
- void activate();
- } deviceCurse;
-
- // This is what gets exposed as the "pipeline cache", not that that concept
- // applies anyway. Here we are just storing the DX bytecode for a shader so
- // we can skip the HLSL->DXBC compilation when the QShader has HLSL source
- // code and the same shader source has already been compiled before.
- // m_shaderCache seemingly does the same, but this here does not care about
- // the ID3D11*Shader, this is just about the bytecode and about allowing
- // the data to be serialized to persistent storage and then reloaded in
- // future runs of the app, or when creating another QRhi, etc.
- struct BytecodeCacheKey {
- QByteArray sourceHash;
- QByteArray target;
- QByteArray entryPoint;
- uint compileFlags;
- };
- QHash<BytecodeCacheKey, QByteArray> m_bytecodeCache;
-};
-
-Q_DECLARE_TYPEINFO(QRhiD3D11::TextureReadback, Q_RELOCATABLE_TYPE);
-Q_DECLARE_TYPEINFO(QRhiD3D11::BufferReadback, Q_RELOCATABLE_TYPE);
-
-inline bool operator==(const QRhiD3D11::BytecodeCacheKey &a, const QRhiD3D11::BytecodeCacheKey &b) noexcept
-{
- return a.sourceHash == b.sourceHash
- && a.target == b.target
- && a.entryPoint == b.entryPoint
- && a.compileFlags == b.compileFlags;
-}
-
-inline bool operator!=(const QRhiD3D11::BytecodeCacheKey &a, const QRhiD3D11::BytecodeCacheKey &b) noexcept
-{
- return !(a == b);
-}
-
-inline size_t qHash(const QRhiD3D11::BytecodeCacheKey &k, size_t seed = 0) noexcept
-{
- return qHash(k.sourceHash, seed) ^ qHash(k.target) ^ qHash(k.entryPoint) ^ k.compileFlags;
-}
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/gui/rhi/qrhid3d12.cpp b/src/gui/rhi/qrhid3d12.cpp
new file mode 100644
index 0000000000..0f176c683d
--- /dev/null
+++ b/src/gui/rhi/qrhid3d12.cpp
@@ -0,0 +1,6569 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qrhid3d12_p.h"
+#include <qmath.h>
+#include <QtCore/private/qsystemerror_p.h>
+#include <comdef.h>
+#include "qrhid3dhelpers_p.h"
+#include "cs_mipmap_p.h"
+
+#if __has_include(<pix.h>)
+#include <pix.h>
+#define QRHI_D3D12_HAS_OLD_PIX
+#endif
+
+#ifdef __ID3D12Device2_INTERFACE_DEFINED__
+
+QT_BEGIN_NAMESPACE
+
+/*
+ Direct 3D 12 backend.
+*/
+
+/*!
+ \class QRhiD3D12InitParams
+ \inmodule QtGui
+ \brief Direct3D 12 specific initialization parameters.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
+ A D3D12-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
+ QRhiD3D12InitParams params;
+ params.enableDebugLayer = true;
+ rhi = QRhi::create(QRhi::D3D12, &params);
+ \endcode
+
+ \note QRhiSwapChain should only be used in combination with QWindow
+ instances that have their surface type set to QSurface::Direct3DSurface.
+
+ \section2 Working with existing Direct3D 12 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 QRhiD3D12NativeHandles to
+ QRhi::create(). QRhi does not take ownership of any of the external
+ objects.
+
+ Sometimes, for example when using QRhi in combination with OpenXR, one will
+ want to specify which adapter to use, and optionally, which feature level
+ to request on the device, while leaving the device creation to QRhi. This
+ is achieved by leaving the device pointer set to null, while specifying the
+ adapter LUID and feature level.
+
+ Optionally the ID3D12CommandQueue can be specified as well, by setting \c
+ commandQueue to a non-null value.
+ */
+
+/*!
+ \variable QRhiD3D12InitParams::enableDebugLayer
+
+ When set to true, the debug layer is enabled, if installed and available.
+ The default value is false.
+*/
+
+/*!
+ \class QRhiD3D12NativeHandles
+ \inmodule QtGui
+ \brief Holds the D3D12 device used by the QRhi.
+
+ \note The class uses \c{void *} as the type since including the COM-based
+ \c{d3d12.h} headers is not acceptable here. The actual types are
+ \c{ID3D12Device *} and \c{ID3D12CommandQueue *}.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+ */
+
+/*!
+ \variable QRhiD3D12NativeHandles::dev
+
+ Points to a
+ \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12device}{ID3D12Device}
+ or left set to \nullptr if no existing device is to be imported.
+*/
+
+/*!
+ \variable QRhiD3D12NativeHandles::minimumFeatureLevel
+
+ Specifies the \b minimum feature level passed to
+ \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-d3d12createdevice}{D3D12CreateDevice()}.
+ When not set, \c{D3D_FEATURE_LEVEL_11_0} is used. See
+ \l{https://learn.microsoft.com/en-us/windows/win32/direct3d12/hardware-feature-levels}{this
+ page} for details.
+
+ Relevant only when QRhi creates the device, ignored when importing a device
+ and device context.
+*/
+
+/*!
+ \variable QRhiD3D12NativeHandles::adapterLuidLow
+
+ The low part of the local identifier (LUID) of the DXGI adapter to use.
+ Relevant only when QRhi creates the device, ignored when importing a device
+ and device context.
+*/
+
+/*!
+ \variable QRhiD3D12NativeHandles::adapterLuidHigh
+
+ The high part of the local identifier (LUID) of the DXGI adapter to use.
+ Relevant only when QRhi creates the device, ignored when importing a device
+ and device context.
+*/
+
+/*!
+ \variable QRhiD3D12NativeHandles::commandQueue
+
+ When set, must point to a
+ \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12commandqueue}{ID3D12CommandQueue}.
+ It allows to optionally import a command queue as well, in addition to a
+ device.
+*/
+
+/*!
+ \class QRhiD3D12CommandBufferNativeHandles
+ \inmodule QtGui
+ \brief Holds the ID3D12GraphicsCommandList1 object that is backing a QRhiCommandBuffer.
+
+ \note The command list 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::endOffscreenFrame()}{endOffscreenFrame()} pair.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+ */
+
+/*!
+ \variable QRhiD3D12CommandBufferNativeHandles::commandList
+*/
+
+// https://learn.microsoft.com/en-us/windows/win32/direct3d12/hardware-feature-levels
+static const D3D_FEATURE_LEVEL MIN_FEATURE_LEVEL = D3D_FEATURE_LEVEL_11_0;
+
+QRhiD3D12::QRhiD3D12(QRhiD3D12InitParams *params, QRhiD3D12NativeHandles *importParams)
+{
+ debugLayer = params->enableDebugLayer;
+ if (importParams) {
+ if (importParams->dev) {
+ ID3D12Device *d3d12Device = reinterpret_cast<ID3D12Device *>(importParams->dev);
+ if (SUCCEEDED(d3d12Device->QueryInterface(__uuidof(ID3D12Device2), reinterpret_cast<void **>(&dev)))) {
+ // get rid of the ref added by QueryInterface
+ d3d12Device->Release();
+ importedDevice = true;
+ } else {
+ qWarning("ID3D12Device2 not supported, cannot import device");
+ }
+ }
+ if (importParams->commandQueue) {
+ cmdQueue = reinterpret_cast<ID3D12CommandQueue *>(importParams->commandQueue);
+ importedCommandQueue = true;
+ }
+ minimumFeatureLevel = D3D_FEATURE_LEVEL(importParams->minimumFeatureLevel);
+ adapterLuid.LowPart = importParams->adapterLuidLow;
+ adapterLuid.HighPart = importParams->adapterLuidHigh;
+ }
+}
+
+template <class Int>
+inline Int aligned(Int v, Int byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+static inline UINT calcSubresource(UINT mipSlice, UINT arraySlice, UINT mipLevels)
+{
+ return mipSlice + arraySlice * mipLevels;
+}
+
+static inline QD3D12RenderTargetData *rtData(QRhiRenderTarget *rt)
+{
+ switch (rt->resourceType()) {
+ case QRhiResource::SwapChainRenderTarget:
+ return &QRHI_RES(QD3D12SwapChainRenderTarget, rt)->d;
+ case QRhiResource::TextureRenderTarget:
+ return &QRHI_RES(QD3D12TextureRenderTarget, rt)->d;
+ break;
+ default:
+ break;
+ }
+ Q_UNREACHABLE_RETURN(nullptr);
+}
+
+bool QRhiD3D12::create(QRhi::Flags flags)
+{
+ rhiFlags = flags;
+
+ UINT factoryFlags = 0;
+ if (debugLayer)
+ factoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
+ HRESULT hr = CreateDXGIFactory2(factoryFlags, __uuidof(IDXGIFactory2), reinterpret_cast<void **>(&dxgiFactory));
+ if (FAILED(hr)) {
+ // retry without debug, if it was requested (to match D3D11 backend behavior)
+ if (debugLayer) {
+ qCDebug(QRHI_LOG_INFO, "Debug layer was requested but is not available. "
+ "Attempting to create DXGIFactory2 without it.");
+ factoryFlags &= ~DXGI_CREATE_FACTORY_DEBUG;
+ hr = CreateDXGIFactory2(factoryFlags, __uuidof(IDXGIFactory2), reinterpret_cast<void **>(&dxgiFactory));
+ }
+ if (SUCCEEDED(hr)) {
+ debugLayer = false;
+ } else {
+ qWarning("CreateDXGIFactory2() failed to create DXGI factory: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ }
+
+ supportsAllowTearing = false;
+ IDXGIFactory5 *factory5 = nullptr;
+ if (SUCCEEDED(dxgiFactory->QueryInterface(__uuidof(IDXGIFactory5), reinterpret_cast<void **>(&factory5)))) {
+ BOOL allowTearing = false;
+ if (SUCCEEDED(factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing))))
+ supportsAllowTearing = allowTearing;
+ factory5->Release();
+ }
+
+ if (debugLayer) {
+ ID3D12Debug1 *debug = nullptr;
+ if (SUCCEEDED(D3D12GetDebugInterface(__uuidof(ID3D12Debug1), reinterpret_cast<void **>(&debug)))) {
+ qCDebug(QRHI_LOG_INFO, "Enabling D3D12 debug layer");
+ debug->EnableDebugLayer();
+ debug->Release();
+ }
+ }
+
+ if (!importedDevice) {
+ IDXGIAdapter1 *adapter;
+ int requestedAdapterIndex = -1;
+ if (qEnvironmentVariableIsSet("QT_D3D_ADAPTER_INDEX"))
+ requestedAdapterIndex = qEnvironmentVariableIntValue("QT_D3D_ADAPTER_INDEX");
+
+ // The importParams may specify an adapter by the luid, take that into account.
+ if (requestedAdapterIndex < 0 && (adapterLuid.LowPart || adapterLuid.HighPart)) {
+ for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
+ DXGI_ADAPTER_DESC1 desc;
+ adapter->GetDesc1(&desc);
+ adapter->Release();
+ if (desc.AdapterLuid.LowPart == adapterLuid.LowPart
+ && desc.AdapterLuid.HighPart == adapterLuid.HighPart)
+ {
+ requestedAdapterIndex = adapterIndex;
+ break;
+ }
+ }
+ }
+
+ if (requestedAdapterIndex < 0 && flags.testFlag(QRhi::PreferSoftwareRenderer)) {
+ for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
+ DXGI_ADAPTER_DESC1 desc;
+ adapter->GetDesc1(&desc);
+ adapter->Release();
+ if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
+ requestedAdapterIndex = adapterIndex;
+ break;
+ }
+ }
+ }
+
+ activeAdapter = nullptr;
+ for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
+ DXGI_ADAPTER_DESC1 desc;
+ adapter->GetDesc1(&desc);
+ const QString name = QString::fromUtf16(reinterpret_cast<char16_t *>(desc.Description));
+ qCDebug(QRHI_LOG_INFO, "Adapter %d: '%s' (vendor 0x%X device 0x%X flags 0x%X)",
+ adapterIndex,
+ qPrintable(name),
+ desc.VendorId,
+ desc.DeviceId,
+ desc.Flags);
+ if (!activeAdapter && (requestedAdapterIndex < 0 || requestedAdapterIndex == adapterIndex)) {
+ activeAdapter = adapter;
+ adapterLuid = desc.AdapterLuid;
+ QRhiD3D::fillDriverInfo(&driverInfoStruct, desc);
+ qCDebug(QRHI_LOG_INFO, " using this adapter");
+ } else {
+ adapter->Release();
+ }
+ }
+ if (!activeAdapter) {
+ qWarning("No adapter");
+ return false;
+ }
+
+ if (minimumFeatureLevel == 0)
+ minimumFeatureLevel = MIN_FEATURE_LEVEL;
+
+ hr = D3D12CreateDevice(activeAdapter,
+ minimumFeatureLevel,
+ __uuidof(ID3D12Device2),
+ reinterpret_cast<void **>(&dev));
+ if (FAILED(hr)) {
+ qWarning("Failed to create D3D12 device: %s", qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ } else {
+ Q_ASSERT(dev);
+ // cannot just get a IDXGIDevice from the ID3D12Device anymore, look up the adapter instead
+ adapterLuid = dev->GetAdapterLuid();
+ IDXGIAdapter1 *adapter;
+ for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
+ DXGI_ADAPTER_DESC1 desc;
+ adapter->GetDesc1(&desc);
+ if (desc.AdapterLuid.LowPart == adapterLuid.LowPart
+ && desc.AdapterLuid.HighPart == adapterLuid.HighPart)
+ {
+ activeAdapter = adapter;
+ QRhiD3D::fillDriverInfo(&driverInfoStruct, desc);
+ break;
+ } else {
+ adapter->Release();
+ }
+ }
+ if (!activeAdapter) {
+ qWarning("No adapter");
+ return false;
+ }
+ qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev);
+ }
+
+ if (debugLayer) {
+ ID3D12InfoQueue *infoQueue;
+ if (SUCCEEDED(dev->QueryInterface(__uuidof(ID3D12InfoQueue), reinterpret_cast<void **>(&infoQueue)))) {
+ if (qEnvironmentVariableIntValue("QT_D3D_DEBUG_BREAK")) {
+ infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
+ infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
+ infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, true);
+ }
+ D3D12_INFO_QUEUE_FILTER filter = {};
+ D3D12_MESSAGE_ID suppressedMessages[2] = {
+ // there is no way of knowing the clear color upfront
+ D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE,
+ // we have no control over viewport and scissor rects
+ D3D12_MESSAGE_ID_DRAW_EMPTY_SCISSOR_RECTANGLE
+ };
+ filter.DenyList.NumIDs = 2;
+ filter.DenyList.pIDList = suppressedMessages;
+ // Setting the filter would enable Info messages (e.g. about
+ // resource creation) which we don't need.
+ D3D12_MESSAGE_SEVERITY infoSev = D3D12_MESSAGE_SEVERITY_INFO;
+ filter.DenyList.NumSeverities = 1;
+ filter.DenyList.pSeverityList = &infoSev;
+ infoQueue->PushStorageFilter(&filter);
+ infoQueue->Release();
+ }
+ }
+
+ if (!importedCommandQueue) {
+ D3D12_COMMAND_QUEUE_DESC queueDesc = {};
+ queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
+ queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
+ hr = dev->CreateCommandQueue(&queueDesc, __uuidof(ID3D12CommandQueue), reinterpret_cast<void **>(&cmdQueue));
+ if (FAILED(hr)) {
+ qWarning("Failed to create command queue: %s", qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ }
+
+ hr = dev->CreateFence(0, D3D12_FENCE_FLAG_NONE, __uuidof(ID3D12Fence), reinterpret_cast<void **>(&fullFence));
+ if (FAILED(hr)) {
+ qWarning("Failed to create fence: %s", qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ fullFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+ fullFenceCounter = 0;
+
+ for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
+ hr = dev->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
+ __uuidof(ID3D12CommandAllocator),
+ reinterpret_cast<void **>(&cmdAllocators[i]));
+ if (FAILED(hr)) {
+ qWarning("Failed to create command allocator: %s", qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ }
+
+ if (!vma.create(dev, activeAdapter)) {
+ qWarning("Failed to initialize graphics memory suballocator");
+ return false;
+ }
+
+ if (!rtvPool.create(dev, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, "main RTV pool")) {
+ qWarning("Could not create RTV pool");
+ return false;
+ }
+
+ if (!dsvPool.create(dev, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, "main DSV pool")) {
+ qWarning("Could not create DSV pool");
+ return false;
+ }
+
+ if (!cbvSrvUavPool.create(dev, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, "main CBV-SRV-UAV pool")) {
+ qWarning("Could not create CBV-SRV-UAV pool");
+ return false;
+ }
+
+ resourcePool.create("main resource pool");
+ pipelinePool.create("main pipeline pool");
+ rootSignaturePool.create("main root signature pool");
+ releaseQueue.create(&resourcePool, &pipelinePool, &rootSignaturePool);
+ barrierGen.create(&resourcePool);
+
+ if (!samplerMgr.create(dev)) {
+ qWarning("Could not create sampler pool and shader-visible sampler heap");
+ return false;
+ }
+
+ if (!mipmapGen.create(this)) {
+ qWarning("Could not initialize mipmap generator");
+ return false;
+ }
+
+ const qint32 smallStagingSize = aligned(SMALL_STAGING_AREA_BYTES_PER_FRAME, QD3D12StagingArea::ALIGNMENT);
+ for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
+ if (!smallStagingAreas[i].create(this, smallStagingSize, D3D12_HEAP_TYPE_UPLOAD)) {
+ qWarning("Could not create host-visible staging area");
+ return false;
+ }
+ QString decoratedName = QLatin1String("Small staging area buffer/");
+ decoratedName += QString::number(i);
+ smallStagingAreas[i].mem.buffer->SetName(reinterpret_cast<LPCWSTR>(decoratedName.utf16()));
+ }
+
+ if (!shaderVisibleCbvSrvUavHeap.create(dev,
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
+ SHADER_VISIBLE_CBV_SRV_UAV_HEAP_PER_FRAME_START_SIZE))
+ {
+ qWarning("Could not create first shader-visible CBV/SRV/UAV heap");
+ return false;
+ }
+
+ if (flags.testFlag(QRhi::EnableTimestamps)) {
+ static bool wantsStablePowerState = qEnvironmentVariableIntValue("QT_D3D_STABLE_POWER_STATE");
+ //
+ // https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-setstablepowerstate
+ //
+ // NB! This is a _global_ setting, affecting other processes (and 3D
+ // APIs such as Vulkan), as long as this application is running. Hence
+ // making it an env.var. for now. Never enable it in production. But
+ // extremely useful for the GPU timings with NVIDIA at least; the
+ // timestamps become stable and smooth, making the number readable and
+ // actually useful e.g. in Quick 3D's DebugView when this is enabled.
+ // (otherwise the number's all over the place)
+ //
+ // See also
+ // https://developer.nvidia.com/blog/advanced-api-performance-setstablepowerstate/
+ // for possible other approaches.
+ //
+ if (wantsStablePowerState)
+ dev->SetStablePowerState(TRUE);
+
+ hr = cmdQueue->GetTimestampFrequency(&timestampTicksPerSecond);
+ if (FAILED(hr)) {
+ qWarning("Failed to query timestamp frequency: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ if (!timestampQueryHeap.create(dev, QD3D12_FRAMES_IN_FLIGHT * 2, D3D12_QUERY_HEAP_TYPE_TIMESTAMP)) {
+ qWarning("Failed to create timestamp query pool");
+ return false;
+ }
+ const quint32 readbackBufSize = QD3D12_FRAMES_IN_FLIGHT * 2 * sizeof(quint64);
+ if (!timestampReadbackArea.create(this, readbackBufSize, D3D12_HEAP_TYPE_READBACK)) {
+ qWarning("Failed to create timestamp readback buffer");
+ return false;
+ }
+ timestampReadbackArea.mem.buffer->SetName(L"Timestamp readback buffer");
+ memset(timestampReadbackArea.mem.p, 0, readbackBufSize);
+ }
+
+ caps = {};
+ D3D12_FEATURE_DATA_D3D12_OPTIONS3 options3 = {};
+ if (SUCCEEDED(dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS3, &options3, sizeof(options3)))) {
+ caps.multiView = options3.ViewInstancingTier != D3D12_VIEW_INSTANCING_TIER_NOT_SUPPORTED;
+ // https://microsoft.github.io/DirectX-Specs/d3d/RelaxedCasting.html
+ caps.textureViewFormat = options3.CastingFullyTypedFormatSupported;
+ }
+
+ deviceLost = false;
+ offscreenActive = false;
+
+ nativeHandlesStruct.dev = dev;
+ nativeHandlesStruct.minimumFeatureLevel = minimumFeatureLevel;
+ nativeHandlesStruct.adapterLuidLow = adapterLuid.LowPart;
+ nativeHandlesStruct.adapterLuidHigh = adapterLuid.HighPart;
+ nativeHandlesStruct.commandQueue = cmdQueue;
+
+ return true;
+}
+
+void QRhiD3D12::destroy()
+{
+ if (!deviceLost && fullFence && fullFenceEvent)
+ waitGpu();
+
+ releaseQueue.releaseAll();
+
+ for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
+ if (offscreenCb[i]) {
+ if (offscreenCb[i]->cmdList)
+ offscreenCb[i]->cmdList->Release();
+ delete offscreenCb[i];
+ offscreenCb[i] = nullptr;
+ }
+ }
+
+ timestampQueryHeap.destroy();
+ timestampReadbackArea.destroy();
+
+ shaderVisibleCbvSrvUavHeap.destroy();
+
+ for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i)
+ smallStagingAreas[i].destroy();
+
+ mipmapGen.destroy();
+ samplerMgr.destroy();
+ resourcePool.destroy();
+ pipelinePool.destroy();
+ rootSignaturePool.destroy();
+ rtvPool.destroy();
+ dsvPool.destroy();
+ cbvSrvUavPool.destroy();
+
+ for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
+ if (cmdAllocators[i]) {
+ cmdAllocators[i]->Release();
+ cmdAllocators[i] = nullptr;
+ }
+ }
+
+ if (fullFenceEvent) {
+ CloseHandle(fullFenceEvent);
+ fullFenceEvent = nullptr;
+ }
+
+ if (fullFence) {
+ fullFence->Release();
+ fullFence = nullptr;
+ }
+
+ if (!importedCommandQueue) {
+ if (cmdQueue) {
+ cmdQueue->Release();
+ cmdQueue = nullptr;
+ }
+ }
+
+ vma.destroy();
+
+ if (!importedDevice) {
+ if (dev) {
+ dev->Release();
+ dev = nullptr;
+ }
+ }
+
+ if (dcompDevice) {
+ dcompDevice->Release();
+ dcompDevice = nullptr;
+ }
+
+ if (activeAdapter) {
+ activeAdapter->Release();
+ activeAdapter = nullptr;
+ }
+
+ if (dxgiFactory) {
+ dxgiFactory->Release();
+ dxgiFactory = nullptr;
+ }
+}
+
+QList<int> QRhiD3D12::supportedSampleCounts() const
+{
+ return { 1, 2, 4, 8 };
+}
+
+QRhiSwapChain *QRhiD3D12::createSwapChain()
+{
+ return new QD3D12SwapChain(this);
+}
+
+QRhiBuffer *QRhiD3D12::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
+{
+ return new QD3D12Buffer(this, type, usage, size);
+}
+
+int QRhiD3D12::ubufAlignment() const
+{
+ return D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT; // 256
+}
+
+bool QRhiD3D12::isYUpInFramebuffer() const
+{
+ return false;
+}
+
+bool QRhiD3D12::isYUpInNDC() const
+{
+ return true;
+}
+
+bool QRhiD3D12::isClipDepthZeroToOne() const
+{
+ return true;
+}
+
+QMatrix4x4 QRhiD3D12::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 QRhiD3D12::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 QRhiD3D12::isFeatureSupported(QRhi::Feature feature) const
+{
+ switch (feature) {
+ case QRhi::MultisampleTexture:
+ return true;
+ case QRhi::MultisampleRenderBuffer:
+ return true;
+ case QRhi::DebugMarkers:
+#ifdef QRHI_D3D12_HAS_OLD_PIX
+ return true;
+#else
+ return false;
+#endif
+ 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;
+ 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;
+ case QRhi::WideLines:
+ return false;
+ case QRhi::VertexShaderPointSize:
+ return false;
+ case QRhi::BaseVertex:
+ return true;
+ case QRhi::BaseInstance:
+ return true;
+ case QRhi::TriangleFanTopology:
+ return false;
+ case QRhi::ReadBackNonUniformBuffer:
+ return true;
+ case QRhi::ReadBackNonBaseMipLevel:
+ return true;
+ case QRhi::TexelFetch:
+ return true;
+ case QRhi::RenderToNonBaseMipLevel:
+ return true;
+ case QRhi::IntAttributes:
+ return true;
+ case QRhi::ScreenSpaceDerivatives:
+ return true;
+ case QRhi::ReadBackAnyTextureFormat:
+ return true;
+ case QRhi::PipelineCacheDataLoadSave:
+ return false; // ###
+ case QRhi::ImageDataStride:
+ return true;
+ case QRhi::RenderBufferImport:
+ return false;
+ case QRhi::ThreeDimensionalTextures:
+ return true;
+ case QRhi::RenderTo3DTextureSlice:
+ return true;
+ case QRhi::TextureArrays:
+ return true;
+ case QRhi::Tessellation:
+ return true;
+ case QRhi::GeometryShader:
+ return true;
+ case QRhi::TextureArrayRange:
+ return true;
+ case QRhi::NonFillPolygonMode:
+ return true;
+ case QRhi::OneDimensionalTextures:
+ return true;
+ case QRhi::OneDimensionalTextureMipmaps:
+ return false; // we generate mipmaps ourselves with compute and this is not implemented
+ case QRhi::HalfAttributes:
+ return true;
+ case QRhi::RenderToOneDimensionalTexture:
+ return true;
+ case QRhi::ThreeDimensionalTextureMipmaps:
+ return false; // we generate mipmaps ourselves with compute and this is not implemented
+ case QRhi::MultiView:
+ return caps.multiView;
+ case QRhi::TextureViewFormat:
+ return caps.textureViewFormat;
+ case QRhi::ResolveDepthStencil:
+ // there is no Multisample Resolve support for depth/stencil formats
+ // https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/hardware-support-for-direct3d-12-1-formats
+ return false;
+ }
+ return false;
+}
+
+int QRhiD3D12::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 QD3D12_FRAMES_IN_FLIGHT;
+ case QRhi::MaxAsyncReadbackFrames:
+ return QD3D12_FRAMES_IN_FLIGHT;
+ case QRhi::MaxThreadGroupsPerDimension:
+ return 65535;
+ case QRhi::MaxThreadsPerThreadGroup:
+ return 1024;
+ case QRhi::MaxThreadGroupX:
+ return 1024;
+ case QRhi::MaxThreadGroupY:
+ return 1024;
+ case QRhi::MaxThreadGroupZ:
+ return 1024;
+ case QRhi::TextureArraySizeMax:
+ return 2048;
+ case QRhi::MaxUniformBufferRange:
+ return 65536;
+ case QRhi::MaxVertexInputs:
+ return 32;
+ case QRhi::MaxVertexOutputs:
+ return 32;
+ }
+ return 0;
+}
+
+const QRhiNativeHandles *QRhiD3D12::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+QRhiDriverInfo QRhiD3D12::driverInfo() const
+{
+ return driverInfoStruct;
+}
+
+QRhiStats QRhiD3D12::statistics()
+{
+ QRhiStats result;
+ result.totalPipelineCreationTime = totalPipelineCreationTime();
+
+ D3D12MA::Budget budgets[2]; // [gpu, system] with discreet GPU or [shared, nothing] with UMA
+ vma.getBudget(&budgets[0], &budgets[1]);
+ for (int i = 0; i < 2; ++i) {
+ const D3D12MA::Statistics &stats(budgets[i].Stats);
+ result.blockCount += stats.BlockCount;
+ result.allocCount += stats.AllocationCount;
+ result.usedBytes += stats.AllocationBytes;
+ result.unusedBytes += stats.BlockBytes - stats.AllocationBytes;
+ result.totalUsageBytes += budgets[i].UsageBytes;
+ }
+
+ return result;
+}
+
+bool QRhiD3D12::makeThreadLocalNativeContextCurrent()
+{
+ // not applicable
+ return false;
+}
+
+void QRhiD3D12::releaseCachedResources()
+{
+ shaderBytecodeCache.data.clear();
+}
+
+bool QRhiD3D12::isDeviceLost() const
+{
+ return deviceLost;
+}
+
+QByteArray QRhiD3D12::pipelineCacheData()
+{
+ return {};
+}
+
+void QRhiD3D12::setPipelineCacheData(const QByteArray &data)
+{
+ Q_UNUSED(data);
+}
+
+QRhiRenderBuffer *QRhiD3D12::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags,
+ QRhiTexture::Format backingFormatHint)
+{
+ return new QD3D12RenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
+}
+
+QRhiTexture *QRhiD3D12::createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize, int depth, int arraySize,
+ int sampleCount, QRhiTexture::Flags flags)
+{
+ return new QD3D12Texture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
+}
+
+QRhiSampler *QRhiD3D12::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
+{
+ return new QD3D12Sampler(this, magFilter, minFilter, mipmapMode, u, v, w);
+}
+
+QRhiTextureRenderTarget *QRhiD3D12::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags)
+{
+ return new QD3D12TextureRenderTarget(this, desc, flags);
+}
+
+QRhiGraphicsPipeline *QRhiD3D12::createGraphicsPipeline()
+{
+ return new QD3D12GraphicsPipeline(this);
+}
+
+QRhiComputePipeline *QRhiD3D12::createComputePipeline()
+{
+ return new QD3D12ComputePipeline(this);
+}
+
+QRhiShaderResourceBindings *QRhiD3D12::createShaderResourceBindings()
+{
+ return new QD3D12ShaderResourceBindings(this);
+}
+
+void QRhiD3D12::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
+ QD3D12GraphicsPipeline *psD = QRHI_RES(QD3D12GraphicsPipeline, ps);
+ const bool pipelineChanged = cbD->currentGraphicsPipeline != psD || cbD->currentPipelineGeneration != psD->generation;
+
+ if (pipelineChanged) {
+ cbD->currentGraphicsPipeline = psD;
+ cbD->currentComputePipeline = nullptr;
+ cbD->currentPipelineGeneration = psD->generation;
+
+ if (QD3D12Pipeline *pipeline = pipelinePool.lookupRef(psD->handle)) {
+ Q_ASSERT(pipeline->type == QD3D12Pipeline::Graphics);
+ cbD->cmdList->SetPipelineState(pipeline->pso);
+ if (QD3D12RootSignature *rs = rootSignaturePool.lookupRef(psD->rootSigHandle))
+ cbD->cmdList->SetGraphicsRootSignature(rs->rootSig);
+ }
+
+ cbD->cmdList->IASetPrimitiveTopology(psD->topology);
+
+ if (psD->viewInstanceMask)
+ cbD->cmdList->SetViewInstanceMask(psD->viewInstanceMask);
+ }
+}
+
+void QD3D12CommandBuffer::visitUniformBuffer(QD3D12Stage s,
+ const QRhiShaderResourceBinding::Data::UniformBufferData &d,
+ int,
+ int binding,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
+{
+ QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, d.buf);
+ quint32 offset = d.offset;
+ if (d.hasDynamicOffset) {
+ for (int i = 0; i < dynamicOffsetCount; ++i) {
+ const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
+ if (dynOfs.first == binding) {
+ Q_ASSERT(aligned(dynOfs.second, 256u) == dynOfs.second);
+ offset += dynOfs.second;
+ }
+ }
+ }
+ QRHI_RES_RHI(QRhiD3D12);
+ visitorData.cbufs[s].append({ bufD->handles[rhiD->currentFrameSlot], offset });
+}
+
+void QD3D12CommandBuffer::visitTexture(QD3D12Stage s,
+ const QRhiShaderResourceBinding::TextureAndSampler &d,
+ int)
+{
+ QD3D12Texture *texD = QRHI_RES(QD3D12Texture, d.tex);
+ visitorData.srvs[s].append(texD->srv);
+}
+
+void QD3D12CommandBuffer::visitSampler(QD3D12Stage s,
+ const QRhiShaderResourceBinding::TextureAndSampler &d,
+ int)
+{
+ QD3D12Sampler *samplerD = QRHI_RES(QD3D12Sampler, d.sampler);
+ visitorData.samplers[s].append(samplerD->lookupOrCreateShaderVisibleDescriptor());
+}
+
+void QD3D12CommandBuffer::visitStorageBuffer(QD3D12Stage s,
+ const QRhiShaderResourceBinding::Data::StorageBufferData &d,
+ QD3D12ShaderResourceVisitor::StorageOp,
+ int)
+{
+ QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, d.buf);
+ // SPIRV-Cross generated HLSL uses RWByteAddressBuffer
+ D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
+ uavDesc.Format = DXGI_FORMAT_R32_TYPELESS;
+ uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
+ uavDesc.Buffer.FirstElement = d.offset / 4;
+ uavDesc.Buffer.NumElements = aligned(bufD->m_size - d.offset, 4u) / 4;
+ uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW;
+ visitorData.uavs[s].append({ bufD->handles[0], uavDesc });
+}
+
+void QD3D12CommandBuffer::visitStorageImage(QD3D12Stage s,
+ const QRhiShaderResourceBinding::Data::StorageImageData &d,
+ QD3D12ShaderResourceVisitor::StorageOp,
+ int)
+{
+ QD3D12Texture *texD = QRHI_RES(QD3D12Texture, d.tex);
+ const bool isCube = texD->m_flags.testFlag(QRhiTexture::CubeMap);
+ const bool isArray = texD->m_flags.testFlag(QRhiTexture::TextureArray);
+ const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
+ uavDesc.Format = texD->rtFormat;
+ if (isCube) {
+ uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
+ uavDesc.Texture2DArray.MipSlice = UINT(d.level);
+ uavDesc.Texture2DArray.FirstArraySlice = 0;
+ uavDesc.Texture2DArray.ArraySize = 6;
+ } else if (isArray) {
+ uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
+ uavDesc.Texture2DArray.MipSlice = UINT(d.level);
+ uavDesc.Texture2DArray.FirstArraySlice = 0;
+ uavDesc.Texture2DArray.ArraySize = UINT(qMax(0, texD->m_arraySize));
+ } else if (is3D) {
+ uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
+ uavDesc.Texture3D.MipSlice = UINT(d.level);
+ } else {
+ uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
+ uavDesc.Texture2D.MipSlice = UINT(d.level);
+ }
+ visitorData.uavs[s].append({ texD->handle, uavDesc });
+}
+
+void QRhiD3D12::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass != QD3D12CommandBuffer::NoPass);
+ QD3D12GraphicsPipeline *gfxPsD = QRHI_RES(QD3D12GraphicsPipeline, cbD->currentGraphicsPipeline);
+ QD3D12ComputePipeline *compPsD = QRHI_RES(QD3D12ComputePipeline, cbD->currentComputePipeline);
+
+ if (!srb) {
+ if (gfxPsD)
+ srb = gfxPsD->m_shaderResourceBindings;
+ else
+ srb = compPsD->m_shaderResourceBindings;
+ }
+
+ QD3D12ShaderResourceBindings *srbD = QRHI_RES(QD3D12ShaderResourceBindings, srb);
+
+ for (int i = 0, ie = srbD->m_bindings.size(); i != ie; ++i) {
+ const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings[i]);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, b->u.ubuf.buf);
+ Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
+ Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
+ bufD->executeHostWritesForFrameSlot(currentFrameSlot);
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ case QRhiShaderResourceBinding::Texture:
+ case QRhiShaderResourceBinding::Sampler:
+ {
+ const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
+ for (int elem = 0; elem < data->count; ++elem) {
+ QD3D12Texture *texD = QRHI_RES(QD3D12Texture, data->texSamplers[elem].tex);
+ QD3D12Sampler *samplerD = QRHI_RES(QD3D12Sampler, data->texSamplers[elem].sampler);
+ // We use the same code path for both combined and separate
+ // images and samplers, so tex or sampler (but not both) can be
+ // null here.
+ Q_ASSERT(texD || samplerD);
+ if (texD) {
+ UINT state = 0;
+ if (b->stage == QRhiShaderResourceBinding::FragmentStage) {
+ state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
+ } else if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
+ state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
+ } else {
+ state = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
+ }
+ barrierGen.addTransitionBarrier(texD->handle, D3D12_RESOURCE_STATES(state));
+ barrierGen.enqueueBufferedTransitionBarriers(cbD);
+ }
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::ImageLoad:
+ case QRhiShaderResourceBinding::ImageStore:
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ {
+ QD3D12Texture *texD = QRHI_RES(QD3D12Texture, b->u.simage.tex);
+ if (QD3D12Resource *res = resourcePool.lookupRef(texD->handle)) {
+ if (res->uavUsage) {
+ if (res->uavUsage & QD3D12Resource::UavUsageWrite) {
+ // RaW or WaW
+ barrierGen.enqueueUavBarrier(cbD, texD->handle);
+ } else {
+ if (b->type == QRhiShaderResourceBinding::ImageStore
+ || b->type == QRhiShaderResourceBinding::ImageLoadStore)
+ {
+ // WaR or WaW
+ barrierGen.enqueueUavBarrier(cbD, texD->handle);
+ }
+ }
+ }
+ res->uavUsage = 0;
+ if (b->type == QRhiShaderResourceBinding::ImageLoad || b->type == QRhiShaderResourceBinding::ImageLoadStore)
+ res->uavUsage |= QD3D12Resource::UavUsageRead;
+ if (b->type == QRhiShaderResourceBinding::ImageStore || b->type == QRhiShaderResourceBinding::ImageLoadStore)
+ res->uavUsage |= QD3D12Resource::UavUsageWrite;
+ barrierGen.addTransitionBarrier(texD->handle, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
+ barrierGen.enqueueBufferedTransitionBarriers(cbD);
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::BufferLoad:
+ case QRhiShaderResourceBinding::BufferStore:
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ {
+ QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, b->u.sbuf.buf);
+ Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
+ Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
+ if (QD3D12Resource *res = resourcePool.lookupRef(bufD->handles[0])) {
+ if (res->uavUsage) {
+ if (res->uavUsage & QD3D12Resource::UavUsageWrite) {
+ // RaW or WaW
+ barrierGen.enqueueUavBarrier(cbD, bufD->handles[0]);
+ } else {
+ if (b->type == QRhiShaderResourceBinding::BufferStore
+ || b->type == QRhiShaderResourceBinding::BufferLoadStore)
+ {
+ // WaR or WaW
+ barrierGen.enqueueUavBarrier(cbD, bufD->handles[0]);
+ }
+ }
+ }
+ res->uavUsage = 0;
+ if (b->type == QRhiShaderResourceBinding::BufferLoad || b->type == QRhiShaderResourceBinding::BufferLoadStore)
+ res->uavUsage |= QD3D12Resource::UavUsageRead;
+ if (b->type == QRhiShaderResourceBinding::BufferStore || b->type == QRhiShaderResourceBinding::BufferLoadStore)
+ res->uavUsage |= QD3D12Resource::UavUsageWrite;
+ barrierGen.addTransitionBarrier(bufD->handles[0], D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
+ barrierGen.enqueueBufferedTransitionBarriers(cbD);
+ }
+ }
+ break;
+ }
+ }
+
+ const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
+ const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
+
+ if (srbChanged || srbRebuilt || srbD->hasDynamicOffset) {
+ const QD3D12ShaderStageData *stageData = gfxPsD ? gfxPsD->stageData.data() : &compPsD->stageData;
+
+ // The order of root parameters must match
+ // QD3D12ShaderResourceBindings::createRootSignature(), meaning the
+ // logic below must mirror that function (uniform buffers first etc.)
+
+ QD3D12ShaderResourceVisitor visitor(srbD, stageData, gfxPsD ? 5 : 1);
+
+ QD3D12CommandBuffer::VisitorData &visitorData(cbD->visitorData);
+ visitorData = {};
+
+ using namespace std::placeholders;
+ visitor.uniformBuffer = std::bind(&QD3D12CommandBuffer::visitUniformBuffer, cbD, _1, _2, _3, _4, dynamicOffsetCount, dynamicOffsets);
+ visitor.texture = std::bind(&QD3D12CommandBuffer::visitTexture, cbD, _1, _2, _3);
+ visitor.sampler = std::bind(&QD3D12CommandBuffer::visitSampler, cbD, _1, _2, _3);
+ visitor.storageBuffer = std::bind(&QD3D12CommandBuffer::visitStorageBuffer, cbD, _1, _2, _3, _4);
+ visitor.storageImage = std::bind(&QD3D12CommandBuffer::visitStorageImage, cbD, _1, _2, _3, _4);
+
+ visitor.visit();
+
+ quint32 cbvSrvUavCount = 0;
+ for (int s = 0; s < 6; ++s) {
+ // CBs use root constant buffer views, no need to count them here
+ cbvSrvUavCount += visitorData.srvs[s].count();
+ cbvSrvUavCount += visitorData.uavs[s].count();
+ }
+
+ bool gotNewHeap = false;
+ if (!ensureShaderVisibleDescriptorHeapCapacity(&shaderVisibleCbvSrvUavHeap,
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
+ currentFrameSlot,
+ cbvSrvUavCount,
+ &gotNewHeap))
+ {
+ return;
+ }
+ if (gotNewHeap) {
+ qCDebug(QRHI_LOG_INFO, "Created new shader-visible CBV/SRV/UAV descriptor heap,"
+ " per-frame slice size is now %u,"
+ " if this happens frequently then that's not great.",
+ shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[0].capacity);
+ bindShaderVisibleHeaps(cbD);
+ }
+
+ int rootParamIndex = 0;
+ for (int s = 0; s < 6; ++s) {
+ if (!visitorData.cbufs[s].isEmpty()) {
+ for (int i = 0, count = visitorData.cbufs[s].count(); i < count; ++i) {
+ const auto &cbuf(visitorData.cbufs[s][i]);
+ if (QD3D12Resource *res = resourcePool.lookupRef(cbuf.first)) {
+ quint32 offset = cbuf.second;
+ D3D12_GPU_VIRTUAL_ADDRESS gpuAddr = res->resource->GetGPUVirtualAddress() + offset;
+ if (cbD->currentGraphicsPipeline)
+ cbD->cmdList->SetGraphicsRootConstantBufferView(rootParamIndex, gpuAddr);
+ else
+ cbD->cmdList->SetComputeRootConstantBufferView(rootParamIndex, gpuAddr);
+ }
+ rootParamIndex += 1;
+ }
+ }
+ }
+ for (int s = 0; s < 6; ++s) {
+ if (!visitorData.srvs[s].isEmpty()) {
+ QD3D12DescriptorHeap &gpuSrvHeap(shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[currentFrameSlot]);
+ QD3D12Descriptor startDesc = gpuSrvHeap.get(visitorData.srvs[s].count());
+ for (int i = 0, count = visitorData.srvs[s].count(); i < count; ++i) {
+ const auto &srv(visitorData.srvs[s][i]);
+ dev->CopyDescriptorsSimple(1, gpuSrvHeap.incremented(startDesc, i).cpuHandle, srv.cpuHandle,
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ }
+
+ if (cbD->currentGraphicsPipeline)
+ cbD->cmdList->SetGraphicsRootDescriptorTable(rootParamIndex, startDesc.gpuHandle);
+ else if (cbD->currentComputePipeline)
+ cbD->cmdList->SetComputeRootDescriptorTable(rootParamIndex, startDesc.gpuHandle);
+
+ rootParamIndex += 1;
+ }
+ }
+ for (int s = 0; s < 6; ++s) {
+ // Samplers are one parameter / descriptor table each, and the
+ // descriptor is from the shader visible sampler heap already.
+ for (const QD3D12Descriptor &samplerDescriptor : visitorData.samplers[s]) {
+ if (cbD->currentGraphicsPipeline)
+ cbD->cmdList->SetGraphicsRootDescriptorTable(rootParamIndex, samplerDescriptor.gpuHandle);
+ else if (cbD->currentComputePipeline)
+ cbD->cmdList->SetComputeRootDescriptorTable(rootParamIndex, samplerDescriptor.gpuHandle);
+
+ rootParamIndex += 1;
+ }
+ }
+ for (int s = 0; s < 6; ++s) {
+ if (!visitorData.uavs[s].isEmpty()) {
+ QD3D12DescriptorHeap &gpuUavHeap(shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[currentFrameSlot]);
+ QD3D12Descriptor startDesc = gpuUavHeap.get(visitorData.uavs[s].count());
+ for (int i = 0, count = visitorData.uavs[s].count(); i < count; ++i) {
+ const auto &uav(visitorData.uavs[s][i]);
+ if (QD3D12Resource *res = resourcePool.lookupRef(uav.first)) {
+ dev->CreateUnorderedAccessView(res->resource, nullptr, &uav.second,
+ gpuUavHeap.incremented(startDesc, i).cpuHandle);
+ } else {
+ dev->CreateUnorderedAccessView(nullptr, nullptr, nullptr,
+ gpuUavHeap.incremented(startDesc, i).cpuHandle);
+ }
+ }
+
+ if (cbD->currentGraphicsPipeline)
+ cbD->cmdList->SetGraphicsRootDescriptorTable(rootParamIndex, startDesc.gpuHandle);
+ else if (cbD->currentComputePipeline)
+ cbD->cmdList->SetComputeRootDescriptorTable(rootParamIndex, startDesc.gpuHandle);
+
+ rootParamIndex += 1;
+ }
+ }
+
+ if (gfxPsD) {
+ cbD->currentGraphicsSrb = srb;
+ cbD->currentComputeSrb = nullptr;
+ } else {
+ cbD->currentGraphicsSrb = nullptr;
+ cbD->currentComputeSrb = srb;
+ }
+ cbD->currentSrbGeneration = srbD->generation;
+ }
+}
+
+void QRhiD3D12::setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
+
+ bool needsBindVBuf = false;
+ for (int i = 0; i < bindingCount; ++i) {
+ const int inputSlot = startBinding + i;
+ QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, bindings[i].first);
+ Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
+ const bool isDynamic = bufD->m_type == QRhiBuffer::Dynamic;
+ if (isDynamic)
+ bufD->executeHostWritesForFrameSlot(currentFrameSlot);
+
+ if (cbD->currentVertexBuffers[inputSlot] != bufD->handles[isDynamic ? currentFrameSlot : 0]
+ || cbD->currentVertexOffsets[inputSlot] != bindings[i].second)
+ {
+ needsBindVBuf = true;
+ cbD->currentVertexBuffers[inputSlot] = bufD->handles[isDynamic ? currentFrameSlot : 0];
+ cbD->currentVertexOffsets[inputSlot] = bindings[i].second;
+ }
+ }
+
+ if (needsBindVBuf) {
+ QVarLengthArray<D3D12_VERTEX_BUFFER_VIEW, 4> vbv;
+ vbv.reserve(bindingCount);
+
+ QD3D12GraphicsPipeline *psD = cbD->currentGraphicsPipeline;
+ const QRhiVertexInputLayout &inputLayout(psD->m_vertexInputLayout);
+ const int inputBindingCount = inputLayout.cendBindings() - inputLayout.cbeginBindings();
+
+ for (int i = 0, ie = qMin(bindingCount, inputBindingCount); i != ie; ++i) {
+ QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, bindings[i].first);
+ const QD3D12ObjectHandle handle = bufD->handles[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0];
+ const quint32 offset = bindings[i].second;
+ const quint32 stride = inputLayout.bindingAt(i)->stride();
+
+ if (bufD->m_type != QRhiBuffer::Dynamic) {
+ barrierGen.addTransitionBarrier(handle, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);
+ barrierGen.enqueueBufferedTransitionBarriers(cbD);
+ }
+
+ if (QD3D12Resource *res = resourcePool.lookupRef(handle)) {
+ vbv.append({
+ res->resource->GetGPUVirtualAddress() + offset,
+ UINT(res->desc.Width - offset),
+ stride
+ });
+ }
+ }
+
+ cbD->cmdList->IASetVertexBuffers(UINT(startBinding), vbv.count(), vbv.constData());
+ }
+
+ if (indexBuf) {
+ QD3D12Buffer *ibufD = QRHI_RES(QD3D12Buffer, indexBuf);
+ Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
+ const bool isDynamic = ibufD->m_type == QRhiBuffer::Dynamic;
+ if (isDynamic)
+ ibufD->executeHostWritesForFrameSlot(currentFrameSlot);
+
+ const DXGI_FORMAT dxgiFormat = indexFormat == QRhiCommandBuffer::IndexUInt16 ? DXGI_FORMAT_R16_UINT
+ : DXGI_FORMAT_R32_UINT;
+ if (cbD->currentIndexBuffer != ibufD->handles[isDynamic ? currentFrameSlot : 0]
+ || cbD->currentIndexOffset != indexOffset
+ || cbD->currentIndexFormat != dxgiFormat)
+ {
+ cbD->currentIndexBuffer = ibufD->handles[isDynamic ? currentFrameSlot : 0];
+ cbD->currentIndexOffset = indexOffset;
+ cbD->currentIndexFormat = dxgiFormat;
+
+ if (ibufD->m_type != QRhiBuffer::Dynamic) {
+ barrierGen.addTransitionBarrier(cbD->currentIndexBuffer, D3D12_RESOURCE_STATE_INDEX_BUFFER);
+ barrierGen.enqueueBufferedTransitionBarriers(cbD);
+ }
+
+ if (QD3D12Resource *res = resourcePool.lookupRef(cbD->currentIndexBuffer)) {
+ const D3D12_INDEX_BUFFER_VIEW ibv = {
+ res->resource->GetGPUVirtualAddress() + indexOffset,
+ UINT(res->desc.Width - indexOffset),
+ dxgiFormat
+ };
+ cbD->cmdList->IASetIndexBuffer(&ibv);
+ }
+ }
+ }
+}
+
+void QRhiD3D12::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
+ Q_ASSERT(cbD->currentTarget);
+ const QSize outputSize = cbD->currentTarget->pixelSize();
+
+ // D3D expects top-left, QRhiViewport is bottom-left
+ float x, y, w, h;
+ if (!qrhi_toTopLeftRenderTargetRect<UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
+ return;
+
+ D3D12_VIEWPORT v;
+ v.TopLeftX = x;
+ v.TopLeftY = y;
+ v.Width = w;
+ v.Height = h;
+ v.MinDepth = viewport.minDepth();
+ v.MaxDepth = viewport.maxDepth();
+ cbD->cmdList->RSSetViewports(1, &v);
+
+ if (cbD->currentGraphicsPipeline
+ && !cbD->currentGraphicsPipeline->flags().testFlag(QRhiGraphicsPipeline::UsesScissor))
+ {
+ qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, viewport.viewport(), &x, &y, &w, &h);
+ D3D12_RECT r;
+ r.left = x;
+ r.top = y;
+ // right and bottom are exclusive
+ r.right = x + w;
+ r.bottom = y + h;
+ cbD->cmdList->RSSetScissorRects(1, &r);
+ }
+}
+
+void QRhiD3D12::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
+ Q_ASSERT(cbD->currentTarget);
+ const QSize outputSize = cbD->currentTarget->pixelSize();
+
+ // D3D expects top-left, QRhiScissor is bottom-left
+ int x, y, w, h;
+ if (!qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
+ return;
+
+ D3D12_RECT r;
+ r.left = x;
+ r.top = y;
+ // right and bottom are exclusive
+ r.right = x + w;
+ r.bottom = y + h;
+ cbD->cmdList->RSSetScissorRects(1, &r);
+}
+
+void QRhiD3D12::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
+ float v[4] = { c.redF(), c.greenF(), c.blueF(), c.alphaF() };
+ cbD->cmdList->OMSetBlendFactor(v);
+}
+
+void QRhiD3D12::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
+ cbD->cmdList->OMSetStencilRef(refValue);
+}
+
+void QRhiD3D12::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
+ cbD->cmdList->DrawInstanced(vertexCount, instanceCount, firstVertex, firstInstance);
+}
+
+void QRhiD3D12::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
+ cbD->cmdList->DrawIndexedInstanced(indexCount, instanceCount,
+ firstIndex, vertexOffset,
+ firstInstance);
+}
+
+void QRhiD3D12::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
+{
+ if (!debugMarkers)
+ return;
+
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+#ifdef QRHI_D3D12_HAS_OLD_PIX
+ PIXBeginEvent(cbD->cmdList, PIX_COLOR_DEFAULT, reinterpret_cast<LPCWSTR>(QString::fromLatin1(name).utf16()));
+#else
+ Q_UNUSED(cbD);
+ Q_UNUSED(name);
+#endif
+}
+
+void QRhiD3D12::debugMarkEnd(QRhiCommandBuffer *cb)
+{
+ if (!debugMarkers)
+ return;
+
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+#ifdef QRHI_D3D12_HAS_OLD_PIX
+ PIXEndEvent(cbD->cmdList);
+#else
+ Q_UNUSED(cbD);
+#endif
+}
+
+void QRhiD3D12::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
+{
+ if (!debugMarkers)
+ return;
+
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+#ifdef QRHI_D3D12_HAS_OLD_PIX
+ PIXSetMarker(cbD->cmdList, PIX_COLOR_DEFAULT, reinterpret_cast<LPCWSTR>(QString::fromLatin1(msg).utf16()));
+#else
+ Q_UNUSED(cbD);
+ Q_UNUSED(msg);
+#endif
+}
+
+const QRhiNativeHandles *QRhiD3D12::nativeHandles(QRhiCommandBuffer *cb)
+{
+ return QRHI_RES(QD3D12CommandBuffer, cb)->nativeHandles();
+}
+
+void QRhiD3D12::beginExternal(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+}
+
+void QRhiD3D12::endExternal(QRhiCommandBuffer *cb)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ cbD->resetPerPassState();
+ bindShaderVisibleHeaps(cbD);
+ if (cbD->currentTarget) { // could be compute, no rendertarget then
+ QD3D12RenderTargetData *rtD = rtData(cbD->currentTarget);
+ cbD->cmdList->OMSetRenderTargets(UINT(rtD->colorAttCount),
+ rtD->rtv,
+ TRUE,
+ rtD->dsAttCount ? &rtD->dsv : nullptr);
+ }
+}
+
+double QRhiD3D12::lastCompletedGpuTime(QRhiCommandBuffer *cb)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ return cbD->lastGpuTime;
+}
+
+static void calculateGpuTime(QD3D12CommandBuffer *cbD,
+ int timestampPairStartIndex,
+ const quint8 *readbackBufPtr,
+ quint64 timestampTicksPerSecond)
+{
+ const size_t byteOffset = timestampPairStartIndex * sizeof(quint64);
+ const quint64 *p = reinterpret_cast<const quint64 *>(readbackBufPtr + byteOffset);
+ const quint64 startTime = *p++;
+ const quint64 endTime = *p;
+ if (startTime < endTime) {
+ const quint64 ticks = endTime - startTime;
+ const double timeSec = ticks / double(timestampTicksPerSecond);
+ cbD->lastGpuTime = timeSec;
+ }
+}
+
+QRhi::FrameOpResult QRhiD3D12::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
+{
+ Q_UNUSED(flags);
+
+ QD3D12SwapChain *swapChainD = QRHI_RES(QD3D12SwapChain, swapChain);
+ currentSwapChain = swapChainD;
+ currentFrameSlot = swapChainD->currentFrameSlot;
+ QD3D12SwapChain::FrameResources &fr(swapChainD->frameRes[currentFrameSlot]);
+
+ // We could do smarter things but mirror the Vulkan backend for now: Make
+ // sure the previous commands for this same frame slot have finished. Do
+ // this also for any other swapchain's commands with the same frame slot.
+ // While this reduces concurrency in render-to-swapchain-A,
+ // render-to-swapchain-B, repeat kind of scenarios, 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. 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 guaranteed not to
+ // be in flight anymore). With Qt Quick this situation cannot happen anyway
+ // by design (one QRhi per window).
+ for (QD3D12SwapChain *sc : std::as_const(swapchains))
+ sc->waitCommandCompletionForFrameSlot(currentFrameSlot); // note: swapChainD->currentFrameSlot, not sc's
+
+ HRESULT hr = cmdAllocators[currentFrameSlot]->Reset();
+ if (FAILED(hr)) {
+ qWarning("Failed to reset command allocator: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return QRhi::FrameOpError;
+ }
+
+ if (!startCommandListForCurrentFrameSlot(&fr.cmdList))
+ return QRhi::FrameOpError;
+
+ QD3D12CommandBuffer *cbD = &swapChainD->cbWrapper;
+ cbD->cmdList = fr.cmdList;
+
+ swapChainD->rtWrapper.d.rtv[0] = swapChainD->sampleDesc.Count > 1
+ ? swapChainD->msaaRtvs[swapChainD->currentBackBufferIndex].cpuHandle
+ : swapChainD->rtvs[swapChainD->currentBackBufferIndex].cpuHandle;
+
+ swapChainD->rtWrapper.d.dsv = swapChainD->ds ? swapChainD->ds->dsv.cpuHandle
+ : D3D12_CPU_DESCRIPTOR_HANDLE { 0 };
+
+ if (swapChainD->stereo) {
+ swapChainD->rtWrapperRight.d.rtv[0] = swapChainD->sampleDesc.Count > 1
+ ? swapChainD->msaaRtvs[swapChainD->currentBackBufferIndex].cpuHandle
+ : swapChainD->rtvsRight[swapChainD->currentBackBufferIndex].cpuHandle;
+
+ swapChainD->rtWrapperRight.d.dsv =
+ swapChainD->ds ? swapChainD->ds->dsv.cpuHandle : D3D12_CPU_DESCRIPTOR_HANDLE{ 0 };
+ }
+
+
+ // Time to release things that are marked for currentFrameSlot since due to
+ // the wait above we know that the previous commands on the GPU for this
+ // slot must have finished already.
+ releaseQueue.executeDeferredReleases(currentFrameSlot);
+
+ // Full reset of the command buffer data.
+ cbD->resetState();
+
+ // Move the head back to zero for the per-frame shader-visible descriptor heap work areas.
+ shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[currentFrameSlot].head = 0;
+ // Same for the small staging area.
+ smallStagingAreas[currentFrameSlot].head = 0;
+
+ bindShaderVisibleHeaps(cbD);
+
+ finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls
+
+ if (timestampQueryHeap.isValid() && timestampTicksPerSecond) {
+ // Read the timestamps for the previous frame for this slot. (the
+ // ResolveQuery() should have completed by now due to the wait above)
+ const int timestampPairStartIndex = currentFrameSlot * QD3D12_FRAMES_IN_FLIGHT;
+ calculateGpuTime(cbD,
+ timestampPairStartIndex,
+ timestampReadbackArea.mem.p,
+ timestampTicksPerSecond);
+ // Write the start timestamp for this frame for this slot.
+ cbD->cmdList->EndQuery(timestampQueryHeap.heap,
+ D3D12_QUERY_TYPE_TIMESTAMP,
+ timestampPairStartIndex);
+ }
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiD3D12::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
+{
+ QD3D12SwapChain *swapChainD = QRHI_RES(QD3D12SwapChain, swapChain);
+ Q_ASSERT(currentSwapChain == swapChainD);
+ QD3D12CommandBuffer *cbD = &swapChainD->cbWrapper;
+
+ QD3D12ObjectHandle backBufferResourceHandle = swapChainD->colorBuffers[swapChainD->currentBackBufferIndex];
+ if (swapChainD->sampleDesc.Count > 1) {
+ QD3D12ObjectHandle msaaBackBufferResourceHandle = swapChainD->msaaBuffers[swapChainD->currentBackBufferIndex];
+ barrierGen.addTransitionBarrier(msaaBackBufferResourceHandle, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
+ barrierGen.addTransitionBarrier(backBufferResourceHandle, D3D12_RESOURCE_STATE_RESOLVE_DEST);
+ barrierGen.enqueueBufferedTransitionBarriers(cbD);
+ const QD3D12Resource *src = resourcePool.lookupRef(msaaBackBufferResourceHandle);
+ const QD3D12Resource *dst = resourcePool.lookupRef(backBufferResourceHandle);
+ if (src && dst)
+ cbD->cmdList->ResolveSubresource(dst->resource, 0, src->resource, 0, swapChainD->colorFormat);
+ }
+
+ barrierGen.addTransitionBarrier(backBufferResourceHandle, D3D12_RESOURCE_STATE_PRESENT);
+ barrierGen.enqueueBufferedTransitionBarriers(cbD);
+
+ if (timestampQueryHeap.isValid()) {
+ const int timestampPairStartIndex = currentFrameSlot * QD3D12_FRAMES_IN_FLIGHT;
+ cbD->cmdList->EndQuery(timestampQueryHeap.heap,
+ D3D12_QUERY_TYPE_TIMESTAMP,
+ timestampPairStartIndex + 1);
+ cbD->cmdList->ResolveQueryData(timestampQueryHeap.heap,
+ D3D12_QUERY_TYPE_TIMESTAMP,
+ timestampPairStartIndex,
+ 2,
+ timestampReadbackArea.mem.buffer,
+ timestampPairStartIndex * sizeof(quint64));
+ }
+
+ ID3D12GraphicsCommandList1 *cmdList = cbD->cmdList;
+ HRESULT hr = cmdList->Close();
+ if (FAILED(hr)) {
+ qWarning("Failed to close command list: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return QRhi::FrameOpError;
+ }
+
+ ID3D12CommandList *execList[] = { cmdList };
+ cmdQueue->ExecuteCommandLists(1, execList);
+
+ if (!flags.testFlag(QRhi::SkipPresent)) {
+ UINT presentFlags = 0;
+ if (swapChainD->swapInterval == 0
+ && (swapChainD->swapChainFlags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING))
+ {
+ presentFlags |= DXGI_PRESENT_ALLOW_TEARING;
+ }
+ if (!swapChainD->swapChain) {
+ qWarning("Failed to present, no swapchain");
+ return QRhi::FrameOpError;
+ }
+ HRESULT hr = swapChainD->swapChain->Present(swapChainD->swapInterval, presentFlags);
+ if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
+ qWarning("Device loss detected in Present()");
+ deviceLost = true;
+ return QRhi::FrameOpDeviceLost;
+ } else if (FAILED(hr)) {
+ qWarning("Failed to present: %s", qPrintable(QSystemError::windowsComString(hr)));
+ return QRhi::FrameOpError;
+ }
+
+ if (dcompDevice && swapChainD->dcompTarget && swapChainD->dcompVisual)
+ dcompDevice->Commit();
+ }
+
+ swapChainD->addCommandCompletionSignalForCurrentFrameSlot();
+
+ // NB! The deferred-release mechanism here differs from the older QRhi
+ // backends. There is no lastActiveFrameSlot tracking. Instead,
+ // currentFrameSlot is written to the registered entries now, and so the
+ // resources will get released in the frames_in_flight'th beginFrame()
+ // counting starting from now.
+ releaseQueue.activatePendingDeferredReleaseRequests(currentFrameSlot);
+
+ if (!flags.testFlag(QRhi::SkipPresent)) {
+ // Only move to the next slot if we presented. Otherwise will block and
+ // wait for completion in the next beginFrame already, but SkipPresent
+ // should be infrequent anyway.
+ swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QD3D12_FRAMES_IN_FLIGHT;
+ swapChainD->currentBackBufferIndex = swapChainD->swapChain->GetCurrentBackBufferIndex();
+ }
+
+ currentSwapChain = nullptr;
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiD3D12::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
+{
+ Q_UNUSED(flags);
+
+ // Switch to the next slot manually. Swapchains do not know about this
+ // which is good. So for example an onscreen, onscreen, offscreen,
+ // onscreen, onscreen, onscreen sequence of frames leads to 0, 1, 0, 0, 1,
+ // 0. (no strict alternation anymore) But this is not different from what
+ // happens when multiple swapchains are involved. Offscreen frames are
+ // synchronous anyway in the sense that they wait for execution to complete
+ // in endOffscreenFrame, so no resources used in that frame are busy
+ // anymore in the next frame.
+
+ currentFrameSlot = (currentFrameSlot + 1) % QD3D12_FRAMES_IN_FLIGHT;
+
+ for (QD3D12SwapChain *sc : std::as_const(swapchains))
+ sc->waitCommandCompletionForFrameSlot(currentFrameSlot); // note: not sc's currentFrameSlot
+
+ if (!offscreenCb[currentFrameSlot])
+ offscreenCb[currentFrameSlot] = new QD3D12CommandBuffer(this);
+ QD3D12CommandBuffer *cbD = offscreenCb[currentFrameSlot];
+ if (!startCommandListForCurrentFrameSlot(&cbD->cmdList))
+ return QRhi::FrameOpError;
+
+ releaseQueue.executeDeferredReleases(currentFrameSlot);
+ cbD->resetState();
+ shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[currentFrameSlot].head = 0;
+ smallStagingAreas[currentFrameSlot].head = 0;
+
+ bindShaderVisibleHeaps(cbD);
+
+ if (timestampQueryHeap.isValid() && timestampTicksPerSecond) {
+ cbD->cmdList->EndQuery(timestampQueryHeap.heap,
+ D3D12_QUERY_TYPE_TIMESTAMP,
+ currentFrameSlot * QD3D12_FRAMES_IN_FLIGHT);
+ }
+
+ offscreenActive = true;
+ *cb = cbD;
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiD3D12::endOffscreenFrame(QRhi::EndFrameFlags flags)
+{
+ Q_UNUSED(flags);
+ Q_ASSERT(offscreenActive);
+ offscreenActive = false;
+
+ QD3D12CommandBuffer *cbD = offscreenCb[currentFrameSlot];
+ if (timestampQueryHeap.isValid()) {
+ const int timestampPairStartIndex = currentFrameSlot * QD3D12_FRAMES_IN_FLIGHT;
+ cbD->cmdList->EndQuery(timestampQueryHeap.heap,
+ D3D12_QUERY_TYPE_TIMESTAMP,
+ timestampPairStartIndex + 1);
+ cbD->cmdList->ResolveQueryData(timestampQueryHeap.heap,
+ D3D12_QUERY_TYPE_TIMESTAMP,
+ timestampPairStartIndex,
+ 2,
+ timestampReadbackArea.mem.buffer,
+ timestampPairStartIndex * sizeof(quint64));
+ }
+
+ ID3D12GraphicsCommandList1 *cmdList = cbD->cmdList;
+ HRESULT hr = cmdList->Close();
+ if (FAILED(hr)) {
+ qWarning("Failed to close command list: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return QRhi::FrameOpError;
+ }
+
+ ID3D12CommandList *execList[] = { cmdList };
+ cmdQueue->ExecuteCommandLists(1, execList);
+
+ releaseQueue.activatePendingDeferredReleaseRequests(currentFrameSlot);
+
+ // wait for completion
+ waitGpu();
+
+ // 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);
+
+ // the timestamp query results should be available too, given the wait
+ if (timestampQueryHeap.isValid()) {
+ calculateGpuTime(cbD,
+ currentFrameSlot * QD3D12_FRAMES_IN_FLIGHT,
+ timestampReadbackArea.mem.p,
+ timestampTicksPerSecond);
+ }
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiD3D12::finish()
+{
+ if (!inFrame)
+ return QRhi::FrameOpSuccess;
+
+ QD3D12CommandBuffer *cbD = nullptr;
+ if (offscreenActive) {
+ Q_ASSERT(!currentSwapChain);
+ cbD = offscreenCb[currentFrameSlot];
+ } else {
+ Q_ASSERT(currentSwapChain);
+ cbD = &currentSwapChain->cbWrapper;
+ }
+ if (!cbD)
+ return QRhi::FrameOpError;
+
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::NoPass);
+
+ ID3D12GraphicsCommandList1 *cmdList = cbD->cmdList;
+ HRESULT hr = cmdList->Close();
+ if (FAILED(hr)) {
+ qWarning("Failed to close command list: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return QRhi::FrameOpError;
+ }
+
+ ID3D12CommandList *execList[] = { cmdList };
+ cmdQueue->ExecuteCommandLists(1, execList);
+
+ releaseQueue.activatePendingDeferredReleaseRequests(currentFrameSlot);
+
+ // full blocking wait for everything, frame slots do not matter now
+ waitGpu();
+
+ hr = cmdAllocators[currentFrameSlot]->Reset();
+ if (FAILED(hr)) {
+ qWarning("Failed to reset command allocator: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return QRhi::FrameOpError;
+ }
+
+ if (!startCommandListForCurrentFrameSlot(&cmdList))
+ return QRhi::FrameOpError;
+
+ cbD->resetState();
+
+ shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[currentFrameSlot].head = 0;
+ smallStagingAreas[currentFrameSlot].head = 0;
+
+ bindShaderVisibleHeaps(cbD);
+
+ releaseQueue.executeDeferredReleases(currentFrameSlot);
+
+ finishActiveReadbacks(true);
+
+ return QRhi::FrameOpSuccess;
+}
+
+void QRhiD3D12::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::NoPass);
+ enqueueResourceUpdates(cbD, resourceUpdates);
+}
+
+void QRhiD3D12::beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates,
+ QRhiCommandBuffer::BeginPassFlags)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::NoPass);
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cbD, resourceUpdates);
+
+ QD3D12RenderTargetData *rtD = rtData(rt);
+ bool wantsColorClear = true;
+ bool wantsDsClear = true;
+ if (rt->resourceType() == QRhiRenderTarget::TextureRenderTarget) {
+ QD3D12TextureRenderTarget *rtTex = QRHI_RES(QD3D12TextureRenderTarget, rt);
+ wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
+ wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
+ if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QD3D12Texture, QD3D12RenderBuffer>(rtTex->description(), rtD->currentResIdList))
+ rtTex->create();
+
+ for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments(); it != itEnd; ++it) {
+ QD3D12Texture *texD = QRHI_RES(QD3D12Texture, it->texture());
+ QD3D12Texture *resolveTexD = QRHI_RES(QD3D12Texture, it->resolveTexture());
+ QD3D12RenderBuffer *rbD = QRHI_RES(QD3D12RenderBuffer, it->renderBuffer());
+ if (texD)
+ barrierGen.addTransitionBarrier(texD->handle, D3D12_RESOURCE_STATE_RENDER_TARGET);
+ else if (rbD)
+ barrierGen.addTransitionBarrier(rbD->handle, D3D12_RESOURCE_STATE_RENDER_TARGET);
+ if (resolveTexD)
+ barrierGen.addTransitionBarrier(resolveTexD->handle, D3D12_RESOURCE_STATE_RENDER_TARGET);
+ }
+ if (rtTex->m_desc.depthStencilBuffer()) {
+ QD3D12RenderBuffer *rbD = QRHI_RES(QD3D12RenderBuffer, rtTex->m_desc.depthStencilBuffer());
+ Q_ASSERT(rbD->m_type == QRhiRenderBuffer::DepthStencil);
+ barrierGen.addTransitionBarrier(rbD->handle, D3D12_RESOURCE_STATE_DEPTH_WRITE);
+ } else if (rtTex->m_desc.depthTexture()) {
+ QD3D12Texture *depthTexD = QRHI_RES(QD3D12Texture, rtTex->m_desc.depthTexture());
+ barrierGen.addTransitionBarrier(depthTexD->handle, D3D12_RESOURCE_STATE_DEPTH_WRITE);
+ }
+ barrierGen.enqueueBufferedTransitionBarriers(cbD);
+ } else {
+ Q_ASSERT(currentSwapChain);
+ barrierGen.addTransitionBarrier(currentSwapChain->sampleDesc.Count > 1
+ ? currentSwapChain->msaaBuffers[currentSwapChain->currentBackBufferIndex]
+ : currentSwapChain->colorBuffers[currentSwapChain->currentBackBufferIndex],
+ D3D12_RESOURCE_STATE_RENDER_TARGET);
+ barrierGen.enqueueBufferedTransitionBarriers(cbD);
+ }
+
+ cbD->cmdList->OMSetRenderTargets(UINT(rtD->colorAttCount),
+ rtD->rtv,
+ TRUE,
+ rtD->dsAttCount ? &rtD->dsv : nullptr);
+
+ if (rtD->colorAttCount && wantsColorClear) {
+ float clearColor[4] = {
+ colorClearValue.redF(),
+ colorClearValue.greenF(),
+ colorClearValue.blueF(),
+ colorClearValue.alphaF()
+ };
+ for (int i = 0; i < rtD->colorAttCount; ++i)
+ cbD->cmdList->ClearRenderTargetView(rtD->rtv[i], clearColor, 0, nullptr);
+ }
+ if (rtD->dsAttCount && wantsDsClear) {
+ cbD->cmdList->ClearDepthStencilView(rtD->dsv,
+ D3D12_CLEAR_FLAGS(D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL),
+ depthStencilClearValue.depthClearValue(),
+ UINT8(depthStencilClearValue.stencilClearValue()),
+ 0,
+ nullptr);
+ }
+
+ cbD->recordingPass = QD3D12CommandBuffer::RenderPass;
+ cbD->currentTarget = rt;
+
+ cbD->resetPerPassState();
+}
+
+void QRhiD3D12::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
+
+ if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
+ QD3D12TextureRenderTarget *rtTex = QRHI_RES(QD3D12TextureRenderTarget, cbD->currentTarget);
+ for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
+ it != itEnd; ++it)
+ {
+ const QRhiColorAttachment &colorAtt(*it);
+ if (!colorAtt.resolveTexture())
+ continue;
+
+ QD3D12Texture *dstTexD = QRHI_RES(QD3D12Texture, colorAtt.resolveTexture());
+ QD3D12Resource *dstRes = resourcePool.lookupRef(dstTexD->handle);
+ if (!dstRes)
+ continue;
+
+ QD3D12Texture *srcTexD = QRHI_RES(QD3D12Texture, colorAtt.texture());
+ QD3D12RenderBuffer *srcRbD = QRHI_RES(QD3D12RenderBuffer, colorAtt.renderBuffer());
+ Q_ASSERT(srcTexD || srcRbD);
+ QD3D12Resource *srcRes = resourcePool.lookupRef(srcTexD ? srcTexD->handle : srcRbD->handle);
+ if (!srcRes)
+ continue;
+
+ if (srcTexD) {
+ if (srcTexD->dxgiFormat != dstTexD->dxgiFormat) {
+ qWarning("Resolve source (%d) and destination (%d) formats do not match",
+ int(srcTexD->dxgiFormat), int(dstTexD->dxgiFormat));
+ 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 {
+ if (srcRbD->dxgiFormat != dstTexD->dxgiFormat) {
+ qWarning("Resolve source (%d) and destination (%d) formats do not match",
+ int(srcRbD->dxgiFormat), int(dstTexD->dxgiFormat));
+ continue;
+ }
+ if (srcRbD->m_pixelSize != dstTexD->m_pixelSize) {
+ qWarning("Resolve source and destination sizes do not match");
+ continue;
+ }
+ }
+
+ barrierGen.addTransitionBarrier(srcTexD ? srcTexD->handle : srcRbD->handle, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
+ barrierGen.addTransitionBarrier(dstTexD->handle, D3D12_RESOURCE_STATE_RESOLVE_DEST);
+ barrierGen.enqueueBufferedTransitionBarriers(cbD);
+
+ const UINT resolveCount = colorAtt.multiViewCount() >= 2 ? colorAtt.multiViewCount() : 1;
+ for (UINT resolveIdx = 0; resolveIdx < resolveCount; ++resolveIdx) {
+ const UINT srcSubresource = calcSubresource(0, UINT(colorAtt.layer()) + resolveIdx, 1);
+ const UINT dstSubresource = calcSubresource(UINT(colorAtt.resolveLevel()),
+ UINT(colorAtt.resolveLayer()) + resolveIdx,
+ dstTexD->mipLevelCount);
+ cbD->cmdList->ResolveSubresource(dstRes->resource, dstSubresource,
+ srcRes->resource, srcSubresource,
+ dstTexD->dxgiFormat);
+ }
+ }
+ if (rtTex->m_desc.depthResolveTexture())
+ qWarning("Resolving multisample depth-stencil buffers is not supported with D3D");
+ }
+
+ cbD->recordingPass = QD3D12CommandBuffer::NoPass;
+ cbD->currentTarget = nullptr;
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cbD, resourceUpdates);
+}
+
+void QRhiD3D12::beginComputePass(QRhiCommandBuffer *cb,
+ QRhiResourceUpdateBatch *resourceUpdates,
+ QRhiCommandBuffer::BeginPassFlags)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::NoPass);
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cbD, resourceUpdates);
+
+ cbD->recordingPass = QD3D12CommandBuffer::ComputePass;
+
+ cbD->resetPerPassState();
+}
+
+void QRhiD3D12::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::ComputePass);
+
+ cbD->recordingPass = QD3D12CommandBuffer::NoPass;
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cbD, resourceUpdates);
+}
+
+void QRhiD3D12::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::ComputePass);
+ QD3D12ComputePipeline *psD = QRHI_RES(QD3D12ComputePipeline, ps);
+ const bool pipelineChanged = cbD->currentComputePipeline != psD || cbD->currentPipelineGeneration != psD->generation;
+
+ if (pipelineChanged) {
+ cbD->currentGraphicsPipeline = nullptr;
+ cbD->currentComputePipeline = psD;
+ cbD->currentPipelineGeneration = psD->generation;
+
+ if (QD3D12Pipeline *pipeline = pipelinePool.lookupRef(psD->handle)) {
+ Q_ASSERT(pipeline->type == QD3D12Pipeline::Compute);
+ cbD->cmdList->SetPipelineState(pipeline->pso);
+ if (QD3D12RootSignature *rs = rootSignaturePool.lookupRef(psD->rootSigHandle))
+ cbD->cmdList->SetComputeRootSignature(rs->rootSig);
+ }
+ }
+}
+
+void QRhiD3D12::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
+{
+ QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::ComputePass);
+ cbD->cmdList->Dispatch(UINT(x), UINT(y), UINT(z));
+}
+
+bool QD3D12DescriptorHeap::create(ID3D12Device *device,
+ quint32 descriptorCount,
+ D3D12_DESCRIPTOR_HEAP_TYPE heapType,
+ D3D12_DESCRIPTOR_HEAP_FLAGS heapFlags)
+{
+ head = 0;
+ capacity = descriptorCount;
+ this->heapType = heapType;
+ this->heapFlags = heapFlags;
+
+ D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
+ heapDesc.Type = heapType;
+ heapDesc.NumDescriptors = capacity;
+ heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAGS(heapFlags);
+
+ HRESULT hr = device->CreateDescriptorHeap(&heapDesc, __uuidof(ID3D12DescriptorHeap), reinterpret_cast<void **>(&heap));
+ if (FAILED(hr)) {
+ qWarning("Failed to create descriptor heap: %s", qPrintable(QSystemError::windowsComString(hr)));
+ heap = nullptr;
+ capacity = descriptorByteSize = 0;
+ return false;
+ }
+
+ descriptorByteSize = device->GetDescriptorHandleIncrementSize(heapType);
+ heapStart.cpuHandle = heap->GetCPUDescriptorHandleForHeapStart();
+ if (heapFlags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)
+ heapStart.gpuHandle = heap->GetGPUDescriptorHandleForHeapStart();
+
+ return true;
+}
+
+void QD3D12DescriptorHeap::createWithExisting(const QD3D12DescriptorHeap &other,
+ quint32 offsetInDescriptors,
+ quint32 descriptorCount)
+{
+ heap = nullptr;
+ head = 0;
+ capacity = descriptorCount;
+ heapType = other.heapType;
+ heapFlags = other.heapFlags;
+ descriptorByteSize = other.descriptorByteSize;
+ heapStart = incremented(other.heapStart, offsetInDescriptors);
+}
+
+void QD3D12DescriptorHeap::destroy()
+{
+ if (heap) {
+ heap->Release();
+ heap = nullptr;
+ }
+ capacity = 0;
+}
+
+void QD3D12DescriptorHeap::destroyWithDeferredRelease(QD3D12ReleaseQueue *releaseQueue)
+{
+ if (heap) {
+ releaseQueue->deferredReleaseDescriptorHeap(heap);
+ heap = nullptr;
+ }
+ capacity = 0;
+}
+
+QD3D12Descriptor QD3D12DescriptorHeap::get(quint32 count)
+{
+ Q_ASSERT(count > 0);
+ if (head + count > capacity) {
+ qWarning("Cannot get %u descriptors as that would exceed capacity %u", count, capacity);
+ return {};
+ }
+ head += count;
+ return at(head - count);
+}
+
+QD3D12Descriptor QD3D12DescriptorHeap::at(quint32 index) const
+{
+ const quint32 startOffset = index * descriptorByteSize;
+ QD3D12Descriptor result;
+ result.cpuHandle.ptr = heapStart.cpuHandle.ptr + startOffset;
+ if (heapStart.gpuHandle.ptr != 0)
+ result.gpuHandle.ptr = heapStart.gpuHandle.ptr + startOffset;
+ return result;
+}
+
+bool QD3D12CpuDescriptorPool::create(ID3D12Device *device, D3D12_DESCRIPTOR_HEAP_TYPE heapType, const char *debugName)
+{
+ QD3D12DescriptorHeap firstHeap;
+ if (!firstHeap.create(device, DESCRIPTORS_PER_HEAP, heapType, D3D12_DESCRIPTOR_HEAP_FLAG_NONE))
+ return false;
+ heaps.append(HeapWithMap::init(firstHeap, DESCRIPTORS_PER_HEAP));
+ descriptorByteSize = heaps[0].heap.descriptorByteSize;
+ this->device = device;
+ this->debugName = debugName;
+ return true;
+}
+
+void QD3D12CpuDescriptorPool::destroy()
+{
+#ifndef QT_NO_DEBUG
+ // debug builds: just do it always
+ static bool leakCheck = true;
+#else
+ // release builds: opt-in
+ static bool leakCheck = qEnvironmentVariableIntValue("QT_RHI_LEAK_CHECK");
+#endif
+ if (leakCheck) {
+ for (HeapWithMap &heap : heaps) {
+ const int leakedDescriptorCount = heap.map.count(true);
+ if (leakedDescriptorCount > 0) {
+ qWarning("QD3D12CpuDescriptorPool::destroy(): "
+ "Heap %p for descriptor pool %p '%s' has %d unreleased descriptors",
+ &heap.heap, this, debugName, leakedDescriptorCount);
+ }
+ }
+ }
+ for (HeapWithMap &heap : heaps)
+ heap.heap.destroy();
+ heaps.clear();
+}
+
+QD3D12Descriptor QD3D12CpuDescriptorPool::allocate(quint32 count)
+{
+ Q_ASSERT(count > 0 && count <= DESCRIPTORS_PER_HEAP);
+
+ HeapWithMap &last(heaps.last());
+ if (last.heap.head + count <= last.heap.capacity) {
+ quint32 firstIndex = last.heap.head;
+ for (quint32 i = 0; i < count; ++i)
+ last.map.setBit(firstIndex + i);
+ return last.heap.get(count);
+ }
+
+ for (HeapWithMap &heap : heaps) {
+ quint32 freeCount = 0;
+ for (quint32 i = 0; i < DESCRIPTORS_PER_HEAP; ++i) {
+ if (heap.map.testBit(i)) {
+ freeCount = 0;
+ } else {
+ freeCount += 1;
+ if (freeCount == count) {
+ quint32 firstIndex = i - (freeCount - 1);
+ for (quint32 j = 0; j < count; ++j) {
+ heap.map.setBit(firstIndex + j);
+ return heap.heap.at(firstIndex);
+ }
+ }
+ }
+ }
+ }
+
+ QD3D12DescriptorHeap newHeap;
+ if (!newHeap.create(device, DESCRIPTORS_PER_HEAP, last.heap.heapType, last.heap.heapFlags))
+ return {};
+
+ heaps.append(HeapWithMap::init(newHeap, DESCRIPTORS_PER_HEAP));
+
+ for (quint32 i = 0; i < count; ++i)
+ heaps.last().map.setBit(i);
+
+ return heaps.last().heap.get(count);
+}
+
+void QD3D12CpuDescriptorPool::release(const QD3D12Descriptor &descriptor, quint32 count)
+{
+ Q_ASSERT(count > 0 && count <= DESCRIPTORS_PER_HEAP);
+ if (!descriptor.isValid())
+ return;
+
+ const SIZE_T addr = descriptor.cpuHandle.ptr;
+ for (HeapWithMap &heap : heaps) {
+ const SIZE_T begin = heap.heap.heapStart.cpuHandle.ptr;
+ const SIZE_T end = begin + heap.heap.descriptorByteSize * heap.heap.capacity;
+ if (addr >= begin && addr < end) {
+ quint32 firstIndex = (addr - begin) / heap.heap.descriptorByteSize;
+ for (quint32 i = 0; i < count; ++i)
+ heap.map.setBit(firstIndex + i, false);
+ return;
+ }
+ }
+
+ qWarning("QD3D12CpuDescriptorPool::release: Descriptor with address %llu is not in any heap",
+ quint64(descriptor.cpuHandle.ptr));
+}
+
+bool QD3D12QueryHeap::create(ID3D12Device *device,
+ quint32 queryCount,
+ D3D12_QUERY_HEAP_TYPE heapType)
+{
+ capacity = queryCount;
+
+ D3D12_QUERY_HEAP_DESC heapDesc = {};
+ heapDesc.Type = heapType;
+ heapDesc.Count = capacity;
+
+ HRESULT hr = device->CreateQueryHeap(&heapDesc, __uuidof(ID3D12QueryHeap), reinterpret_cast<void **>(&heap));
+ if (FAILED(hr)) {
+ qWarning("Failed to create query heap: %s", qPrintable(QSystemError::windowsComString(hr)));
+ heap = nullptr;
+ capacity = 0;
+ return false;
+ }
+
+ return true;
+}
+
+void QD3D12QueryHeap::destroy()
+{
+ if (heap) {
+ heap->Release();
+ heap = nullptr;
+ }
+ capacity = 0;
+}
+
+bool QD3D12StagingArea::create(QRhiD3D12 *rhi, quint32 capacity, D3D12_HEAP_TYPE heapType)
+{
+ Q_ASSERT(heapType == D3D12_HEAP_TYPE_UPLOAD || heapType == D3D12_HEAP_TYPE_READBACK);
+ D3D12_RESOURCE_DESC resourceDesc = {};
+ resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ resourceDesc.Width = capacity;
+ resourceDesc.Height = 1;
+ resourceDesc.DepthOrArraySize = 1;
+ resourceDesc.MipLevels = 1;
+ resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
+ resourceDesc.SampleDesc = { 1, 0 };
+ resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+ resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
+ UINT state = heapType == D3D12_HEAP_TYPE_UPLOAD ? D3D12_RESOURCE_STATE_GENERIC_READ : D3D12_RESOURCE_STATE_COPY_DEST;
+ HRESULT hr = rhi->vma.createResource(heapType,
+ &resourceDesc,
+ D3D12_RESOURCE_STATES(state),
+ nullptr,
+ &allocation,
+ __uuidof(ID3D12Resource),
+ reinterpret_cast<void **>(&resource));
+ if (FAILED(hr)) {
+ qWarning("Failed to create buffer for staging area: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ void *p = nullptr;
+ hr = resource->Map(0, nullptr, &p);
+ if (FAILED(hr)) {
+ qWarning("Failed to map buffer for staging area: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ destroy();
+ return false;
+ }
+
+ mem.p = static_cast<quint8 *>(p);
+ mem.gpuAddr = resource->GetGPUVirtualAddress();
+ mem.buffer = resource;
+ mem.bufferOffset = 0;
+
+ this->capacity = capacity;
+ head = 0;
+
+ return true;
+}
+
+void QD3D12StagingArea::destroy()
+{
+ if (resource) {
+ resource->Release();
+ resource = nullptr;
+ }
+ if (allocation) {
+ allocation->Release();
+ allocation = nullptr;
+ }
+ mem = {};
+}
+
+void QD3D12StagingArea::destroyWithDeferredRelease(QD3D12ReleaseQueue *releaseQueue)
+{
+ if (resource)
+ releaseQueue->deferredReleaseResourceAndAllocation(resource, allocation);
+ mem = {};
+}
+
+QD3D12StagingArea::Allocation QD3D12StagingArea::get(quint32 byteSize)
+{
+ const quint32 allocSize = aligned(byteSize, ALIGNMENT);
+ if (head + allocSize > capacity) {
+ qWarning("Failed to allocate %u (%u) bytes from staging area of size %u with %u bytes left",
+ allocSize, byteSize, capacity, remainingCapacity());
+ return {};
+ }
+ const quint32 offset = head;
+ head += allocSize;
+ return {
+ mem.p + offset,
+ mem.gpuAddr + offset,
+ mem.buffer,
+ offset
+ };
+}
+
+// Can be called inside and outside of begin-endFrame. Removes from the pool
+// and releases the underlying native resource only in the frames_in_flight'th
+// beginFrame() counted starting from the next endFrame().
+void QD3D12ReleaseQueue::deferredReleaseResource(const QD3D12ObjectHandle &handle)
+{
+ DeferredReleaseEntry e;
+ e.handle = handle;
+ queue.append(e);
+}
+
+void QD3D12ReleaseQueue::deferredReleaseResourceWithViews(const QD3D12ObjectHandle &handle,
+ QD3D12CpuDescriptorPool *pool,
+ const QD3D12Descriptor &viewsStart,
+ int viewCount)
+{
+ DeferredReleaseEntry e;
+ e.type = DeferredReleaseEntry::Resource;
+ e.handle = handle;
+ e.poolForViews = pool;
+ e.viewsStart = viewsStart;
+ e.viewCount = viewCount;
+ queue.append(e);
+}
+
+void QD3D12ReleaseQueue::deferredReleasePipeline(const QD3D12ObjectHandle &handle)
+{
+ DeferredReleaseEntry e;
+ e.type = DeferredReleaseEntry::Pipeline;
+ e.handle = handle;
+ queue.append(e);
+}
+
+void QD3D12ReleaseQueue::deferredReleaseRootSignature(const QD3D12ObjectHandle &handle)
+{
+ DeferredReleaseEntry e;
+ e.type = DeferredReleaseEntry::RootSignature;
+ e.handle = handle;
+ queue.append(e);
+}
+
+void QD3D12ReleaseQueue::deferredReleaseCallback(std::function<void(void*)> callback, void *userData)
+{
+ DeferredReleaseEntry e;
+ e.type = DeferredReleaseEntry::Callback;
+ e.callback = callback;
+ e.callbackUserData = userData;
+ queue.append(e);
+}
+
+void QD3D12ReleaseQueue::deferredReleaseResourceAndAllocation(ID3D12Resource *resource,
+ D3D12MA::Allocation *allocation)
+{
+ DeferredReleaseEntry e;
+ e.type = DeferredReleaseEntry::ResourceAndAllocation;
+ e.resourceAndAllocation = { resource, allocation };
+ queue.append(e);
+}
+
+void QD3D12ReleaseQueue::deferredReleaseDescriptorHeap(ID3D12DescriptorHeap *heap)
+{
+ DeferredReleaseEntry e;
+ e.type = DeferredReleaseEntry::DescriptorHeap;
+ e.descriptorHeap = heap;
+ queue.append(e);
+}
+
+void QD3D12ReleaseQueue::deferredReleaseViews(QD3D12CpuDescriptorPool *pool,
+ const QD3D12Descriptor &viewsStart,
+ int viewCount)
+{
+ DeferredReleaseEntry e;
+ e.type = DeferredReleaseEntry::Views;
+ e.poolForViews = pool;
+ e.viewsStart = viewsStart;
+ e.viewCount = viewCount;
+ queue.append(e);
+}
+
+void QD3D12ReleaseQueue::activatePendingDeferredReleaseRequests(int frameSlot)
+{
+ for (DeferredReleaseEntry &e : queue) {
+ if (!e.frameSlotToBeReleasedIn.has_value())
+ e.frameSlotToBeReleasedIn = frameSlot;
+ }
+}
+
+void QD3D12ReleaseQueue::executeDeferredReleases(int frameSlot, bool forced)
+{
+ for (int i = queue.count() - 1; i >= 0; --i) {
+ const DeferredReleaseEntry &e(queue[i]);
+ if (forced || (e.frameSlotToBeReleasedIn.has_value() && e.frameSlotToBeReleasedIn.value() == frameSlot)) {
+ switch (e.type) {
+ case DeferredReleaseEntry::Resource:
+ resourcePool->remove(e.handle);
+ if (e.poolForViews && e.viewsStart.isValid() && e.viewCount > 0)
+ e.poolForViews->release(e.viewsStart, e.viewCount);
+ break;
+ case DeferredReleaseEntry::Pipeline:
+ pipelinePool->remove(e.handle);
+ break;
+ case DeferredReleaseEntry::RootSignature:
+ rootSignaturePool->remove(e.handle);
+ break;
+ case DeferredReleaseEntry::Callback:
+ e.callback(e.callbackUserData);
+ break;
+ case DeferredReleaseEntry::ResourceAndAllocation:
+ // order matters: resource first, then the allocation (which
+ // may be null)
+ e.resourceAndAllocation.first->Release();
+ if (e.resourceAndAllocation.second)
+ e.resourceAndAllocation.second->Release();
+ break;
+ case DeferredReleaseEntry::DescriptorHeap:
+ e.descriptorHeap->Release();
+ break;
+ case DeferredReleaseEntry::Views:
+ e.poolForViews->release(e.viewsStart, e.viewCount);
+ break;
+ }
+ queue.removeAt(i);
+ }
+ }
+}
+
+void QD3D12ReleaseQueue::releaseAll()
+{
+ executeDeferredReleases(0, true);
+}
+
+void QD3D12ResourceBarrierGenerator::addTransitionBarrier(const QD3D12ObjectHandle &resourceHandle,
+ D3D12_RESOURCE_STATES stateAfter)
+{
+ if (QD3D12Resource *res = resourcePool->lookupRef(resourceHandle)) {
+ if (stateAfter != res->state) {
+ transitionResourceBarriers.append({ resourceHandle, res->state, stateAfter });
+ res->state = stateAfter;
+ }
+ }
+}
+
+void QD3D12ResourceBarrierGenerator::enqueueBufferedTransitionBarriers(QD3D12CommandBuffer *cbD)
+{
+ QVarLengthArray<D3D12_RESOURCE_BARRIER, PREALLOC> barriers;
+ for (const TransitionResourceBarrier &trb : transitionResourceBarriers) {
+ if (QD3D12Resource *res = resourcePool->lookupRef(trb.resourceHandle)) {
+ D3D12_RESOURCE_BARRIER barrier = {};
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+ barrier.Transition.pResource = res->resource;
+ barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+ barrier.Transition.StateBefore = trb.stateBefore;
+ barrier.Transition.StateAfter = trb.stateAfter;
+ barriers.append(barrier);
+ }
+ }
+ transitionResourceBarriers.clear();
+ if (!barriers.isEmpty())
+ cbD->cmdList->ResourceBarrier(barriers.count(), barriers.constData());
+}
+
+void QD3D12ResourceBarrierGenerator::enqueueSubresourceTransitionBarrier(QD3D12CommandBuffer *cbD,
+ const QD3D12ObjectHandle &resourceHandle,
+ UINT subresource,
+ D3D12_RESOURCE_STATES stateBefore,
+ D3D12_RESOURCE_STATES stateAfter)
+{
+ if (QD3D12Resource *res = resourcePool->lookupRef(resourceHandle)) {
+ D3D12_RESOURCE_BARRIER barrier = {};
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+ barrier.Transition.pResource = res->resource;
+ barrier.Transition.Subresource = subresource;
+ barrier.Transition.StateBefore = stateBefore;
+ barrier.Transition.StateAfter = stateAfter;
+ cbD->cmdList->ResourceBarrier(1, &barrier);
+ }
+}
+
+void QD3D12ResourceBarrierGenerator::enqueueUavBarrier(QD3D12CommandBuffer *cbD,
+ const QD3D12ObjectHandle &resourceHandle)
+{
+ if (QD3D12Resource *res = resourcePool->lookupRef(resourceHandle)) {
+ D3D12_RESOURCE_BARRIER barrier = {};
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
+ barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+ barrier.UAV.pResource = res->resource;
+ cbD->cmdList->ResourceBarrier(1, &barrier);
+ }
+}
+
+void QD3D12ShaderBytecodeCache::insertWithCapacityLimit(const QRhiShaderStage &key, const Shader &s)
+{
+ if (data.count() >= QRhiD3D12::MAX_SHADER_CACHE_ENTRIES)
+ data.clear();
+ data.insert(key, s);
+}
+
+bool QD3D12ShaderVisibleDescriptorHeap::create(ID3D12Device *device,
+ D3D12_DESCRIPTOR_HEAP_TYPE type,
+ quint32 perFrameDescriptorCount)
+{
+ Q_ASSERT(type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV || type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+
+ quint32 size = perFrameDescriptorCount * QD3D12_FRAMES_IN_FLIGHT;
+
+ // https://learn.microsoft.com/en-us/windows/win32/direct3d12/hardware-support
+ const quint32 CBV_SRV_UAV_MAX = 1000000;
+ const quint32 SAMPLER_MAX = 2048;
+ if (type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)
+ size = qMin(size, CBV_SRV_UAV_MAX);
+ else if (type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER)
+ size = qMin(size, SAMPLER_MAX);
+
+ if (!heap.create(device, size, type, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)) {
+ qWarning("Failed to create shader-visible descriptor heap of size %u", size);
+ return false;
+ }
+
+ perFrameDescriptorCount = size / QD3D12_FRAMES_IN_FLIGHT;
+ quint32 currentOffsetInDescriptors = 0;
+ for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
+ perFrameHeapSlice[i].createWithExisting(heap, currentOffsetInDescriptors, perFrameDescriptorCount);
+ currentOffsetInDescriptors += perFrameDescriptorCount;
+ }
+
+ return true;
+}
+
+void QD3D12ShaderVisibleDescriptorHeap::destroy()
+{
+ heap.destroy();
+}
+
+void QD3D12ShaderVisibleDescriptorHeap::destroyWithDeferredRelease(QD3D12ReleaseQueue *releaseQueue)
+{
+ heap.destroyWithDeferredRelease(releaseQueue);
+}
+
+static inline QPair<int, int> mapBinding(int binding, const QShader::NativeResourceBindingMap &map)
+{
+ if (map.isEmpty())
+ return { binding, binding }; // assume 1:1 mapping
+
+ auto it = map.constFind(binding);
+ if (it != map.cend())
+ return *it;
+
+ // Hitting this path is normal too. It is not given that the resource is
+ // present in the shaders for all the stages specified by the visibility
+ // mask in the QRhiShaderResourceBinding.
+ return { -1, -1 };
+}
+
+void QD3D12ShaderResourceVisitor::visit()
+{
+ for (int bindingIdx = 0, bindingCount = srb->m_bindings.count(); bindingIdx != bindingCount; ++bindingIdx) {
+ const QRhiShaderResourceBinding &b(srb->m_bindings[bindingIdx]);
+ const QRhiShaderResourceBinding::Data *bd = QRhiImplementation::shaderResourceBindingData(b);
+
+ for (int stageIdx = 0; stageIdx < stageCount; ++stageIdx) {
+ const QD3D12ShaderStageData *sd = &stageData[stageIdx];
+ if (!sd->valid)
+ continue;
+
+ if (!bd->stage.testFlag(qd3d12_stageToSrb(sd->stage)))
+ continue;
+
+ switch (bd->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ const int shaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
+ if (shaderRegister >= 0 && uniformBuffer)
+ uniformBuffer(sd->stage, bd->u.ubuf, shaderRegister, bd->binding);
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ Q_ASSERT(bd->u.stex.count > 0);
+ const int textureBaseShaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
+ const int samplerBaseShaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).second;
+ for (int i = 0; i < bd->u.stex.count; ++i) {
+ if (textureBaseShaderRegister >= 0 && texture)
+ texture(sd->stage, bd->u.stex.texSamplers[i], textureBaseShaderRegister + i);
+ if (samplerBaseShaderRegister >= 0 && sampler)
+ sampler(sd->stage, bd->u.stex.texSamplers[i], samplerBaseShaderRegister + i);
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::Texture:
+ {
+ Q_ASSERT(bd->u.stex.count > 0);
+ const int baseShaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
+ if (baseShaderRegister >= 0 && texture) {
+ for (int i = 0; i < bd->u.stex.count; ++i)
+ texture(sd->stage, bd->u.stex.texSamplers[i], baseShaderRegister + i);
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::Sampler:
+ {
+ Q_ASSERT(bd->u.stex.count > 0);
+ const int baseShaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
+ if (baseShaderRegister >= 0 && sampler) {
+ for (int i = 0; i < bd->u.stex.count; ++i)
+ sampler(sd->stage, bd->u.stex.texSamplers[i], baseShaderRegister + i);
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::ImageLoad:
+ {
+ const int shaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
+ if (shaderRegister >= 0 && storageImage)
+ storageImage(sd->stage, bd->u.simage, Load, shaderRegister);
+ }
+ break;
+ case QRhiShaderResourceBinding::ImageStore:
+ {
+ const int shaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
+ if (shaderRegister >= 0 && storageImage)
+ storageImage(sd->stage, bd->u.simage, Store, shaderRegister);
+ }
+ break;
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ {
+ const int shaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
+ if (shaderRegister >= 0 && storageImage)
+ storageImage(sd->stage, bd->u.simage, LoadStore, shaderRegister);
+ }
+ break;
+ case QRhiShaderResourceBinding::BufferLoad:
+ {
+ const int shaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
+ if (shaderRegister >= 0 && storageBuffer)
+ storageBuffer(sd->stage, bd->u.sbuf, Load, shaderRegister);
+ }
+ break;
+ case QRhiShaderResourceBinding::BufferStore:
+ {
+ const int shaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
+ if (shaderRegister >= 0 && storageBuffer)
+ storageBuffer(sd->stage, bd->u.sbuf, Store, shaderRegister);
+ }
+ break;
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ {
+ const int shaderRegister = mapBinding(bd->binding, sd->nativeResourceBindingMap).first;
+ if (shaderRegister >= 0 && storageBuffer)
+ storageBuffer(sd->stage, bd->u.sbuf, LoadStore, shaderRegister);
+ }
+ break;
+ }
+ }
+ }
+}
+
+bool QD3D12SamplerManager::create(ID3D12Device *device)
+{
+ // This does not need to be per-frame slot, just grab space for MAX_SAMPLERS samplers.
+ if (!shaderVisibleSamplerHeap.create(device,
+ D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
+ MAX_SAMPLERS / QD3D12_FRAMES_IN_FLIGHT))
+ {
+ qWarning("Could not create shader-visible SAMPLER heap");
+ return false;
+ }
+
+ this->device = device;
+ return true;
+}
+
+void QD3D12SamplerManager::destroy()
+{
+ if (device) {
+ shaderVisibleSamplerHeap.destroy();
+ device = nullptr;
+ }
+}
+
+QD3D12Descriptor QD3D12SamplerManager::getShaderVisibleDescriptor(const D3D12_SAMPLER_DESC &desc)
+{
+ auto it = gpuMap.constFind({desc});
+ if (it != gpuMap.cend())
+ return *it;
+
+ QD3D12Descriptor descriptor = shaderVisibleSamplerHeap.heap.get(1);
+ if (descriptor.isValid()) {
+ device->CreateSampler(&desc, descriptor.cpuHandle);
+ gpuMap.insert({desc}, descriptor);
+ } else {
+ qWarning("Out of shader-visible SAMPLER descriptor heap space,"
+ " this should not happen, maximum number of unique samplers is %u",
+ shaderVisibleSamplerHeap.heap.capacity);
+ }
+
+ return descriptor;
+}
+
+bool QD3D12MipmapGenerator::create(QRhiD3D12 *rhiD)
+{
+ this->rhiD = rhiD;
+
+ D3D12_ROOT_PARAMETER1 rootParams[3] = {};
+ D3D12_DESCRIPTOR_RANGE1 descriptorRanges[2] = {};
+
+ // b0
+ rootParams[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+ rootParams[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+ rootParams[0].Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC;
+
+ // t0
+ descriptorRanges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+ descriptorRanges[0].NumDescriptors = 1;
+ descriptorRanges[0].Flags = D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE;
+ rootParams[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ rootParams[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+ rootParams[1].DescriptorTable.NumDescriptorRanges = 1;
+ rootParams[1].DescriptorTable.pDescriptorRanges = &descriptorRanges[0];
+
+ // u0..3
+ descriptorRanges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
+ descriptorRanges[1].NumDescriptors = 4;
+ rootParams[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ rootParams[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+ rootParams[2].DescriptorTable.NumDescriptorRanges = 1;
+ rootParams[2].DescriptorTable.pDescriptorRanges = &descriptorRanges[1];
+
+ // s0
+ D3D12_STATIC_SAMPLER_DESC samplerDesc = {};
+ samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
+ samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+ samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+ samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+ samplerDesc.MaxLOD = 10000.0f;
+ samplerDesc.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+
+ D3D12_VERSIONED_ROOT_SIGNATURE_DESC rsDesc = {};
+ rsDesc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1;
+ rsDesc.Desc_1_1.NumParameters = 3;
+ rsDesc.Desc_1_1.pParameters = rootParams;
+ rsDesc.Desc_1_1.NumStaticSamplers = 1;
+ rsDesc.Desc_1_1.pStaticSamplers = &samplerDesc;
+
+ ID3DBlob *signature = nullptr;
+ HRESULT hr = D3D12SerializeVersionedRootSignature(&rsDesc, &signature, nullptr);
+ if (FAILED(hr)) {
+ qWarning("Failed to serialize root signature: %s", qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ ID3D12RootSignature *rootSig = nullptr;
+ hr = rhiD->dev->CreateRootSignature(0,
+ signature->GetBufferPointer(),
+ signature->GetBufferSize(),
+ __uuidof(ID3D12RootSignature),
+ reinterpret_cast<void **>(&rootSig));
+ signature->Release();
+ if (FAILED(hr)) {
+ qWarning("Failed to create root signature: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+
+ rootSigHandle = QD3D12RootSignature::addToPool(&rhiD->rootSignaturePool, rootSig);
+
+ D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {};
+ psoDesc.pRootSignature = rootSig;
+ psoDesc.CS.pShaderBytecode = g_csMipmap;
+ psoDesc.CS.BytecodeLength = sizeof(g_csMipmap);
+ ID3D12PipelineState *pso = nullptr;
+ hr = rhiD->dev->CreateComputePipelineState(&psoDesc,
+ __uuidof(ID3D12PipelineState),
+ reinterpret_cast<void **>(&pso));
+ if (FAILED(hr)) {
+ qWarning("Failed to create compute pipeline state: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ rhiD->rootSignaturePool.remove(rootSigHandle);
+ rootSigHandle = {};
+ return false;
+ }
+
+ pipelineHandle = QD3D12Pipeline::addToPool(&rhiD->pipelinePool, QD3D12Pipeline::Compute, pso);
+
+ return true;
+}
+
+void QD3D12MipmapGenerator::destroy()
+{
+ rhiD->pipelinePool.remove(pipelineHandle);
+ pipelineHandle = {};
+ rhiD->rootSignaturePool.remove(rootSigHandle);
+ rootSigHandle = {};
+}
+
+void QD3D12MipmapGenerator::generate(QD3D12CommandBuffer *cbD, const QD3D12ObjectHandle &textureHandle)
+{
+ QD3D12Pipeline *pipeline = rhiD->pipelinePool.lookupRef(pipelineHandle);
+ if (!pipeline)
+ return;
+ QD3D12RootSignature *rootSig = rhiD->rootSignaturePool.lookupRef(rootSigHandle);
+ if (!rootSig)
+ return;
+ QD3D12Resource *res = rhiD->resourcePool.lookupRef(textureHandle);
+ if (!res)
+ return;
+
+ const quint32 mipLevelCount = res->desc.MipLevels;
+ if (mipLevelCount < 2)
+ return;
+
+ if (res->desc.SampleDesc.Count > 1) {
+ qWarning("Cannot generate mipmaps for MSAA texture");
+ return;
+ }
+
+ const bool is1D = res->desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE1D;
+ if (is1D) {
+ qWarning("Cannot generate mipmaps for 1D texture");
+ return;
+ }
+
+ const bool is3D = res->desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D;
+ const bool isCubeOrArray = res->desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D
+ && res->desc.DepthOrArraySize > 1;
+ const quint32 layerCount = isCubeOrArray ? res->desc.DepthOrArraySize : 1;
+
+ if (is3D) {
+ // ### needs its own shader and maybe a different solution
+ qWarning("3D texture mipmapping is not implemented for D3D12 atm");
+ return;
+ }
+
+ rhiD->barrierGen.addTransitionBarrier(textureHandle, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
+ rhiD->barrierGen.enqueueBufferedTransitionBarriers(cbD);
+
+ cbD->cmdList->SetPipelineState(pipeline->pso);
+ cbD->cmdList->SetComputeRootSignature(rootSig->rootSig);
+
+ const quint32 descriptorByteSize = rhiD->shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[rhiD->currentFrameSlot].descriptorByteSize;
+
+ struct CBufData {
+ quint32 srcMipLevel;
+ quint32 numMipLevels;
+ float texelWidth;
+ float texelHeight;
+ };
+
+ const quint32 allocSize = QD3D12StagingArea::allocSizeForArray(sizeof(CBufData), mipLevelCount * layerCount);
+ std::optional<QD3D12StagingArea> ownStagingArea;
+ if (rhiD->smallStagingAreas[rhiD->currentFrameSlot].remainingCapacity() < allocSize) {
+ ownStagingArea = QD3D12StagingArea();
+ if (!ownStagingArea->create(rhiD, allocSize, D3D12_HEAP_TYPE_UPLOAD)) {
+ qWarning("Could not create staging area for mipmap generation");
+ return;
+ }
+ }
+ QD3D12StagingArea *workArea = ownStagingArea.has_value()
+ ? &ownStagingArea.value()
+ : &rhiD->smallStagingAreas[rhiD->currentFrameSlot];
+
+ bool gotNewHeap = false;
+ if (!rhiD->ensureShaderVisibleDescriptorHeapCapacity(&rhiD->shaderVisibleCbvSrvUavHeap,
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
+ rhiD->currentFrameSlot,
+ (1 + 4) * mipLevelCount * layerCount,
+ &gotNewHeap))
+ {
+ qWarning("Could not ensure enough space in descriptor heap for mipmap generation");
+ return;
+ }
+ if (gotNewHeap)
+ rhiD->bindShaderVisibleHeaps(cbD);
+
+ for (quint32 layer = 0; layer < layerCount; ++layer) {
+ for (quint32 level = 0; level < mipLevelCount ;) {
+ UINT subresource = calcSubresource(level, layer, res->desc.MipLevels);
+ rhiD->barrierGen.enqueueSubresourceTransitionBarrier(cbD, textureHandle, subresource,
+ D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
+ D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
+
+ quint32 levelPlusOneMipWidth = res->desc.Width >> (level + 1);
+ quint32 levelPlusOneMipHeight = res->desc.Height >> (level + 1);
+ const quint32 dw = levelPlusOneMipWidth == 1 ? levelPlusOneMipHeight : levelPlusOneMipWidth;
+ const quint32 dh = levelPlusOneMipHeight == 1 ? levelPlusOneMipWidth : levelPlusOneMipHeight;
+ // number of times the size can be halved while still resulting in an even dimension
+ const quint32 additionalMips = qCountTrailingZeroBits(dw | dh);
+ const quint32 numGenMips = qMin(1u + qMin(3u, additionalMips), res->desc.MipLevels - level);
+ levelPlusOneMipWidth = qMax(1u, levelPlusOneMipWidth);
+ levelPlusOneMipHeight = qMax(1u, levelPlusOneMipHeight);
+
+ CBufData cbufData = {
+ level,
+ numGenMips,
+ 1.0f / float(levelPlusOneMipWidth),
+ 1.0f / float(levelPlusOneMipHeight)
+ };
+
+ QD3D12StagingArea::Allocation cbuf = workArea->get(sizeof(cbufData));
+ memcpy(cbuf.p, &cbufData, sizeof(cbufData));
+ cbD->cmdList->SetComputeRootConstantBufferView(0, cbuf.gpuAddr);
+
+ QD3D12Descriptor srv = rhiD->shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[rhiD->currentFrameSlot].get(1);
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
+ srvDesc.Format = res->desc.Format;
+ srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+ if (isCubeOrArray) {
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
+ srvDesc.Texture2DArray.MostDetailedMip = level;
+ srvDesc.Texture2DArray.MipLevels = 1;
+ srvDesc.Texture2DArray.FirstArraySlice = layer;
+ srvDesc.Texture2DArray.ArraySize = 1;
+ } else if (is3D) {
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
+ srvDesc.Texture3D.MostDetailedMip = level;
+ srvDesc.Texture3D.MipLevels = 1;
+ } else {
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+ srvDesc.Texture2D.MostDetailedMip = level;
+ srvDesc.Texture2D.MipLevels = 1;
+ }
+ rhiD->dev->CreateShaderResourceView(res->resource, &srvDesc, srv.cpuHandle);
+ cbD->cmdList->SetComputeRootDescriptorTable(1, srv.gpuHandle);
+
+ QD3D12Descriptor uavStart = rhiD->shaderVisibleCbvSrvUavHeap.perFrameHeapSlice[rhiD->currentFrameSlot].get(4);
+ D3D12_CPU_DESCRIPTOR_HANDLE uavCpuHandle = uavStart.cpuHandle;
+ // if level is N, then need UAVs for levels N+1, ..., N+4
+ for (quint32 uavIdx = 0; uavIdx < 4; ++uavIdx) {
+ const quint32 uavMipLevel = qMin(level + 1u + uavIdx, res->desc.MipLevels - 1u);
+ D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
+ uavDesc.Format = res->desc.Format;
+ if (isCubeOrArray) {
+ uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
+ uavDesc.Texture2DArray.MipSlice = uavMipLevel;
+ uavDesc.Texture2DArray.FirstArraySlice = layer;
+ uavDesc.Texture2DArray.ArraySize = 1;
+ } else if (is3D) {
+ uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
+ uavDesc.Texture3D.MipSlice = uavMipLevel;
+ uavDesc.Texture3D.FirstWSlice = 0; // depth etc. not implemented yet
+ uavDesc.Texture3D.WSize = 1;
+ } else {
+ uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
+ uavDesc.Texture2D.MipSlice = uavMipLevel;
+ }
+ rhiD->dev->CreateUnorderedAccessView(res->resource, nullptr, &uavDesc, uavCpuHandle);
+ uavCpuHandle.ptr += descriptorByteSize;
+ }
+ cbD->cmdList->SetComputeRootDescriptorTable(2, uavStart.gpuHandle);
+
+ cbD->cmdList->Dispatch(levelPlusOneMipWidth, levelPlusOneMipHeight, 1);
+
+ rhiD->barrierGen.enqueueUavBarrier(cbD, textureHandle);
+ rhiD->barrierGen.enqueueSubresourceTransitionBarrier(cbD, textureHandle, subresource,
+ D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
+ D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
+
+ level += numGenMips;
+ }
+ }
+
+ if (ownStagingArea.has_value())
+ ownStagingArea->destroyWithDeferredRelease(&rhiD->releaseQueue);
+}
+
+bool QD3D12MemoryAllocator::create(ID3D12Device *device, IDXGIAdapter1 *adapter)
+{
+ this->device = device;
+
+ // We can function with and without D3D12MA: CreateCommittedResource is
+ // just fine for our purposes and not any complicated API-wise; the memory
+ // allocator is interesting for efficiency mainly since it can suballocate
+ // instead of making everything a committed resource allocation.
+
+ static bool disableMA = qEnvironmentVariableIntValue("QT_D3D_NO_SUBALLOC");
+ if (disableMA)
+ return true;
+
+ DXGI_ADAPTER_DESC1 desc;
+ adapter->GetDesc1(&desc);
+ if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
+ return true;
+
+ D3D12MA::ALLOCATOR_DESC allocatorDesc = {};
+ allocatorDesc.pDevice = device;
+ allocatorDesc.pAdapter = adapter;
+ // A QRhi is supposed to be used from one single thread only. Disable
+ // the allocator's own mutexes. This may give a performance boost.
+ allocatorDesc.Flags = D3D12MA::ALLOCATOR_FLAG_SINGLETHREADED;
+ HRESULT hr = D3D12MA::CreateAllocator(&allocatorDesc, &allocator);
+ if (FAILED(hr)) {
+ qWarning("Failed to initialize D3D12 Memory Allocator: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ return true;
+}
+
+void QD3D12MemoryAllocator::destroy()
+{
+ if (allocator) {
+ allocator->Release();
+ allocator = nullptr;
+ }
+}
+
+HRESULT QD3D12MemoryAllocator::createResource(D3D12_HEAP_TYPE heapType,
+ const D3D12_RESOURCE_DESC *resourceDesc,
+ D3D12_RESOURCE_STATES initialState,
+ const D3D12_CLEAR_VALUE *optimizedClearValue,
+ D3D12MA::Allocation **maybeAllocation,
+ REFIID riidResource,
+ void **ppvResource)
+{
+ if (allocator) {
+ D3D12MA::ALLOCATION_DESC allocDesc = {};
+ allocDesc.HeapType = heapType;
+ return allocator->CreateResource(&allocDesc,
+ resourceDesc,
+ initialState,
+ optimizedClearValue,
+ maybeAllocation,
+ riidResource,
+ ppvResource);
+ } else {
+ *maybeAllocation = nullptr;
+ D3D12_HEAP_PROPERTIES heapProps = {};
+ heapProps.Type = heapType;
+ return device->CreateCommittedResource(&heapProps,
+ D3D12_HEAP_FLAG_NONE,
+ resourceDesc,
+ initialState,
+ optimizedClearValue,
+ riidResource,
+ ppvResource);
+ }
+}
+
+void QD3D12MemoryAllocator::getBudget(D3D12MA::Budget *localBudget, D3D12MA::Budget *nonLocalBudget)
+{
+ if (allocator) {
+ allocator->GetBudget(localBudget, nonLocalBudget);
+ } else {
+ *localBudget = {};
+ *nonLocalBudget = {};
+ }
+}
+
+void QRhiD3D12::waitGpu()
+{
+ fullFenceCounter += 1u;
+ if (SUCCEEDED(cmdQueue->Signal(fullFence, fullFenceCounter))) {
+ if (SUCCEEDED(fullFence->SetEventOnCompletion(fullFenceCounter, fullFenceEvent)))
+ WaitForSingleObject(fullFenceEvent, INFINITE);
+ }
+}
+
+DXGI_SAMPLE_DESC QRhiD3D12::effectiveSampleDesc(int sampleCount, DXGI_FORMAT format) const
+{
+ DXGI_SAMPLE_DESC desc;
+ desc.Count = 1;
+ desc.Quality = 0;
+
+ const int s = effectiveSampleCount(sampleCount);
+
+ if (s > 1) {
+ D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msaaInfo = {};
+ msaaInfo.Format = format;
+ msaaInfo.SampleCount = UINT(s);
+ if (SUCCEEDED(dev->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &msaaInfo, sizeof(msaaInfo)))) {
+ if (msaaInfo.NumQualityLevels > 0) {
+ desc.Count = UINT(s);
+ desc.Quality = msaaInfo.NumQualityLevels - 1;
+ } else {
+ qWarning("No quality levels for multisampling with sample count %d", s);
+ }
+ }
+ }
+
+ return desc;
+}
+
+bool QRhiD3D12::startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList1 **cmdList)
+{
+ ID3D12CommandAllocator *cmdAlloc = cmdAllocators[currentFrameSlot];
+ if (!*cmdList) {
+ HRESULT hr = dev->CreateCommandList(0,
+ D3D12_COMMAND_LIST_TYPE_DIRECT,
+ cmdAlloc,
+ nullptr,
+ __uuidof(ID3D12GraphicsCommandList1),
+ reinterpret_cast<void **>(cmdList));
+ if (FAILED(hr)) {
+ qWarning("Failed to create command list: %s", qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ } else {
+ HRESULT hr = (*cmdList)->Reset(cmdAlloc, nullptr);
+ if (FAILED(hr)) {
+ qWarning("Failed to reset command list: %s", qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ }
+ return true;
+}
+
+static inline QRhiTexture::Format swapchainReadbackTextureFormat(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_R16G16B16A16_FLOAT:
+ return QRhiTexture::RGBA16F;
+ case DXGI_FORMAT_R32G32B32A32_FLOAT:
+ return QRhiTexture::RGBA32F;
+ case DXGI_FORMAT_R10G10B10A2_UNORM:
+ return QRhiTexture::RGB10A2;
+ default:
+ qWarning("DXGI_FORMAT %d cannot be read back", format);
+ break;
+ }
+ return QRhiTexture::UnknownFormat;
+}
+
+void QRhiD3D12::enqueueResourceUpdates(QD3D12CommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
+
+ for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
+ const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
+ if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) {
+ QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, u.buf);
+ Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
+ for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
+ if (u.offset == 0 && u.data.size() == bufD->m_size)
+ bufD->pendingHostWrites[i].clear();
+ bufD->pendingHostWrites[i].append({ u.offset, u.data });
+ }
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) {
+ QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, u.buf);
+ Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
+ Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
+
+ // The general approach to staging upload data is to first try
+ // using the per-frame "small" staging area, which is a very simple
+ // linear allocator; if that's not big enough then create a
+ // dedicated StagingArea and then deferred-release it to make sure
+ // if stays alive while the frame is possibly still in flight.
+
+ QD3D12StagingArea::Allocation stagingAlloc;
+ const quint32 allocSize = QD3D12StagingArea::allocSizeForArray(bufD->m_size, 1);
+ if (smallStagingAreas[currentFrameSlot].remainingCapacity() >= allocSize)
+ stagingAlloc = smallStagingAreas[currentFrameSlot].get(bufD->m_size);
+
+ std::optional<QD3D12StagingArea> ownStagingArea;
+ if (!stagingAlloc.isValid()) {
+ ownStagingArea = QD3D12StagingArea();
+ if (!ownStagingArea->create(this, allocSize, D3D12_HEAP_TYPE_UPLOAD))
+ continue;
+ stagingAlloc = ownStagingArea->get(allocSize);
+ if (!stagingAlloc.isValid()) {
+ ownStagingArea->destroy();
+ continue;
+ }
+ }
+
+ memcpy(stagingAlloc.p + u.offset, u.data.constData(), u.data.size());
+
+ barrierGen.addTransitionBarrier(bufD->handles[0], D3D12_RESOURCE_STATE_COPY_DEST);
+ barrierGen.enqueueBufferedTransitionBarriers(cbD);
+
+ if (QD3D12Resource *res = resourcePool.lookupRef(bufD->handles[0])) {
+ cbD->cmdList->CopyBufferRegion(res->resource,
+ u.offset,
+ stagingAlloc.buffer,
+ stagingAlloc.bufferOffset + u.offset,
+ u.data.size());
+ }
+
+ if (ownStagingArea.has_value())
+ ownStagingArea->destroyWithDeferredRelease(&releaseQueue);
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) {
+ QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, u.buf);
+ if (bufD->m_type == QRhiBuffer::Dynamic) {
+ bufD->executeHostWritesForFrameSlot(currentFrameSlot);
+ if (QD3D12Resource *res = resourcePool.lookupRef(bufD->handles[currentFrameSlot])) {
+ Q_ASSERT(res->cpuMapPtr);
+ u.result->data.resize(u.readSize);
+ memcpy(u.result->data.data(), reinterpret_cast<char *>(res->cpuMapPtr) + u.offset, u.readSize);
+ }
+ if (u.result->completed)
+ u.result->completed();
+ } else {
+ QD3D12Readback readback;
+ readback.frameSlot = currentFrameSlot;
+ readback.result = u.result;
+ readback.byteSize = u.readSize;
+ const quint32 allocSize = aligned(u.readSize, QD3D12StagingArea::ALIGNMENT);
+ if (!readback.staging.create(this, allocSize, D3D12_HEAP_TYPE_READBACK)) {
+ if (u.result->completed)
+ u.result->completed();
+ continue;
+ }
+ QD3D12StagingArea::Allocation stagingAlloc = readback.staging.get(u.readSize);
+ if (!stagingAlloc.isValid()) {
+ readback.staging.destroy();
+ if (u.result->completed)
+ u.result->completed();
+ continue;
+ }
+ Q_ASSERT(stagingAlloc.bufferOffset == 0);
+ barrierGen.addTransitionBarrier(bufD->handles[0], D3D12_RESOURCE_STATE_COPY_SOURCE);
+ barrierGen.enqueueBufferedTransitionBarriers(cbD);
+ if (QD3D12Resource *res = resourcePool.lookupRef(bufD->handles[0])) {
+ cbD->cmdList->CopyBufferRegion(stagingAlloc.buffer, 0, res->resource, u.offset, u.readSize);
+ activeReadbacks.append(readback);
+ } else {
+ readback.staging.destroy();
+ if (u.result->completed)
+ u.result->completed();
+ }
+ }
+ }
+ }
+
+ for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
+ const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
+ if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
+ QD3D12Texture *texD = QRHI_RES(QD3D12Texture, u.dst);
+ const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ QD3D12Resource *res = resourcePool.lookupRef(texD->handle);
+ if (!res)
+ continue;
+ barrierGen.addTransitionBarrier(texD->handle, D3D12_RESOURCE_STATE_COPY_DEST);
+ barrierGen.enqueueBufferedTransitionBarriers(cbD);
+ for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
+ for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level])) {
+ D3D12_SUBRESOURCE_FOOTPRINT footprint = {};
+ footprint.Format = res->desc.Format;
+ footprint.Depth = 1;
+ quint32 totalBytes = 0;
+
+ const QSize subresSize = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
+ : subresDesc.sourceSize();
+ const QPoint srcPos = subresDesc.sourceTopLeft();
+ QPoint dstPos = subresDesc.destinationTopLeft();
+
+ if (!subresDesc.image().isNull()) {
+ const QImage img = subresDesc.image();
+ const int bpl = img.bytesPerLine();
+ footprint.RowPitch = aligned<UINT>(bpl, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
+ totalBytes = footprint.RowPitch * img.height();
+ } else if (!subresDesc.data().isEmpty() && isCompressedFormat(texD->m_format)) {
+ QSize blockDim;
+ quint32 bpl = 0;
+ compressedFormatInfo(texD->m_format, subresSize, &bpl, nullptr, &blockDim);
+ footprint.RowPitch = aligned<UINT>(bpl, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
+ const int rowCount = aligned(subresSize.height(), blockDim.height()) / blockDim.height();
+ totalBytes = footprint.RowPitch * rowCount;
+ } else if (!subresDesc.data().isEmpty()) {
+ quint32 bpl = 0;
+ if (subresDesc.dataStride())
+ bpl = subresDesc.dataStride();
+ else
+ textureFormatInfo(texD->m_format, subresSize, &bpl, nullptr, nullptr);
+ footprint.RowPitch = aligned<UINT>(bpl, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
+ totalBytes = footprint.RowPitch * subresSize.height();
+ } else {
+ qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
+ continue;
+ }
+
+ const quint32 allocSize = QD3D12StagingArea::allocSizeForArray(totalBytes, 1);
+ QD3D12StagingArea::Allocation stagingAlloc;
+ if (smallStagingAreas[currentFrameSlot].remainingCapacity() >= allocSize)
+ stagingAlloc = smallStagingAreas[currentFrameSlot].get(allocSize);
+
+ std::optional<QD3D12StagingArea> ownStagingArea;
+ if (!stagingAlloc.isValid()) {
+ ownStagingArea = QD3D12StagingArea();
+ if (!ownStagingArea->create(this, allocSize, D3D12_HEAP_TYPE_UPLOAD))
+ continue;
+ stagingAlloc = ownStagingArea->get(allocSize);
+ if (!stagingAlloc.isValid()) {
+ ownStagingArea->destroy();
+ continue;
+ }
+ }
+
+ D3D12_TEXTURE_COPY_LOCATION dst;
+ dst.pResource = res->resource;
+ dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ dst.SubresourceIndex = calcSubresource(UINT(level), is3D ? 0u : UINT(layer), texD->mipLevelCount);
+ D3D12_TEXTURE_COPY_LOCATION src;
+ src.pResource = stagingAlloc.buffer;
+ src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+ src.PlacedFootprint.Offset = stagingAlloc.bufferOffset;
+
+ D3D12_BOX srcBox; // back, right, bottom are exclusive
+
+ if (!subresDesc.image().isNull()) {
+ const QImage img = subresDesc.image();
+ const int bpc = qMax(1, img.depth() / 8);
+ const int bpl = img.bytesPerLine();
+
+ QSize size = subresDesc.sourceSize().isEmpty() ? img.size() : subresDesc.sourceSize();
+ size.setWidth(qMin(size.width(), img.width() - srcPos.x()));
+ size.setHeight(qMin(size.height(), img.height() - srcPos.y()));
+
+ footprint.Width = size.width();
+ footprint.Height = size.height();
+
+ srcBox.left = 0;
+ srcBox.top = 0;
+ srcBox.right = UINT(size.width());
+ srcBox.bottom = UINT(size.height());
+ srcBox.front = 0;
+ srcBox.back = 1;
+
+ const uchar *imgPtr = img.constBits();
+ const quint32 lineBytes = size.width() * bpc;
+ for (int y = 0, h = size.height(); y < h; ++y) {
+ memcpy(stagingAlloc.p + y * footprint.RowPitch,
+ imgPtr + srcPos.x() * bpc + (y + srcPos.y()) * bpl,
+ lineBytes);
+ }
+ } else if (!subresDesc.data().isEmpty() && isCompressedFormat(texD->m_format)) {
+ QSize blockDim;
+ quint32 bpl = 0;
+ compressedFormatInfo(texD->m_format, subresSize, &bpl, nullptr, &blockDim);
+ // x and y must be multiples of the block width and height
+ dstPos.setX(aligned(dstPos.x(), blockDim.width()));
+ dstPos.setY(aligned(dstPos.y(), blockDim.height()));
+
+ srcBox.left = 0;
+ srcBox.top = 0;
+ // width and height must be multiples of the block width and height
+ srcBox.right = aligned(subresSize.width(), blockDim.width());
+ srcBox.bottom = aligned(subresSize.height(), blockDim.height());
+
+ srcBox.front = 0;
+ srcBox.back = 1;
+
+ footprint.Width = aligned(subresSize.width(), blockDim.width());
+ footprint.Height = aligned(subresSize.height(), blockDim.height());
+
+ const quint32 copyBytes = qMin(bpl, footprint.RowPitch);
+ const QByteArray imgData = subresDesc.data();
+ const char *imgPtr = imgData.constData();
+ const int rowCount = aligned(subresSize.height(), blockDim.height()) / blockDim.height();
+ for (int y = 0; y < rowCount; ++y)
+ memcpy(stagingAlloc.p + y * footprint.RowPitch, imgPtr + y * bpl, copyBytes);
+ } else if (!subresDesc.data().isEmpty()) {
+ srcBox.left = 0;
+ srcBox.top = 0;
+ srcBox.right = subresSize.width();
+ srcBox.bottom = subresSize.height();
+ srcBox.front = 0;
+ srcBox.back = 1;
+
+ footprint.Width = subresSize.width();
+ footprint.Height = subresSize.height();
+
+ quint32 bpl = 0;
+ if (subresDesc.dataStride())
+ bpl = subresDesc.dataStride();
+ else
+ textureFormatInfo(texD->m_format, subresSize, &bpl, nullptr, nullptr);
+
+ const quint32 copyBytes = qMin(bpl, footprint.RowPitch);
+ const QByteArray data = subresDesc.data();
+ const char *imgPtr = data.constData();
+ for (int y = 0, h = subresSize.height(); y < h; ++y)
+ memcpy(stagingAlloc.p + y * footprint.RowPitch, imgPtr + y * bpl, copyBytes);
+ }
+
+ src.PlacedFootprint.Footprint = footprint;
+
+ cbD->cmdList->CopyTextureRegion(&dst,
+ UINT(dstPos.x()),
+ UINT(dstPos.y()),
+ is3D ? UINT(layer) : 0u,
+ &src,
+ &srcBox);
+
+ if (ownStagingArea.has_value())
+ ownStagingArea->destroyWithDeferredRelease(&releaseQueue);
+ }
+ }
+ }
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
+ Q_ASSERT(u.src && u.dst);
+ QD3D12Texture *srcD = QRHI_RES(QD3D12Texture, u.src);
+ QD3D12Texture *dstD = QRHI_RES(QD3D12Texture, u.dst);
+ const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ QD3D12Resource *srcRes = resourcePool.lookupRef(srcD->handle);
+ QD3D12Resource *dstRes = resourcePool.lookupRef(dstD->handle);
+ if (!srcRes || !dstRes)
+ continue;
+
+ barrierGen.addTransitionBarrier(srcD->handle, D3D12_RESOURCE_STATE_COPY_SOURCE);
+ barrierGen.addTransitionBarrier(dstD->handle, D3D12_RESOURCE_STATE_COPY_DEST);
+ barrierGen.enqueueBufferedTransitionBarriers(cbD);
+
+ const UINT srcSubresource = calcSubresource(UINT(u.desc.sourceLevel()),
+ srcIs3D ? 0u : UINT(u.desc.sourceLayer()),
+ srcD->mipLevelCount);
+ const UINT dstSubresource = calcSubresource(UINT(u.desc.destinationLevel()),
+ dstIs3D ? 0u : UINT(u.desc.destinationLayer()),
+ dstD->mipLevelCount);
+ const QPoint dp = u.desc.destinationTopLeft();
+ const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
+ const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
+ const QPoint sp = u.desc.sourceTopLeft();
+
+ D3D12_BOX srcBox;
+ srcBox.left = UINT(sp.x());
+ srcBox.top = UINT(sp.y());
+ srcBox.front = srcIs3D ? UINT(u.desc.sourceLayer()) : 0u;
+ // back, right, bottom are exclusive
+ srcBox.right = srcBox.left + UINT(copySize.width());
+ srcBox.bottom = srcBox.top + UINT(copySize.height());
+ srcBox.back = srcBox.front + 1;
+
+ D3D12_TEXTURE_COPY_LOCATION src;
+ src.pResource = srcRes->resource;
+ src.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ src.SubresourceIndex = srcSubresource;
+ D3D12_TEXTURE_COPY_LOCATION dst;
+ dst.pResource = dstRes->resource;
+ dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ dst.SubresourceIndex = dstSubresource;
+
+ cbD->cmdList->CopyTextureRegion(&dst,
+ UINT(dp.x()),
+ UINT(dp.y()),
+ dstIs3D ? UINT(u.desc.destinationLayer()) : 0u,
+ &src,
+ &srcBox);
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
+ QD3D12Readback readback;
+ readback.frameSlot = currentFrameSlot;
+ readback.result = u.result;
+
+ QD3D12ObjectHandle srcHandle;
+ bool is3D = false;
+ if (u.rb.texture()) {
+ QD3D12Texture *texD = QRHI_RES(QD3D12Texture, u.rb.texture());
+ if (texD->sampleDesc.Count > 1) {
+ qWarning("Multisample texture cannot be read back");
+ continue;
+ }
+ is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ readback.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
+ readback.format = texD->m_format;
+ srcHandle = texD->handle;
+ } else {
+ Q_ASSERT(currentSwapChain);
+ readback.pixelSize = currentSwapChain->pixelSize;
+ readback.format = swapchainReadbackTextureFormat(currentSwapChain->colorFormat, nullptr);
+ if (readback.format == QRhiTexture::UnknownFormat)
+ continue;
+ srcHandle = currentSwapChain->colorBuffers[currentSwapChain->currentBackBufferIndex];
+ }
+
+ textureFormatInfo(readback.format,
+ readback.pixelSize,
+ &readback.bytesPerLine,
+ &readback.byteSize,
+ nullptr);
+
+ QD3D12Resource *srcRes = resourcePool.lookupRef(srcHandle);
+ if (!srcRes)
+ continue;
+
+ const UINT subresource = calcSubresource(UINT(u.rb.level()),
+ is3D ? 0u : UINT(u.rb.layer()),
+ srcRes->desc.MipLevels);
+ D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout;
+ // totalBytes is what we get from D3D, with the 256 aligned stride,
+ // readback.byteSize is the final result that's not relevant here yet
+ UINT64 totalBytes = 0;
+ dev->GetCopyableFootprints(&srcRes->desc, subresource, 1, 0,
+ &layout, nullptr, nullptr, &totalBytes);
+ readback.stagingRowPitch = layout.Footprint.RowPitch;
+
+ const quint32 allocSize = aligned<quint32>(totalBytes, QD3D12StagingArea::ALIGNMENT);
+ if (!readback.staging.create(this, allocSize, D3D12_HEAP_TYPE_READBACK)) {
+ if (u.result->completed)
+ u.result->completed();
+ continue;
+ }
+ QD3D12StagingArea::Allocation stagingAlloc = readback.staging.get(totalBytes);
+ if (!stagingAlloc.isValid()) {
+ readback.staging.destroy();
+ if (u.result->completed)
+ u.result->completed();
+ continue;
+ }
+ Q_ASSERT(stagingAlloc.bufferOffset == 0);
+
+ barrierGen.addTransitionBarrier(srcHandle, D3D12_RESOURCE_STATE_COPY_SOURCE);
+ barrierGen.enqueueBufferedTransitionBarriers(cbD);
+
+ D3D12_TEXTURE_COPY_LOCATION dst;
+ dst.pResource = stagingAlloc.buffer;
+ dst.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+ dst.PlacedFootprint.Offset = 0;
+ dst.PlacedFootprint.Footprint = layout.Footprint;
+
+ D3D12_TEXTURE_COPY_LOCATION src;
+ src.pResource = srcRes->resource;
+ src.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ src.SubresourceIndex = subresource;
+
+ D3D12_BOX srcBox = {};
+ if (is3D) {
+ srcBox.front = UINT(u.rb.layer());
+ srcBox.back = srcBox.front + 1;
+ srcBox.right = readback.pixelSize.width(); // exclusive
+ srcBox.bottom = readback.pixelSize.height();
+ }
+ cbD->cmdList->CopyTextureRegion(&dst, 0, 0, 0, &src, is3D ? &srcBox : nullptr);
+ activeReadbacks.append(readback);
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) {
+ QD3D12Texture *texD = QRHI_RES(QD3D12Texture, u.dst);
+ Q_ASSERT(texD->flags().testFlag(QRhiTexture::UsedWithGenerateMips));
+ mipmapGen.generate(cbD, texD->handle);
+ }
+ }
+
+ ud->free();
+}
+
+void QRhiD3D12::finishActiveReadbacks(bool forced)
+{
+ QVarLengthArray<std::function<void()>, 4> completedCallbacks;
+
+ for (int i = activeReadbacks.size() - 1; i >= 0; --i) {
+ QD3D12Readback &readback(activeReadbacks[i]);
+ if (forced || currentFrameSlot == readback.frameSlot || readback.frameSlot < 0) {
+ readback.result->format = readback.format;
+ readback.result->pixelSize = readback.pixelSize;
+ readback.result->data.resize(int(readback.byteSize));
+
+ if (readback.format != QRhiTexture::UnknownFormat) {
+ quint8 *dstPtr = reinterpret_cast<quint8 *>(readback.result->data.data());
+ const quint8 *srcPtr = readback.staging.mem.p;
+ const quint32 lineSize = qMin(readback.bytesPerLine, readback.stagingRowPitch);
+ for (int y = 0, h = readback.pixelSize.height(); y < h; ++y)
+ memcpy(dstPtr + y * readback.bytesPerLine, srcPtr + y * readback.stagingRowPitch, lineSize);
+ } else {
+ memcpy(readback.result->data.data(), readback.staging.mem.p, readback.byteSize);
+ }
+
+ readback.staging.destroy();
+
+ if (readback.result->completed)
+ completedCallbacks.append(readback.result->completed);
+
+ activeReadbacks.removeLast();
+ }
+ }
+
+ for (auto f : completedCallbacks)
+ f();
+}
+
+bool QRhiD3D12::ensureShaderVisibleDescriptorHeapCapacity(QD3D12ShaderVisibleDescriptorHeap *h,
+ D3D12_DESCRIPTOR_HEAP_TYPE type,
+ int frameSlot,
+ quint32 neededDescriptorCount,
+ bool *gotNew)
+{
+ // Gets a new heap if needed. Note that the capacity we get is clamped
+ // automatically (e.g. to 1 million, or 2048 for samplers), so * 2 does not
+ // mean we can grow indefinitely, then again even using the same size would
+ // work (because we what we are after here is a new heap for the rest of
+ // the commands, not affecting what's already recorded).
+ if (h->perFrameHeapSlice[frameSlot].remainingCapacity() < neededDescriptorCount) {
+ const quint32 newPerFrameSize = qMax(h->perFrameHeapSlice[frameSlot].capacity * 2,
+ neededDescriptorCount);
+ QD3D12ShaderVisibleDescriptorHeap newHeap;
+ if (!newHeap.create(dev, type, newPerFrameSize)) {
+ qWarning("Could not create new shader-visible descriptor heap");
+ return false;
+ }
+ h->destroyWithDeferredRelease(&releaseQueue);
+ *h = newHeap;
+ *gotNew = true;
+ }
+ return true;
+}
+
+void QRhiD3D12::bindShaderVisibleHeaps(QD3D12CommandBuffer *cbD)
+{
+ ID3D12DescriptorHeap *heaps[] = {
+ shaderVisibleCbvSrvUavHeap.heap.heap,
+ samplerMgr.shaderVisibleSamplerHeap.heap.heap
+ };
+ cbD->cmdList->SetDescriptorHeaps(2, heaps);
+}
+
+QD3D12Buffer::QD3D12Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
+ : QRhiBuffer(rhi, type, usage, size)
+{
+}
+
+QD3D12Buffer::~QD3D12Buffer()
+{
+ destroy();
+}
+
+void QD3D12Buffer::destroy()
+{
+ if (handles[0].isNull())
+ return;
+
+ QRHI_RES_RHI(QRhiD3D12);
+
+ // destroy() implementations, unlike other functions, are expected to test
+ // for m_rhi (rhiD) being null, to allow surviving in case one attempts to
+ // destroy a (leaked) resource after the QRhi.
+ //
+ // If there is no QRhi anymore, we do not deferred-release but that's fine
+ // since the QRhi already released everything that was in the resourcePool.
+
+ for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
+ if (rhiD)
+ rhiD->releaseQueue.deferredReleaseResource(handles[i]);
+ handles[i] = {};
+ pendingHostWrites[i].clear();
+ }
+
+ if (rhiD)
+ rhiD->unregisterResource(this);
+}
+
+bool QD3D12Buffer::create()
+{
+ if (!handles[0].isNull())
+ destroy();
+
+ if (m_usage.testFlag(QRhiBuffer::UniformBuffer) && m_type != Dynamic) {
+ qWarning("UniformBuffer must always be Dynamic");
+ return false;
+ }
+
+ if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
+ qWarning("StorageBuffer cannot be combined with Dynamic");
+ return false;
+ }
+
+ const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
+ const quint32 roundedSize = aligned(nonZeroSize, m_usage.testFlag(QRhiBuffer::UniformBuffer) ? 256u : 4u);
+
+ UINT resourceFlags = D3D12_RESOURCE_FLAG_NONE;
+ if (m_usage.testFlag(QRhiBuffer::StorageBuffer))
+ resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
+
+ QRHI_RES_RHI(QRhiD3D12);
+ HRESULT hr = 0;
+ for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
+ if (i == 0 || m_type == Dynamic) {
+ D3D12_RESOURCE_DESC resourceDesc = {};
+ resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ resourceDesc.Width = roundedSize;
+ resourceDesc.Height = 1;
+ resourceDesc.DepthOrArraySize = 1;
+ resourceDesc.MipLevels = 1;
+ resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
+ resourceDesc.SampleDesc = { 1, 0 };
+ resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+ resourceDesc.Flags = D3D12_RESOURCE_FLAGS(resourceFlags);
+ ID3D12Resource *resource = nullptr;
+ D3D12MA::Allocation *allocation = nullptr;
+ // Dynamic == host (CPU) visible
+ D3D12_HEAP_TYPE heapType = m_type == Dynamic
+ ? D3D12_HEAP_TYPE_UPLOAD
+ : D3D12_HEAP_TYPE_DEFAULT;
+ D3D12_RESOURCE_STATES resourceState = m_type == Dynamic
+ ? D3D12_RESOURCE_STATE_GENERIC_READ
+ : D3D12_RESOURCE_STATE_COMMON;
+ hr = rhiD->vma.createResource(heapType,
+ &resourceDesc,
+ resourceState,
+ nullptr,
+ &allocation,
+ __uuidof(resource),
+ reinterpret_cast<void **>(&resource));
+ if (FAILED(hr))
+ break;
+ if (!m_objectName.isEmpty()) {
+ QString decoratedName = QString::fromUtf8(m_objectName);
+ if (m_type == Dynamic) {
+ decoratedName += QLatin1Char('/');
+ decoratedName += QString::number(i);
+ }
+ resource->SetName(reinterpret_cast<LPCWSTR>(decoratedName.utf16()));
+ }
+ void *cpuMemPtr = nullptr;
+ if (m_type == Dynamic) {
+ // will be mapped for ever on the CPU, this makes future host write operations very simple
+ hr = resource->Map(0, nullptr, &cpuMemPtr);
+ if (FAILED(hr)) {
+ qWarning("Map() failed to dynamic buffer");
+ resource->Release();
+ if (allocation)
+ allocation->Release();
+ break;
+ }
+ }
+ handles[i] = QD3D12Resource::addToPool(&rhiD->resourcePool,
+ resource,
+ resourceState,
+ allocation,
+ cpuMemPtr);
+ }
+ }
+ if (FAILED(hr)) {
+ qWarning("Failed to create buffer: '%s' Type was %d, size was %u, using D3D12MA was %d.",
+ qPrintable(QSystemError::windowsComString(hr)),
+ int(m_type),
+ roundedSize,
+ int(rhiD->vma.isUsingD3D12MA()));
+ return false;
+ }
+
+ rhiD->registerResource(this);
+ return true;
+}
+
+QRhiBuffer::NativeBuffer QD3D12Buffer::nativeBuffer()
+{
+ NativeBuffer b;
+ Q_ASSERT(sizeof(b.objects) / sizeof(b.objects[0]) >= size_t(QD3D12_FRAMES_IN_FLIGHT));
+ QRHI_RES_RHI(QRhiD3D12);
+ if (m_type == Dynamic) {
+ for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
+ executeHostWritesForFrameSlot(i);
+ if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handles[i]))
+ b.objects[i] = res->resource;
+ else
+ b.objects[i] = nullptr;
+ }
+ b.slotCount = QD3D12_FRAMES_IN_FLIGHT;
+ return b;
+ }
+ if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handles[0]))
+ b.objects[0] = res->resource;
+ else
+ b.objects[0] = nullptr;
+ b.slotCount = 1;
+ return b;
+}
+
+char *QD3D12Buffer::beginFullDynamicBufferUpdateForCurrentFrame()
+{
+ // Shortcut the entire buffer update mechanism and allow the client to do
+ // the host writes directly to the buffer. This will lead to unexpected
+ // results when combined with QRhiResourceUpdateBatch-based updates for the
+ // buffer, but provides a fast path for dynamic buffers that have all their
+ // content changed in every frame.
+
+ Q_ASSERT(m_type == Dynamic);
+ QRHI_RES_RHI(QRhiD3D12);
+ Q_ASSERT(rhiD->inFrame);
+ if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handles[rhiD->currentFrameSlot]))
+ return static_cast<char *>(res->cpuMapPtr);
+
+ return nullptr;
+}
+
+void QD3D12Buffer::endFullDynamicBufferUpdateForCurrentFrame()
+{
+ // nothing to do here
+}
+
+void QD3D12Buffer::executeHostWritesForFrameSlot(int frameSlot)
+{
+ if (pendingHostWrites[frameSlot].isEmpty())
+ return;
+
+ Q_ASSERT(m_type == QRhiBuffer::Dynamic);
+ QRHI_RES_RHI(QRhiD3D12);
+ if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handles[frameSlot])) {
+ Q_ASSERT(res->cpuMapPtr);
+ for (const QD3D12Buffer::HostWrite &u : std::as_const(pendingHostWrites[frameSlot]))
+ memcpy(static_cast<char *>(res->cpuMapPtr) + u.offset, u.data.constData(), u.data.size());
+ }
+ pendingHostWrites[frameSlot].clear();
+}
+
+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::RG8:
+ return DXGI_FORMAT_R8G8_UNORM;
+ case QRhiTexture::R16:
+ return DXGI_FORMAT_R16_UNORM;
+ case QRhiTexture::RG16:
+ return DXGI_FORMAT_R16G16_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::R16F:
+ return DXGI_FORMAT_R16_FLOAT;
+ case QRhiTexture::R32F:
+ return DXGI_FORMAT_R32_FLOAT;
+
+ case QRhiTexture::RGB10A2:
+ return DXGI_FORMAT_R10G10B10A2_UNORM;
+
+ case QRhiTexture::D16:
+ return DXGI_FORMAT_R16_TYPELESS;
+ case QRhiTexture::D24:
+ return DXGI_FORMAT_R24G8_TYPELESS;
+ case QRhiTexture::D24S8:
+ return DXGI_FORMAT_R24G8_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:
+ case QRhiTexture::ETC2_RGB8A1:
+ case QRhiTexture::ETC2_RGBA8:
+ qWarning("QRhiD3D12 does not support ETC2 textures");
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+
+ 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("QRhiD3D12 does not support ASTC textures");
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+
+ default:
+ break;
+ }
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+}
+
+QD3D12RenderBuffer::QD3D12RenderBuffer(QRhiImplementation *rhi,
+ Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ Flags flags,
+ QRhiTexture::Format backingFormatHint)
+ : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags, backingFormatHint)
+{
+}
+
+QD3D12RenderBuffer::~QD3D12RenderBuffer()
+{
+ destroy();
+}
+
+void QD3D12RenderBuffer::destroy()
+{
+ if (handle.isNull())
+ return;
+
+ QRHI_RES_RHI(QRhiD3D12);
+ if (rhiD) {
+ if (rtv.isValid())
+ rhiD->releaseQueue.deferredReleaseResourceWithViews(handle, &rhiD->rtvPool, rtv, 1);
+ else if (dsv.isValid())
+ rhiD->releaseQueue.deferredReleaseResourceWithViews(handle, &rhiD->dsvPool, dsv, 1);
+ }
+
+ handle = {};
+ rtv = {};
+ dsv = {};
+
+ if (rhiD)
+ rhiD->unregisterResource(this);
+}
+
+bool QD3D12RenderBuffer::create()
+{
+ if (!handle.isNull())
+ destroy();
+
+ if (m_pixelSize.isEmpty())
+ return false;
+
+ QRHI_RES_RHI(QRhiD3D12);
+
+ switch (m_type) {
+ case QRhiRenderBuffer::Color:
+ {
+ dxgiFormat = toD3DTextureFormat(backingFormat(), {});
+ sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, dxgiFormat);
+ D3D12_RESOURCE_DESC resourceDesc = {};
+ resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ resourceDesc.Width = UINT64(m_pixelSize.width());
+ resourceDesc.Height = UINT(m_pixelSize.height());
+ resourceDesc.DepthOrArraySize = 1;
+ resourceDesc.MipLevels = 1;
+ resourceDesc.Format = dxgiFormat;
+ resourceDesc.SampleDesc = sampleDesc;
+ resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+ resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
+ D3D12_CLEAR_VALUE clearValue = {};
+ clearValue.Format = dxgiFormat;
+ // have a separate allocation and resource object (meaning both will need its own Release())
+ ID3D12Resource *resource = nullptr;
+ D3D12MA::Allocation *allocation = nullptr;
+ HRESULT hr = rhiD->vma.createResource(D3D12_HEAP_TYPE_DEFAULT,
+ &resourceDesc,
+ D3D12_RESOURCE_STATE_RENDER_TARGET,
+ &clearValue,
+ &allocation,
+ __uuidof(ID3D12Resource),
+ reinterpret_cast<void **>(&resource));
+ if (FAILED(hr)) {
+ qWarning("Failed to create color buffer: %s", qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ handle = QD3D12Resource::addToPool(&rhiD->resourcePool, resource, D3D12_RESOURCE_STATE_RENDER_TARGET, allocation);
+ rtv = rhiD->rtvPool.allocate(1);
+ if (!rtv.isValid())
+ return false;
+ D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
+ rtvDesc.Format = dxgiFormat;
+ rtvDesc.ViewDimension = sampleDesc.Count > 1 ? D3D12_RTV_DIMENSION_TEXTURE2DMS
+ : D3D12_RTV_DIMENSION_TEXTURE2D;
+ rhiD->dev->CreateRenderTargetView(resource, &rtvDesc, rtv.cpuHandle);
+ }
+ break;
+ case QRhiRenderBuffer::DepthStencil:
+ {
+ dxgiFormat = DS_FORMAT;
+ sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, dxgiFormat);
+ D3D12_RESOURCE_DESC resourceDesc = {};
+ resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ resourceDesc.Width = UINT64(m_pixelSize.width());
+ resourceDesc.Height = UINT(m_pixelSize.height());
+ resourceDesc.DepthOrArraySize = 1;
+ resourceDesc.MipLevels = 1;
+ resourceDesc.Format = dxgiFormat;
+ resourceDesc.SampleDesc = sampleDesc;
+ resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+ resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
+ if (m_flags.testFlag(UsedWithSwapChainOnly))
+ resourceDesc.Flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
+ D3D12_CLEAR_VALUE clearValue = {};
+ clearValue.Format = dxgiFormat;
+ clearValue.DepthStencil.Depth = 1.0f;
+ clearValue.DepthStencil.Stencil = 0;
+ ID3D12Resource *resource = nullptr;
+ D3D12MA::Allocation *allocation = nullptr;
+ HRESULT hr = rhiD->vma.createResource(D3D12_HEAP_TYPE_DEFAULT,
+ &resourceDesc,
+ D3D12_RESOURCE_STATE_DEPTH_WRITE,
+ &clearValue,
+ &allocation,
+ __uuidof(ID3D12Resource),
+ reinterpret_cast<void **>(&resource));
+ if (FAILED(hr)) {
+ qWarning("Failed to create depth-stencil buffer: %s", qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ handle = QD3D12Resource::addToPool(&rhiD->resourcePool, resource, D3D12_RESOURCE_STATE_DEPTH_WRITE, allocation);
+ dsv = rhiD->dsvPool.allocate(1);
+ if (!dsv.isValid())
+ return false;
+ D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
+ dsvDesc.Format = dxgiFormat;
+ dsvDesc.ViewDimension = sampleDesc.Count > 1 ? D3D12_DSV_DIMENSION_TEXTURE2DMS
+ : D3D12_DSV_DIMENSION_TEXTURE2D;
+ rhiD->dev->CreateDepthStencilView(resource, &dsvDesc, dsv.cpuHandle);
+ }
+ break;
+ }
+
+ if (!m_objectName.isEmpty()) {
+ if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handle)) {
+ const QString name = QString::fromUtf8(m_objectName);
+ res->resource->SetName(reinterpret_cast<LPCWSTR>(name.utf16()));
+ }
+ }
+
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QRhiTexture::Format QD3D12RenderBuffer::backingFormat() const
+{
+ if (m_backingFormatHint != QRhiTexture::UnknownFormat)
+ return m_backingFormatHint;
+ else
+ return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
+}
+
+QD3D12Texture::QD3D12Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
+ int arraySize, int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
+{
+}
+
+QD3D12Texture::~QD3D12Texture()
+{
+ destroy();
+}
+
+void QD3D12Texture::destroy()
+{
+ if (handle.isNull())
+ return;
+
+ QRHI_RES_RHI(QRhiD3D12);
+ if (rhiD)
+ rhiD->releaseQueue.deferredReleaseResourceWithViews(handle, &rhiD->cbvSrvUavPool, srv, 1);
+
+ handle = {};
+ srv = {};
+
+ if (rhiD)
+ 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::D24:
+ return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
+ case QRhiTexture::Format::D24S8:
+ return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
+ case QRhiTexture::Format::D32F:
+ return DXGI_FORMAT_R32_FLOAT;
+ default:
+ break;
+ }
+ Q_UNREACHABLE_RETURN(DXGI_FORMAT_R32_FLOAT);
+}
+
+static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format)
+{
+ // here the result cannot be typeless
+ switch (format) {
+ case QRhiTexture::Format::D16:
+ return DXGI_FORMAT_D16_UNORM;
+ case QRhiTexture::Format::D24:
+ return DXGI_FORMAT_D24_UNORM_S8_UINT;
+ case QRhiTexture::Format::D24S8:
+ return DXGI_FORMAT_D24_UNORM_S8_UINT;
+ case QRhiTexture::Format::D32F:
+ return DXGI_FORMAT_D32_FLOAT;
+ default:
+ break;
+ }
+ Q_UNREACHABLE_RETURN(DXGI_FORMAT_D32_FLOAT);
+}
+
+static inline bool isDepthTextureFormat(QRhiTexture::Format format)
+{
+ switch (format) {
+ case QRhiTexture::Format::D16:
+ case QRhiTexture::Format::D24:
+ case QRhiTexture::Format::D24S8:
+ case QRhiTexture::Format::D32F:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool QD3D12Texture::prepareCreate(QSize *adjustedSize)
+{
+ if (!handle.isNull())
+ destroy();
+
+ const bool isDepth = isDepthTextureFormat(m_format);
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool isArray = m_flags.testFlag(TextureArray);
+ const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ const bool is1D = m_flags.testFlag(OneDimensional);
+
+ const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
+ : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
+
+ dxgiFormat = toD3DTextureFormat(m_format, m_flags);
+ if (isDepth) {
+ srvFormat = toD3DDepthTextureSRVFormat(m_format);
+ rtFormat = toD3DDepthTextureDSVFormat(m_format);
+ } else {
+ srvFormat = dxgiFormat;
+ rtFormat = dxgiFormat;
+ }
+ if (m_writeViewFormat.format != UnknownFormat) {
+ if (isDepth)
+ rtFormat = toD3DDepthTextureDSVFormat(m_writeViewFormat.format);
+ else
+ rtFormat = toD3DTextureFormat(m_writeViewFormat.format, m_writeViewFormat.srgb ? sRGB : Flags());
+ }
+ if (m_readViewFormat.format != UnknownFormat) {
+ if (isDepth)
+ srvFormat = toD3DDepthTextureSRVFormat(m_readViewFormat.format);
+ else
+ srvFormat = toD3DTextureFormat(m_readViewFormat.format, m_readViewFormat.srgb ? sRGB : Flags());
+ }
+
+ QRHI_RES_RHI(QRhiD3D12);
+ mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
+ sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, dxgiFormat);
+ if (sampleDesc.Count > 1) {
+ if (isCube) {
+ qWarning("Cubemap texture cannot be multisample");
+ return false;
+ }
+ if (is3D) {
+ qWarning("3D 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 (isCube && is3D) {
+ qWarning("Texture cannot be both cube and 3D");
+ return false;
+ }
+ if (isArray && is3D) {
+ qWarning("Texture cannot be both array and 3D");
+ return false;
+ }
+ if (isCube && is1D) {
+ qWarning("Texture cannot be both cube and 1D");
+ return false;
+ }
+ if (is1D && is3D) {
+ qWarning("Texture cannot be both 1D and 3D");
+ return false;
+ }
+ if (m_depth > 1 && !is3D) {
+ qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
+ return false;
+ }
+ if (m_arraySize > 0 && !isArray) {
+ qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
+ return false;
+ }
+ if (m_arraySize < 1 && isArray) {
+ qWarning("Texture is an array but array size is %d", m_arraySize);
+ return false;
+ }
+
+ if (adjustedSize)
+ *adjustedSize = size;
+
+ return true;
+}
+
+bool QD3D12Texture::finishCreate()
+{
+ QRHI_RES_RHI(QRhiD3D12);
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool isArray = m_flags.testFlag(TextureArray);
+ const bool is1D = m_flags.testFlag(OneDimensional);
+
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
+ srvDesc.Format = srvFormat;
+ srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+
+ if (isCube) {
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
+ srvDesc.TextureCube.MipLevels = mipLevelCount;
+ } else {
+ if (is1D) {
+ if (isArray) {
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY;
+ srvDesc.Texture1DArray.MipLevels = mipLevelCount;
+ if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
+ srvDesc.Texture1DArray.FirstArraySlice = UINT(m_arrayRangeStart);
+ srvDesc.Texture1DArray.ArraySize = UINT(m_arrayRangeLength);
+ } else {
+ srvDesc.Texture1DArray.FirstArraySlice = 0;
+ srvDesc.Texture1DArray.ArraySize = UINT(qMax(0, m_arraySize));
+ }
+ } else {
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D;
+ srvDesc.Texture1D.MipLevels = mipLevelCount;
+ }
+ } else if (isArray) {
+ if (sampleDesc.Count > 1) {
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY;
+ if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
+ srvDesc.Texture2DMSArray.FirstArraySlice = UINT(m_arrayRangeStart);
+ srvDesc.Texture2DMSArray.ArraySize = UINT(m_arrayRangeLength);
+ } else {
+ srvDesc.Texture2DMSArray.FirstArraySlice = 0;
+ srvDesc.Texture2DMSArray.ArraySize = UINT(qMax(0, m_arraySize));
+ }
+ } else {
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
+ srvDesc.Texture2DArray.MipLevels = mipLevelCount;
+ if (m_arrayRangeStart >= 0 && m_arrayRangeLength >= 0) {
+ srvDesc.Texture2DArray.FirstArraySlice = UINT(m_arrayRangeStart);
+ srvDesc.Texture2DArray.ArraySize = UINT(m_arrayRangeLength);
+ } else {
+ srvDesc.Texture2DArray.FirstArraySlice = 0;
+ srvDesc.Texture2DArray.ArraySize = UINT(qMax(0, m_arraySize));
+ }
+ }
+ } else {
+ if (sampleDesc.Count > 1) {
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
+ } else if (is3D) {
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
+ srvDesc.Texture3D.MipLevels = mipLevelCount;
+ } else {
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+ srvDesc.Texture2D.MipLevels = mipLevelCount;
+ }
+ }
+ }
+
+ srv = rhiD->cbvSrvUavPool.allocate(1);
+ if (!srv.isValid())
+ return false;
+
+ if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handle)) {
+ rhiD->dev->CreateShaderResourceView(res->resource, &srvDesc, srv.cpuHandle);
+ if (!m_objectName.isEmpty()) {
+ const QString name = QString::fromUtf8(m_objectName);
+ res->resource->SetName(reinterpret_cast<LPCWSTR>(name.utf16()));
+ }
+ } else {
+ return false;
+ }
+
+ generation += 1;
+ return true;
+}
+
+bool QD3D12Texture::create()
+{
+ QSize size;
+ if (!prepareCreate(&size))
+ return false;
+
+ const bool isDepth = isDepthTextureFormat(m_format);
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool isArray = m_flags.testFlag(TextureArray);
+ const bool is1D = m_flags.testFlag(OneDimensional);
+
+ QRHI_RES_RHI(QRhiD3D12);
+
+ bool needsOptimizedClearValueSpecified = false;
+ UINT resourceFlags = 0;
+ if (m_flags.testFlag(RenderTarget) || sampleDesc.Count > 1) {
+ if (isDepth)
+ resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
+ else
+ resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
+ needsOptimizedClearValueSpecified = true;
+ }
+ if (m_flags.testFlag(UsedWithGenerateMips)) {
+ if (isDepth) {
+ qWarning("Depth texture cannot have mipmaps generated");
+ return false;
+ }
+ resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
+ }
+ if (m_flags.testFlag(UsedWithLoadStore))
+ resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
+
+ D3D12_RESOURCE_DESC resourceDesc = {};
+ resourceDesc.Dimension = is1D ? D3D12_RESOURCE_DIMENSION_TEXTURE1D
+ : (is3D ? D3D12_RESOURCE_DIMENSION_TEXTURE3D
+ : D3D12_RESOURCE_DIMENSION_TEXTURE2D);
+ resourceDesc.Width = UINT64(size.width());
+ resourceDesc.Height = UINT(size.height());
+ resourceDesc.DepthOrArraySize = isCube ? 6
+ : (isArray ? UINT(qMax(0, m_arraySize))
+ : (is3D ? qMax(1, m_depth)
+ : 1));
+ resourceDesc.MipLevels = mipLevelCount;
+ resourceDesc.Format = dxgiFormat;
+ resourceDesc.SampleDesc = sampleDesc;
+ resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+ resourceDesc.Flags = D3D12_RESOURCE_FLAGS(resourceFlags);
+ D3D12_CLEAR_VALUE clearValue = {};
+ clearValue.Format = dxgiFormat;
+ if (isDepth) {
+ clearValue.Format = toD3DDepthTextureDSVFormat(m_format);
+ clearValue.DepthStencil.Depth = 1.0f;
+ clearValue.DepthStencil.Stencil = 0;
+ }
+ ID3D12Resource *resource = nullptr;
+ D3D12MA::Allocation *allocation = nullptr;
+ HRESULT hr = rhiD->vma.createResource(D3D12_HEAP_TYPE_DEFAULT,
+ &resourceDesc,
+ D3D12_RESOURCE_STATE_COMMON,
+ needsOptimizedClearValueSpecified ? &clearValue : nullptr,
+ &allocation,
+ __uuidof(ID3D12Resource),
+ reinterpret_cast<void **>(&resource));
+ if (FAILED(hr)) {
+ qWarning("Failed to create texture: '%s'"
+ " Dim was %d Size was %ux%u Depth/ArraySize was %u MipLevels was %u Format was %d Sample count was %d",
+ qPrintable(QSystemError::windowsComString(hr)),
+ int(resourceDesc.Dimension),
+ uint(resourceDesc.Width),
+ uint(resourceDesc.Height),
+ uint(resourceDesc.DepthOrArraySize),
+ uint(resourceDesc.MipLevels),
+ int(resourceDesc.Format),
+ int(resourceDesc.SampleDesc.Count));
+ return false;
+ }
+
+ handle = QD3D12Resource::addToPool(&rhiD->resourcePool, resource, D3D12_RESOURCE_STATE_COMMON, allocation);
+
+ if (!finishCreate())
+ return false;
+
+ rhiD->registerResource(this);
+ return true;
+}
+
+bool QD3D12Texture::createFrom(QRhiTexture::NativeTexture src)
+{
+ if (!src.object)
+ return false;
+
+ if (!prepareCreate())
+ return false;
+
+ ID3D12Resource *resource = reinterpret_cast<ID3D12Resource *>(src.object);
+ D3D12_RESOURCE_STATES state = D3D12_RESOURCE_STATES(src.layout);
+
+ QRHI_RES_RHI(QRhiD3D12);
+ handle = QD3D12Resource::addNonOwningToPool(&rhiD->resourcePool, resource, state);
+
+ if (!finishCreate())
+ return false;
+
+ rhiD->registerResource(this);
+ return true;
+}
+
+QRhiTexture::NativeTexture QD3D12Texture::nativeTexture()
+{
+ QRHI_RES_RHI(QRhiD3D12);
+ if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handle))
+ return { quint64(res->resource), int(res->state) };
+
+ return {};
+}
+
+void QD3D12Texture::setNativeLayout(int layout)
+{
+ QRHI_RES_RHI(QRhiD3D12);
+ if (QD3D12Resource *res = rhiD->resourcePool.lookupRef(handle))
+ res->state = D3D12_RESOURCE_STATES(layout);
+}
+
+QD3D12Sampler::QD3D12Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v, AddressMode w)
+ : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v, w)
+{
+}
+
+QD3D12Sampler::~QD3D12Sampler()
+{
+ destroy();
+}
+
+void QD3D12Sampler::destroy()
+{
+ shaderVisibleDescriptor = {};
+
+ QRHI_RES_RHI(QRhiD3D12);
+ if (rhiD)
+ rhiD->unregisterResource(this);
+}
+
+static inline D3D12_FILTER toD3DFilter(QRhiSampler::Filter minFilter, QRhiSampler::Filter magFilter, QRhiSampler::Filter mipFilter)
+{
+ if (minFilter == QRhiSampler::Nearest) {
+ if (magFilter == QRhiSampler::Nearest) {
+ if (mipFilter == QRhiSampler::Linear)
+ return D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR;
+ else
+ return D3D12_FILTER_MIN_MAG_MIP_POINT;
+ } else {
+ if (mipFilter == QRhiSampler::Linear)
+ return D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR;
+ else
+ return D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
+ }
+ } else {
+ if (magFilter == QRhiSampler::Nearest) {
+ if (mipFilter == QRhiSampler::Linear)
+ return D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
+ else
+ return D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT;
+ } else {
+ if (mipFilter == QRhiSampler::Linear)
+ return D3D12_FILTER_MIN_MAG_MIP_LINEAR;
+ else
+ return D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
+ }
+ }
+ Q_UNREACHABLE_RETURN(D3D12_FILTER_MIN_MAG_MIP_LINEAR);
+}
+
+static inline D3D12_TEXTURE_ADDRESS_MODE toD3DAddressMode(QRhiSampler::AddressMode m)
+{
+ switch (m) {
+ case QRhiSampler::Repeat:
+ return D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+ case QRhiSampler::ClampToEdge:
+ return D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+ case QRhiSampler::Mirror:
+ return D3D12_TEXTURE_ADDRESS_MODE_MIRROR;
+ }
+ Q_UNREACHABLE_RETURN(D3D12_TEXTURE_ADDRESS_MODE_CLAMP);
+}
+
+static inline D3D12_COMPARISON_FUNC toD3DTextureComparisonFunc(QRhiSampler::CompareOp op)
+{
+ switch (op) {
+ case QRhiSampler::Never:
+ return D3D12_COMPARISON_FUNC_NEVER;
+ case QRhiSampler::Less:
+ return D3D12_COMPARISON_FUNC_LESS;
+ case QRhiSampler::Equal:
+ return D3D12_COMPARISON_FUNC_EQUAL;
+ case QRhiSampler::LessOrEqual:
+ return D3D12_COMPARISON_FUNC_LESS_EQUAL;
+ case QRhiSampler::Greater:
+ return D3D12_COMPARISON_FUNC_GREATER;
+ case QRhiSampler::NotEqual:
+ return D3D12_COMPARISON_FUNC_NOT_EQUAL;
+ case QRhiSampler::GreaterOrEqual:
+ return D3D12_COMPARISON_FUNC_GREATER_EQUAL;
+ case QRhiSampler::Always:
+ return D3D12_COMPARISON_FUNC_ALWAYS;
+ }
+ Q_UNREACHABLE_RETURN(D3D12_COMPARISON_FUNC_NEVER);
+}
+
+bool QD3D12Sampler::create()
+{
+ desc = {};
+ desc.Filter = toD3DFilter(m_minFilter, m_magFilter, m_mipmapMode);
+ if (m_compareOp != Never)
+ desc.Filter = D3D12_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 : 10000.0f;
+
+ QRHI_RES_RHI(QRhiD3D12);
+ rhiD->registerResource(this, false);
+ return true;
+}
+
+QD3D12Descriptor QD3D12Sampler::lookupOrCreateShaderVisibleDescriptor()
+{
+ if (!shaderVisibleDescriptor.isValid()) {
+ QRHI_RES_RHI(QRhiD3D12);
+ shaderVisibleDescriptor = rhiD->samplerMgr.getShaderVisibleDescriptor(desc);
+ }
+ return shaderVisibleDescriptor;
+}
+
+QD3D12TextureRenderTarget::QD3D12TextureRenderTarget(QRhiImplementation *rhi,
+ const QRhiTextureRenderTargetDescription &desc,
+ Flags flags)
+ : QRhiTextureRenderTarget(rhi, desc, flags),
+ d(rhi)
+{
+}
+
+QD3D12TextureRenderTarget::~QD3D12TextureRenderTarget()
+{
+ destroy();
+}
+
+void QD3D12TextureRenderTarget::destroy()
+{
+ if (!rtv[0].isValid() && !dsv.isValid())
+ return;
+
+ QRHI_RES_RHI(QRhiD3D12);
+ if (dsv.isValid()) {
+ if (ownsDsv && rhiD)
+ rhiD->releaseQueue.deferredReleaseViews(&rhiD->dsvPool, dsv, 1);
+ dsv = {};
+ }
+
+ for (int i = 0; i < QD3D12RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) {
+ if (rtv[i].isValid()) {
+ if (ownsRtv[i] && rhiD)
+ rhiD->releaseQueue.deferredReleaseViews(&rhiD->rtvPool, rtv[i], 1);
+ rtv[i] = {};
+ }
+ }
+
+ if (rhiD)
+ rhiD->unregisterResource(this);
+}
+
+QRhiRenderPassDescriptor *QD3D12TextureRenderTarget::newCompatibleRenderPassDescriptor()
+{
+ // not yet built so cannot rely on data computed in create()
+
+ QD3D12RenderPassDescriptor *rpD = new QD3D12RenderPassDescriptor(m_rhi);
+
+ rpD->colorAttachmentCount = 0;
+ for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it) {
+ QD3D12Texture *texD = QRHI_RES(QD3D12Texture, it->texture());
+ QD3D12RenderBuffer *rbD = QRHI_RES(QD3D12RenderBuffer, it->renderBuffer());
+ if (texD)
+ rpD->colorFormat[rpD->colorAttachmentCount] = texD->rtFormat;
+ else if (rbD)
+ rpD->colorFormat[rpD->colorAttachmentCount] = rbD->dxgiFormat;
+ rpD->colorAttachmentCount += 1;
+ }
+
+ rpD->hasDepthStencil = false;
+ if (m_desc.depthStencilBuffer()) {
+ rpD->hasDepthStencil = true;
+ rpD->dsFormat = QD3D12RenderBuffer::DS_FORMAT;
+ } else if (m_desc.depthTexture()) {
+ QD3D12Texture *depthTexD = QRHI_RES(QD3D12Texture, m_desc.depthTexture());
+ rpD->hasDepthStencil = true;
+ rpD->dsFormat = toD3DDepthTextureDSVFormat(depthTexD->format()); // cannot be a typeless format
+ }
+
+ rpD->updateSerializedFormat();
+
+ QRHI_RES_RHI(QRhiD3D12);
+ rhiD->registerResource(rpD);
+ return rpD;
+}
+
+bool QD3D12TextureRenderTarget::create()
+{
+ if (rtv[0].isValid() || dsv.isValid())
+ destroy();
+
+ QRHI_RES_RHI(QRhiD3D12);
+ Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
+ Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
+ const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
+ d.colorAttCount = 0;
+ int attIndex = 0;
+
+ for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
+ d.colorAttCount += 1;
+ const QRhiColorAttachment &colorAtt(*it);
+ QRhiTexture *texture = colorAtt.texture();
+ QRhiRenderBuffer *rb = colorAtt.renderBuffer();
+ Q_ASSERT(texture || rb);
+ if (texture) {
+ QD3D12Texture *texD = QRHI_RES(QD3D12Texture, texture);
+ QD3D12Resource *res = rhiD->resourcePool.lookupRef(texD->handle);
+ if (!res) {
+ qWarning("Could not look up texture handle for render target");
+ return false;
+ }
+ const bool isMultiView = it->multiViewCount() >= 2;
+ UINT layerCount = isMultiView ? UINT(it->multiViewCount()) : 1;
+ D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
+ rtvDesc.Format = texD->rtFormat;
+ if (texD->flags().testFlag(QRhiTexture::CubeMap)) {
+ rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
+ rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
+ rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
+ rtvDesc.Texture2DArray.ArraySize = layerCount;
+ } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) {
+ if (texD->flags().testFlag(QRhiTexture::TextureArray)) {
+ rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1DARRAY;
+ rtvDesc.Texture1DArray.MipSlice = UINT(colorAtt.level());
+ rtvDesc.Texture1DArray.FirstArraySlice = UINT(colorAtt.layer());
+ rtvDesc.Texture1DArray.ArraySize = layerCount;
+ } else {
+ rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1D;
+ rtvDesc.Texture1D.MipSlice = UINT(colorAtt.level());
+ }
+ } else if (texD->flags().testFlag(QRhiTexture::TextureArray)) {
+ if (texD->sampleDesc.Count > 1) {
+ rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY;
+ rtvDesc.Texture2DMSArray.FirstArraySlice = UINT(colorAtt.layer());
+ rtvDesc.Texture2DMSArray.ArraySize = layerCount;
+ } else {
+ rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
+ rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
+ rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
+ rtvDesc.Texture2DArray.ArraySize = layerCount;
+ }
+ } else if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) {
+ rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D;
+ rtvDesc.Texture3D.MipSlice = UINT(colorAtt.level());
+ rtvDesc.Texture3D.FirstWSlice = UINT(colorAtt.layer());
+ rtvDesc.Texture3D.WSize = layerCount;
+ } else {
+ if (texD->sampleDesc.Count > 1) {
+ rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS;
+ } else {
+ rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
+ rtvDesc.Texture2D.MipSlice = UINT(colorAtt.level());
+ }
+ }
+ rtv[attIndex] = rhiD->rtvPool.allocate(1);
+ if (!rtv[attIndex].isValid()) {
+ qWarning("Failed to allocate RTV for texture render target");
+ return false;
+ }
+ rhiD->dev->CreateRenderTargetView(res->resource, &rtvDesc, rtv[attIndex].cpuHandle);
+ ownsRtv[attIndex] = true;
+ if (attIndex == 0) {
+ d.pixelSize = rhiD->q->sizeForMipLevel(colorAtt.level(), texD->pixelSize());
+ d.sampleCount = int(texD->sampleDesc.Count);
+ }
+ } else if (rb) {
+ QD3D12RenderBuffer *rbD = QRHI_RES(QD3D12RenderBuffer, rb);
+ ownsRtv[attIndex] = false;
+ rtv[attIndex] = rbD->rtv;
+ if (attIndex == 0) {
+ d.pixelSize = rbD->pixelSize();
+ d.sampleCount = int(rbD->sampleDesc.Count);
+ }
+ }
+ }
+
+ d.dpr = 1;
+
+ if (hasDepthStencil) {
+ if (m_desc.depthTexture()) {
+ ownsDsv = true;
+ QD3D12Texture *depthTexD = QRHI_RES(QD3D12Texture, m_desc.depthTexture());
+ QD3D12Resource *res = rhiD->resourcePool.lookupRef(depthTexD->handle);
+ if (!res) {
+ qWarning("Could not look up depth texture handle");
+ return false;
+ }
+ D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
+ dsvDesc.Format = depthTexD->rtFormat;
+ dsvDesc.ViewDimension = depthTexD->sampleDesc.Count > 1 ? D3D12_DSV_DIMENSION_TEXTURE2DMS
+ : D3D12_DSV_DIMENSION_TEXTURE2D;
+ if (depthTexD->flags().testFlag(QRhiTexture::TextureArray)) {
+ if (depthTexD->sampleDesc.Count > 1) {
+ dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY;
+ if (depthTexD->arrayRangeStart() >= 0 && depthTexD->arrayRangeLength() >= 0) {
+ dsvDesc.Texture2DMSArray.FirstArraySlice = UINT(depthTexD->arrayRangeStart());
+ dsvDesc.Texture2DMSArray.ArraySize = UINT(depthTexD->arrayRangeLength());
+ } else {
+ dsvDesc.Texture2DMSArray.FirstArraySlice = 0;
+ dsvDesc.Texture2DMSArray.ArraySize = UINT(qMax(0, depthTexD->arraySize()));
+ }
+ } else {
+ dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
+ if (depthTexD->arrayRangeStart() >= 0 && depthTexD->arrayRangeLength() >= 0) {
+ dsvDesc.Texture2DArray.FirstArraySlice = UINT(depthTexD->arrayRangeStart());
+ dsvDesc.Texture2DArray.ArraySize = UINT(depthTexD->arrayRangeLength());
+ } else {
+ dsvDesc.Texture2DArray.FirstArraySlice = 0;
+ dsvDesc.Texture2DArray.ArraySize = UINT(qMax(0, depthTexD->arraySize()));
+ }
+ }
+ }
+ dsv = rhiD->dsvPool.allocate(1);
+ if (!dsv.isValid()) {
+ qWarning("Failed to allocate DSV for texture render target");
+ return false;
+ }
+ rhiD->dev->CreateDepthStencilView(res->resource, &dsvDesc, dsv.cpuHandle);
+ if (d.colorAttCount == 0) {
+ d.pixelSize = depthTexD->pixelSize();
+ d.sampleCount = int(depthTexD->sampleDesc.Count);
+ }
+ } else {
+ ownsDsv = false;
+ QD3D12RenderBuffer *depthRbD = QRHI_RES(QD3D12RenderBuffer, m_desc.depthStencilBuffer());
+ dsv = depthRbD->dsv;
+ if (d.colorAttCount == 0) {
+ d.pixelSize = m_desc.depthStencilBuffer()->pixelSize();
+ d.sampleCount = int(depthRbD->sampleDesc.Count);
+ }
+ }
+ d.dsAttCount = 1;
+ } else {
+ d.dsAttCount = 0;
+ }
+
+ D3D12_CPU_DESCRIPTOR_HANDLE nullDescHandle = { 0 };
+ for (int i = 0; i < QD3D12RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i)
+ d.rtv[i] = i < d.colorAttCount ? rtv[i].cpuHandle : nullDescHandle;
+ d.dsv = dsv.cpuHandle;
+ d.rp = QRHI_RES(QD3D12RenderPassDescriptor, m_renderPassDesc);
+
+ QRhiRenderTargetAttachmentTracker::updateResIdList<QD3D12Texture, QD3D12RenderBuffer>(m_desc, &d.currentResIdList);
+
+ rhiD->registerResource(this);
+ return true;
+}
+
+QSize QD3D12TextureRenderTarget::pixelSize() const
+{
+ if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QD3D12Texture, QD3D12RenderBuffer>(m_desc, d.currentResIdList))
+ const_cast<QD3D12TextureRenderTarget *>(this)->create();
+
+ return d.pixelSize;
+}
+
+float QD3D12TextureRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QD3D12TextureRenderTarget::sampleCount() const
+{
+ return d.sampleCount;
+}
+
+QD3D12ShaderResourceBindings::QD3D12ShaderResourceBindings(QRhiImplementation *rhi)
+ : QRhiShaderResourceBindings(rhi)
+{
+}
+
+QD3D12ShaderResourceBindings::~QD3D12ShaderResourceBindings()
+{
+ destroy();
+}
+
+void QD3D12ShaderResourceBindings::destroy()
+{
+ QRHI_RES_RHI(QRhiD3D12);
+ if (rhiD)
+ rhiD->unregisterResource(this);
+}
+
+bool QD3D12ShaderResourceBindings::create()
+{
+ QRHI_RES_RHI(QRhiD3D12);
+ if (!rhiD->sanityCheckShaderResourceBindings(this))
+ return false;
+
+ rhiD->updateLayoutDesc(this);
+
+ hasDynamicOffset = false;
+ for (const QRhiShaderResourceBinding &b : std::as_const(m_bindings)) {
+ const QRhiShaderResourceBinding::Data *bd = QRhiImplementation::shaderResourceBindingData(b);
+ if (bd->type == QRhiShaderResourceBinding::UniformBuffer && bd->u.ubuf.hasDynamicOffset) {
+ hasDynamicOffset = true;
+ break;
+ }
+ }
+
+ // The root signature is not part of the srb. Unintuitive, but the shader
+ // translation pipeline ties our hands: as long as the per-shader (so per
+ // stage!) nativeResourceBindingMap exist, meaning f.ex. that a SPIR-V
+ // combined image sampler binding X passed in here may map to the tY and sY
+ // HLSL registers, where Y is known only once the mapping table from the
+ // shader is looked up. Creating a root parameters at this stage is
+ // therefore impossible.
+
+ generation += 1;
+ rhiD->registerResource(this, false);
+ return true;
+}
+
+void QD3D12ShaderResourceBindings::updateResources(UpdateFlags flags)
+{
+ Q_UNUSED(flags);
+ generation += 1;
+}
+
+// Accessing the QRhiBuffer/Texture/Sampler resources must be avoided in the
+// callbacks; that would only be possible if the srb had those specified, and
+// that's not required at the time of srb and pipeline create() time, and
+// createRootSignature is called from the pipeline create().
+
+void QD3D12ShaderResourceBindings::visitUniformBuffer(QD3D12Stage s,
+ const QRhiShaderResourceBinding::Data::UniformBufferData &,
+ int shaderRegister,
+ int)
+{
+ D3D12_ROOT_PARAMETER1 rootParam = {};
+ rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+ rootParam.ShaderVisibility = qd3d12_stageToVisibility(s);
+ rootParam.Descriptor.ShaderRegister = shaderRegister;
+ rootParam.Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC;
+ visitorData.cbParams[s].append(rootParam);
+}
+
+void QD3D12ShaderResourceBindings::visitTexture(QD3D12Stage s,
+ const QRhiShaderResourceBinding::TextureAndSampler &,
+ int shaderRegister)
+{
+ D3D12_DESCRIPTOR_RANGE1 range = {};
+ range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+ range.NumDescriptors = 1;
+ range.BaseShaderRegister = shaderRegister;
+ range.OffsetInDescriptorsFromTableStart = visitorData.currentSrvRangeOffset[s];
+ visitorData.currentSrvRangeOffset[s] += 1;
+ visitorData.srvRanges[s].append(range);
+ if (visitorData.srvRanges[s].count() == 1) {
+ visitorData.srvTables[s].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ visitorData.srvTables[s].ShaderVisibility = qd3d12_stageToVisibility(s);
+ }
+}
+
+void QD3D12ShaderResourceBindings::visitSampler(QD3D12Stage s,
+ const QRhiShaderResourceBinding::TextureAndSampler &,
+ int shaderRegister)
+{
+ // Unlike SRVs and UAVs, samplers are handled so that each sampler becomes
+ // a root parameter with its own descriptor table.
+
+ int &rangeStoreIdx(visitorData.samplerRangeHeads[s]);
+ if (rangeStoreIdx == 16) {
+ qWarning("Sampler count in QD3D12Stage %d exceeds the limit of 16, this is disallowed by QRhi", s);
+ return;
+ }
+ D3D12_DESCRIPTOR_RANGE1 range = {};
+ range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
+ range.NumDescriptors = 1;
+ range.BaseShaderRegister = shaderRegister;
+ visitorData.samplerRanges[s][rangeStoreIdx] = range;
+ D3D12_ROOT_PARAMETER1 param = {};
+ param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ param.ShaderVisibility = qd3d12_stageToVisibility(s);
+ param.DescriptorTable.NumDescriptorRanges = 1;
+ param.DescriptorTable.pDescriptorRanges = &visitorData.samplerRanges[s][rangeStoreIdx];
+ rangeStoreIdx += 1;
+ visitorData.samplerTables[s].append(param);
+}
+
+void QD3D12ShaderResourceBindings::visitStorageBuffer(QD3D12Stage s,
+ const QRhiShaderResourceBinding::Data::StorageBufferData &,
+ QD3D12ShaderResourceVisitor::StorageOp,
+ int shaderRegister)
+{
+ D3D12_DESCRIPTOR_RANGE1 range = {};
+ range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
+ range.NumDescriptors = 1;
+ range.BaseShaderRegister = shaderRegister;
+ range.OffsetInDescriptorsFromTableStart = visitorData.currentUavRangeOffset[s];
+ visitorData.currentUavRangeOffset[s] += 1;
+ visitorData.uavRanges[s].append(range);
+ if (visitorData.uavRanges[s].count() == 1) {
+ visitorData.uavTables[s].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ visitorData.uavTables[s].ShaderVisibility = qd3d12_stageToVisibility(s);
+ }
+}
+
+void QD3D12ShaderResourceBindings::visitStorageImage(QD3D12Stage s,
+ const QRhiShaderResourceBinding::Data::StorageImageData &,
+ QD3D12ShaderResourceVisitor::StorageOp,
+ int shaderRegister)
+{
+ D3D12_DESCRIPTOR_RANGE1 range = {};
+ range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
+ range.NumDescriptors = 1;
+ range.BaseShaderRegister = shaderRegister;
+ range.OffsetInDescriptorsFromTableStart = visitorData.currentUavRangeOffset[s];
+ visitorData.currentUavRangeOffset[s] += 1;
+ visitorData.uavRanges[s].append(range);
+ if (visitorData.uavRanges[s].count() == 1) {
+ visitorData.uavTables[s].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ visitorData.uavTables[s].ShaderVisibility = qd3d12_stageToVisibility(s);
+ }
+}
+
+QD3D12ObjectHandle QD3D12ShaderResourceBindings::createRootSignature(const QD3D12ShaderStageData *stageData,
+ int stageCount)
+{
+ QRHI_RES_RHI(QRhiD3D12);
+
+ // It's not just that the root signature has to be tied to the pipeline
+ // (cannot just freely create it like e.g. with Vulkan where one just
+ // creates a descriptor layout 1:1 with the QRhiShaderResourceBindings'
+ // data), due to not knowing the shader-specific resource binding mapping
+ // tables at the point of srb creation, but each shader stage may have a
+ // different mapping table. (ugh!)
+ //
+ // Hence we set up everything per-stage, even if it means the root
+ // signature gets unnecessarily big. (note that the magic is in the
+ // ShaderVisibility: even though the register range is the same in the
+ // descriptor tables, the visibility is different)
+
+ QD3D12ShaderResourceVisitor visitor(this, stageData, stageCount);
+
+ visitorData = {};
+
+ using namespace std::placeholders;
+ visitor.uniformBuffer = std::bind(&QD3D12ShaderResourceBindings::visitUniformBuffer, this, _1, _2, _3, _4);
+ visitor.texture = std::bind(&QD3D12ShaderResourceBindings::visitTexture, this, _1, _2, _3);
+ visitor.sampler = std::bind(&QD3D12ShaderResourceBindings::visitSampler, this, _1, _2, _3);
+ visitor.storageBuffer = std::bind(&QD3D12ShaderResourceBindings::visitStorageBuffer, this, _1, _2, _3, _4);
+ visitor.storageImage = std::bind(&QD3D12ShaderResourceBindings::visitStorageImage, this, _1, _2, _3, _4);
+
+ visitor.visit();
+
+ // The maximum size of a root signature is 256 bytes, where a descriptor
+ // table is 4, a root descriptor (e.g. CBV) is 8. We have 5 stages at most
+ // (or 1 with compute) and a separate descriptor table for SRVs (->
+ // textures) and UAVs (-> storage buffers and images) per stage, plus each
+ // uniform buffer counts as a CBV in the stages it is visible.
+ //
+ // Due to the limited maximum size of a shader-visible sampler heap (2048)
+ // and the potential costly switching of descriptor heaps, each sampler is
+ // declared as a separate root parameter / descriptor table (meaning that
+ // two samplers in the same stage are two parameters and two tables, not
+ // just one). QRhi documents a hard limit of 16 on texture/sampler bindings
+ // in a shader (matching D3D11), so we can hopefully get away with this.
+ //
+ // This means that e.g. a vertex+fragment shader with a uniform buffer
+ // visible in both and one texture+sampler in the fragment shader would
+ // consume 2*8 + 4 + 4 = 24 bytes. This also implies that clients
+ // specifying the minimal stage bit mask for each entry in
+ // QRhiShaderResourceBindings are ideal for this backend since it helps
+ // reducing the chance of hitting the size limit.
+
+ QVarLengthArray<D3D12_ROOT_PARAMETER1, 4> rootParams;
+ for (int s = 0; s < 6; ++s) {
+ if (!visitorData.cbParams[s].isEmpty())
+ rootParams.append(visitorData.cbParams[s].constData(), visitorData.cbParams[s].count());
+ }
+ for (int s = 0; s < 6; ++s) {
+ if (!visitorData.srvRanges[s].isEmpty()) {
+ visitorData.srvTables[s].DescriptorTable.NumDescriptorRanges = visitorData.srvRanges[s].count();
+ visitorData.srvTables[s].DescriptorTable.pDescriptorRanges = visitorData.srvRanges[s].constData();
+ rootParams.append(visitorData.srvTables[s]);
+ }
+ }
+ for (int s = 0; s < 6; ++s) {
+ if (!visitorData.samplerTables[s].isEmpty())
+ rootParams.append(visitorData.samplerTables[s].constData(), visitorData.samplerTables[s].count());
+ }
+ for (int s = 0; s < 6; ++s) {
+ if (!visitorData.uavRanges[s].isEmpty()) {
+ visitorData.uavTables[s].DescriptorTable.NumDescriptorRanges = visitorData.uavRanges[s].count();
+ visitorData.uavTables[s].DescriptorTable.pDescriptorRanges = visitorData.uavRanges[s].constData();
+ rootParams.append(visitorData.uavTables[s]);
+ }
+ }
+
+ D3D12_VERSIONED_ROOT_SIGNATURE_DESC rsDesc = {};
+ rsDesc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1;
+ if (!rootParams.isEmpty()) {
+ rsDesc.Desc_1_1.NumParameters = rootParams.count();
+ rsDesc.Desc_1_1.pParameters = rootParams.constData();
+ }
+
+ UINT rsFlags = 0;
+ for (int stageIdx = 0; stageIdx < stageCount; ++stageIdx) {
+ if (stageData[stageIdx].valid && stageData[stageIdx].stage == VS)
+ rsFlags |= D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
+ }
+ rsDesc.Desc_1_1.Flags = D3D12_ROOT_SIGNATURE_FLAGS(rsFlags);
+
+ ID3DBlob *signature = nullptr;
+ HRESULT hr = D3D12SerializeVersionedRootSignature(&rsDesc, &signature, nullptr);
+ if (FAILED(hr)) {
+ qWarning("Failed to serialize root signature: %s", qPrintable(QSystemError::windowsComString(hr)));
+ return {};
+ }
+ ID3D12RootSignature *rootSig = nullptr;
+ hr = rhiD->dev->CreateRootSignature(0,
+ signature->GetBufferPointer(),
+ signature->GetBufferSize(),
+ __uuidof(ID3D12RootSignature),
+ reinterpret_cast<void **>(&rootSig));
+ signature->Release();
+ if (FAILED(hr)) {
+ qWarning("Failed to create root signature: %s", qPrintable(QSystemError::windowsComString(hr)));
+ return {};
+ }
+
+ return QD3D12RootSignature::addToPool(&rhiD->rootSignaturePool, rootSig);
+}
+
+// For shader model < 6.0 we do the same as the D3D11 backend: use the old
+// compiler (D3DCompile) to generate DXBC, just as qsb does (when -c is passed)
+// by invoking fxc, not dxc. For SM >= 6.0 we have to use the new compiler and
+// work with DXIL. And that involves IDxcCompiler and needs the presence of
+// dxcompiler.dll and dxil.dll at runtime. Plus there's a chance we have
+// ancient SDK headers when not using MSVC. So this is heavily optional,
+// meaning support for dxc can be disabled both at build time (no dxcapi.h) and
+// at run time (no DLLs).
+
+static inline void makeHlslTargetString(char target[7], const char stage[3], int version)
+{
+ const int smMajor = version / 10;
+ const int smMinor = version % 10;
+ target[0] = stage[0];
+ target[1] = stage[1];
+ target[2] = '_';
+ target[3] = '0' + smMajor;
+ target[4] = '_';
+ target[5] = '0' + smMinor;
+ target[6] = '\0';
+}
+
+enum class HlslCompileFlag
+{
+ WithDebugInfo = 0x01
+};
+
+static QByteArray legacyCompile(const QShaderCode &hlslSource, const char *target, int flags, QString *error)
+{
+ static const pD3DCompile d3dCompile = QRhiD3D::resolveD3DCompile();
+ if (!d3dCompile) {
+ qWarning("Unable to resolve function D3DCompile()");
+ return QByteArray();
+ }
+
+ ID3DBlob *bytecode = nullptr;
+ ID3DBlob *errors = nullptr;
+ UINT d3dCompileFlags = 0;
+ if (flags & int(HlslCompileFlag::WithDebugInfo))
+ d3dCompileFlags |= D3DCOMPILE_DEBUG;
+
+ HRESULT hr = d3dCompile(hlslSource.shader().constData(), SIZE_T(hlslSource.shader().size()),
+ nullptr, nullptr, nullptr,
+ hlslSource.entryPoint().constData(), target, d3dCompileFlags, 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()),
+ int(errors->GetBufferSize()));
+ errors->Release();
+ }
+ return QByteArray();
+ }
+
+ QByteArray result;
+ result.resize(int(bytecode->GetBufferSize()));
+ memcpy(result.data(), bytecode->GetBufferPointer(), size_t(result.size()));
+ bytecode->Release();
+ return result;
+}
+
+#ifdef QRHI_D3D12_HAS_DXC
+
+#ifndef DXC_CP_UTF8
+#define DXC_CP_UTF8 65001
+#endif
+
+#ifndef DXC_ARG_DEBUG
+#define DXC_ARG_DEBUG L"-Zi"
+#endif
+
+static QByteArray dxcCompile(const QShaderCode &hlslSource, const char *target, int flags, QString *error)
+{
+ static std::pair<IDxcCompiler *, IDxcLibrary *> dxc = QRhiD3D::createDxcCompiler();
+ IDxcCompiler *compiler = dxc.first;
+ if (!compiler) {
+ qWarning("Unable to instantiate IDxcCompiler. Likely no dxcompiler.dll and dxil.dll present. "
+ "Use windeployqt or try https://github.com/microsoft/DirectXShaderCompiler/releases");
+ return QByteArray();
+ }
+ IDxcLibrary *library = dxc.second;
+ if (!library)
+ return QByteArray();
+
+ IDxcBlobEncoding *sourceBlob = nullptr;
+ HRESULT hr = library->CreateBlobWithEncodingOnHeapCopy(hlslSource.shader().constData(),
+ UINT32(hlslSource.shader().size()),
+ DXC_CP_UTF8,
+ &sourceBlob);
+ if (FAILED(hr)) {
+ qWarning("Failed to create source blob for dxc: 0x%x (%s)",
+ uint(hr),
+ qPrintable(QSystemError::windowsComString(hr)));
+ return QByteArray();
+ }
+
+ const QString entryPointStr = QString::fromLatin1(hlslSource.entryPoint());
+ const QString targetStr = QString::fromLatin1(target);
+
+ QVarLengthArray<LPCWSTR, 4> argPtrs;
+ QString debugArg;
+ if (flags & int(HlslCompileFlag::WithDebugInfo)) {
+ debugArg = QString::fromUtf16(reinterpret_cast<const char16_t *>(DXC_ARG_DEBUG));
+ argPtrs.append(reinterpret_cast<LPCWSTR>(debugArg.utf16()));
+ }
+
+ IDxcOperationResult *result = nullptr;
+ hr = compiler->Compile(sourceBlob,
+ nullptr,
+ reinterpret_cast<LPCWSTR>(entryPointStr.utf16()),
+ reinterpret_cast<LPCWSTR>(targetStr.utf16()),
+ argPtrs.data(), argPtrs.count(),
+ nullptr, 0,
+ nullptr,
+ &result);
+ sourceBlob->Release();
+ if (SUCCEEDED(hr))
+ result->GetStatus(&hr);
+ if (FAILED(hr)) {
+ qWarning("HLSL shader compilation failed: 0x%x (%s)",
+ uint(hr),
+ qPrintable(QSystemError::windowsComString(hr)));
+ if (result) {
+ IDxcBlobEncoding *errorsBlob = nullptr;
+ if (SUCCEEDED(result->GetErrorBuffer(&errorsBlob))) {
+ if (errorsBlob) {
+ *error = QString::fromUtf8(static_cast<const char *>(errorsBlob->GetBufferPointer()),
+ int(errorsBlob->GetBufferSize()));
+ errorsBlob->Release();
+ }
+ }
+ }
+ return QByteArray();
+ }
+
+ IDxcBlob *bytecode = nullptr;
+ if FAILED(result->GetResult(&bytecode)) {
+ qWarning("No result from IDxcCompiler: 0x%x (%s)",
+ uint(hr),
+ qPrintable(QSystemError::windowsComString(hr)));
+ return QByteArray();
+ }
+
+ QByteArray ba;
+ ba.resize(int(bytecode->GetBufferSize()));
+ memcpy(ba.data(), bytecode->GetBufferPointer(), size_t(ba.size()));
+ bytecode->Release();
+ return ba;
+}
+
+#endif // QRHI_D3D12_HAS_DXC
+
+static QByteArray compileHlslShaderSource(const QShader &shader,
+ QShader::Variant shaderVariant,
+ int flags,
+ QString *error,
+ QShaderKey *usedShaderKey)
+{
+ // look for SM 6.7, 6.6, .., 5.0
+ const int shaderModelMax = 67;
+ for (int sm = shaderModelMax; sm >= 50; --sm) {
+ for (QShader::Source type : { QShader::DxilShader, QShader::DxbcShader }) {
+ QShaderKey key = { type, sm, shaderVariant };
+ QShaderCode intermediateBytecodeShader = shader.shader(key);
+ if (!intermediateBytecodeShader.shader().isEmpty()) {
+ if (usedShaderKey)
+ *usedShaderKey = key;
+ return intermediateBytecodeShader.shader();
+ }
+ }
+ }
+
+ QShaderCode hlslSource;
+ QShaderKey key;
+ for (int sm = shaderModelMax; sm >= 50; --sm) {
+ key = { QShader::HlslShader, sm, shaderVariant };
+ hlslSource = shader.shader(key);
+ if (!hlslSource.shader().isEmpty())
+ break;
+ }
+
+ if (hlslSource.shader().isEmpty()) {
+ qWarning() << "No HLSL (shader model 6.7..5.0) code found in baked shader" << shader;
+ return QByteArray();
+ }
+
+ if (usedShaderKey)
+ *usedShaderKey = key;
+
+ char target[7];
+ switch (shader.stage()) {
+ case QShader::VertexStage:
+ makeHlslTargetString(target, "vs", key.sourceVersion().version());
+ break;
+ case QShader::TessellationControlStage:
+ makeHlslTargetString(target, "hs", key.sourceVersion().version());
+ break;
+ case QShader::TessellationEvaluationStage:
+ makeHlslTargetString(target, "ds", key.sourceVersion().version());
+ break;
+ case QShader::GeometryStage:
+ makeHlslTargetString(target, "gs", key.sourceVersion().version());
+ break;
+ case QShader::FragmentStage:
+ makeHlslTargetString(target, "ps", key.sourceVersion().version());
+ break;
+ case QShader::ComputeStage:
+ makeHlslTargetString(target, "cs", key.sourceVersion().version());
+ break;
+ }
+
+ if (key.sourceVersion().version() >= 60) {
+#ifdef QRHI_D3D12_HAS_DXC
+ return dxcCompile(hlslSource, target, flags, error);
+#else
+ qWarning("Attempted to runtime-compile HLSL source code for shader model >= 6.0 "
+ "but the Qt build has no support for DXC. "
+ "Rebuild Qt with a recent Windows SDK or switch to an MSVC build.");
+#endif
+ }
+
+ return legacyCompile(hlslSource, target, flags, error);
+}
+
+static inline UINT8 toD3DColorWriteMask(QRhiGraphicsPipeline::ColorMask c)
+{
+ UINT8 f = 0;
+ if (c.testFlag(QRhiGraphicsPipeline::R))
+ f |= D3D12_COLOR_WRITE_ENABLE_RED;
+ if (c.testFlag(QRhiGraphicsPipeline::G))
+ f |= D3D12_COLOR_WRITE_ENABLE_GREEN;
+ if (c.testFlag(QRhiGraphicsPipeline::B))
+ f |= D3D12_COLOR_WRITE_ENABLE_BLUE;
+ if (c.testFlag(QRhiGraphicsPipeline::A))
+ f |= D3D12_COLOR_WRITE_ENABLE_ALPHA;
+ return f;
+}
+
+static inline D3D12_BLEND toD3DBlendFactor(QRhiGraphicsPipeline::BlendFactor f, bool rgb)
+{
+ // SrcBlendAlpha and DstBlendAlpha do not accept *_COLOR. With other APIs
+ // this is handled internally (so that e.g. VK_BLEND_FACTOR_SRC_COLOR is
+ // accepted and is in effect equivalent to VK_BLEND_FACTOR_SRC_ALPHA when
+ // set as an alpha src/dest factor), but for D3D we have to take care of it
+ // ourselves. Hence the rgb argument.
+
+ switch (f) {
+ case QRhiGraphicsPipeline::Zero:
+ return D3D12_BLEND_ZERO;
+ case QRhiGraphicsPipeline::One:
+ return D3D12_BLEND_ONE;
+ case QRhiGraphicsPipeline::SrcColor:
+ return rgb ? D3D12_BLEND_SRC_COLOR : D3D12_BLEND_SRC_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusSrcColor:
+ return rgb ? D3D12_BLEND_INV_SRC_COLOR : D3D12_BLEND_INV_SRC_ALPHA;
+ case QRhiGraphicsPipeline::DstColor:
+ return rgb ? D3D12_BLEND_DEST_COLOR : D3D12_BLEND_DEST_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusDstColor:
+ return rgb ? D3D12_BLEND_INV_DEST_COLOR : D3D12_BLEND_INV_DEST_ALPHA;
+ case QRhiGraphicsPipeline::SrcAlpha:
+ return D3D12_BLEND_SRC_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusSrcAlpha:
+ return D3D12_BLEND_INV_SRC_ALPHA;
+ case QRhiGraphicsPipeline::DstAlpha:
+ return D3D12_BLEND_DEST_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusDstAlpha:
+ return D3D12_BLEND_INV_DEST_ALPHA;
+ case QRhiGraphicsPipeline::ConstantColor:
+ case QRhiGraphicsPipeline::ConstantAlpha:
+ return D3D12_BLEND_BLEND_FACTOR;
+ case QRhiGraphicsPipeline::OneMinusConstantColor:
+ case QRhiGraphicsPipeline::OneMinusConstantAlpha:
+ return D3D12_BLEND_INV_BLEND_FACTOR;
+ case QRhiGraphicsPipeline::SrcAlphaSaturate:
+ return D3D12_BLEND_SRC_ALPHA_SAT;
+ case QRhiGraphicsPipeline::Src1Color:
+ return rgb ? D3D12_BLEND_SRC1_COLOR : D3D12_BLEND_SRC1_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusSrc1Color:
+ return rgb ? D3D12_BLEND_INV_SRC1_COLOR : D3D12_BLEND_INV_SRC1_ALPHA;
+ case QRhiGraphicsPipeline::Src1Alpha:
+ return D3D12_BLEND_SRC1_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
+ return D3D12_BLEND_INV_SRC1_ALPHA;
+ }
+ Q_UNREACHABLE_RETURN(D3D12_BLEND_ZERO);
+}
+
+static inline D3D12_BLEND_OP toD3DBlendOp(QRhiGraphicsPipeline::BlendOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Add:
+ return D3D12_BLEND_OP_ADD;
+ case QRhiGraphicsPipeline::Subtract:
+ return D3D12_BLEND_OP_SUBTRACT;
+ case QRhiGraphicsPipeline::ReverseSubtract:
+ return D3D12_BLEND_OP_REV_SUBTRACT;
+ case QRhiGraphicsPipeline::Min:
+ return D3D12_BLEND_OP_MIN;
+ case QRhiGraphicsPipeline::Max:
+ return D3D12_BLEND_OP_MAX;
+ }
+ Q_UNREACHABLE_RETURN(D3D12_BLEND_OP_ADD);
+}
+
+static inline D3D12_CULL_MODE toD3DCullMode(QRhiGraphicsPipeline::CullMode c)
+{
+ switch (c) {
+ case QRhiGraphicsPipeline::None:
+ return D3D12_CULL_MODE_NONE;
+ case QRhiGraphicsPipeline::Front:
+ return D3D12_CULL_MODE_FRONT;
+ case QRhiGraphicsPipeline::Back:
+ return D3D12_CULL_MODE_BACK;
+ }
+ Q_UNREACHABLE_RETURN(D3D12_CULL_MODE_NONE);
+}
+
+static inline D3D12_FILL_MODE toD3DFillMode(QRhiGraphicsPipeline::PolygonMode mode)
+{
+ switch (mode) {
+ case QRhiGraphicsPipeline::Fill:
+ return D3D12_FILL_MODE_SOLID;
+ case QRhiGraphicsPipeline::Line:
+ return D3D12_FILL_MODE_WIREFRAME;
+ }
+ Q_UNREACHABLE_RETURN(D3D12_FILL_MODE_SOLID);
+}
+
+static inline D3D12_COMPARISON_FUNC toD3DCompareOp(QRhiGraphicsPipeline::CompareOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Never:
+ return D3D12_COMPARISON_FUNC_NEVER;
+ case QRhiGraphicsPipeline::Less:
+ return D3D12_COMPARISON_FUNC_LESS;
+ case QRhiGraphicsPipeline::Equal:
+ return D3D12_COMPARISON_FUNC_EQUAL;
+ case QRhiGraphicsPipeline::LessOrEqual:
+ return D3D12_COMPARISON_FUNC_LESS_EQUAL;
+ case QRhiGraphicsPipeline::Greater:
+ return D3D12_COMPARISON_FUNC_GREATER;
+ case QRhiGraphicsPipeline::NotEqual:
+ return D3D12_COMPARISON_FUNC_NOT_EQUAL;
+ case QRhiGraphicsPipeline::GreaterOrEqual:
+ return D3D12_COMPARISON_FUNC_GREATER_EQUAL;
+ case QRhiGraphicsPipeline::Always:
+ return D3D12_COMPARISON_FUNC_ALWAYS;
+ }
+ Q_UNREACHABLE_RETURN(D3D12_COMPARISON_FUNC_ALWAYS);
+}
+
+static inline D3D12_STENCIL_OP toD3DStencilOp(QRhiGraphicsPipeline::StencilOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::StencilZero:
+ return D3D12_STENCIL_OP_ZERO;
+ case QRhiGraphicsPipeline::Keep:
+ return D3D12_STENCIL_OP_KEEP;
+ case QRhiGraphicsPipeline::Replace:
+ return D3D12_STENCIL_OP_REPLACE;
+ case QRhiGraphicsPipeline::IncrementAndClamp:
+ return D3D12_STENCIL_OP_INCR_SAT;
+ case QRhiGraphicsPipeline::DecrementAndClamp:
+ return D3D12_STENCIL_OP_DECR_SAT;
+ case QRhiGraphicsPipeline::Invert:
+ return D3D12_STENCIL_OP_INVERT;
+ case QRhiGraphicsPipeline::IncrementAndWrap:
+ return D3D12_STENCIL_OP_INCR;
+ case QRhiGraphicsPipeline::DecrementAndWrap:
+ return D3D12_STENCIL_OP_DECR;
+ }
+ Q_UNREACHABLE_RETURN(D3D12_STENCIL_OP_KEEP);
+}
+
+static inline D3D12_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topology t, int patchControlPointCount)
+{
+ switch (t) {
+ case QRhiGraphicsPipeline::Triangles:
+ return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ case QRhiGraphicsPipeline::TriangleStrip:
+ return D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
+ case QRhiGraphicsPipeline::TriangleFan:
+ qWarning("Triangle fans are not supported with D3D");
+ return D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
+ case QRhiGraphicsPipeline::Lines:
+ return D3D_PRIMITIVE_TOPOLOGY_LINELIST;
+ case QRhiGraphicsPipeline::LineStrip:
+ return D3D_PRIMITIVE_TOPOLOGY_LINESTRIP;
+ case QRhiGraphicsPipeline::Points:
+ return D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
+ case QRhiGraphicsPipeline::Patches:
+ Q_ASSERT(patchControlPointCount >= 1 && patchControlPointCount <= 32);
+ return D3D_PRIMITIVE_TOPOLOGY(D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + (patchControlPointCount - 1));
+ }
+ Q_UNREACHABLE_RETURN(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+}
+
+static inline D3D12_PRIMITIVE_TOPOLOGY_TYPE toD3DTopologyType(QRhiGraphicsPipeline::Topology t)
+{
+ switch (t) {
+ case QRhiGraphicsPipeline::Triangles:
+ case QRhiGraphicsPipeline::TriangleStrip:
+ case QRhiGraphicsPipeline::TriangleFan:
+ return D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
+ case QRhiGraphicsPipeline::Lines:
+ case QRhiGraphicsPipeline::LineStrip:
+ return D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE;
+ case QRhiGraphicsPipeline::Points:
+ return D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT;
+ case QRhiGraphicsPipeline::Patches:
+ return D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
+ }
+ Q_UNREACHABLE_RETURN(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE);
+}
+
+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;
+ case QRhiVertexInputAttribute::UInt4:
+ return DXGI_FORMAT_R32G32B32A32_UINT;
+ case QRhiVertexInputAttribute::UInt3:
+ return DXGI_FORMAT_R32G32B32_UINT;
+ case QRhiVertexInputAttribute::UInt2:
+ return DXGI_FORMAT_R32G32_UINT;
+ case QRhiVertexInputAttribute::UInt:
+ return DXGI_FORMAT_R32_UINT;
+ case QRhiVertexInputAttribute::SInt4:
+ return DXGI_FORMAT_R32G32B32A32_SINT;
+ case QRhiVertexInputAttribute::SInt3:
+ return DXGI_FORMAT_R32G32B32_SINT;
+ case QRhiVertexInputAttribute::SInt2:
+ return DXGI_FORMAT_R32G32_SINT;
+ case QRhiVertexInputAttribute::SInt:
+ return DXGI_FORMAT_R32_SINT;
+ case QRhiVertexInputAttribute::Half4:
+ // Note: D3D does not support half3. Pass through half3 as half4.
+ case QRhiVertexInputAttribute::Half3:
+ return DXGI_FORMAT_R16G16B16A16_FLOAT;
+ case QRhiVertexInputAttribute::Half2:
+ return DXGI_FORMAT_R16G16_FLOAT;
+ case QRhiVertexInputAttribute::Half:
+ return DXGI_FORMAT_R16_FLOAT;
+ case QRhiVertexInputAttribute::UShort4:
+ // Note: D3D does not support UShort3. Pass through UShort3 as UShort4.
+ case QRhiVertexInputAttribute::UShort3:
+ return DXGI_FORMAT_R16G16B16A16_UINT;
+ case QRhiVertexInputAttribute::UShort2:
+ return DXGI_FORMAT_R16G16_UINT;
+ case QRhiVertexInputAttribute::UShort:
+ return DXGI_FORMAT_R16_UINT;
+ case QRhiVertexInputAttribute::SShort4:
+ // Note: D3D does not support SShort3. Pass through SShort3 as SShort4.
+ case QRhiVertexInputAttribute::SShort3:
+ return DXGI_FORMAT_R16G16B16A16_SINT;
+ case QRhiVertexInputAttribute::SShort2:
+ return DXGI_FORMAT_R16G16_SINT;
+ case QRhiVertexInputAttribute::SShort:
+ return DXGI_FORMAT_R16_SINT;
+ }
+ Q_UNREACHABLE_RETURN(DXGI_FORMAT_R32G32B32A32_FLOAT);
+}
+
+QD3D12GraphicsPipeline::QD3D12GraphicsPipeline(QRhiImplementation *rhi)
+ : QRhiGraphicsPipeline(rhi)
+{
+}
+
+QD3D12GraphicsPipeline::~QD3D12GraphicsPipeline()
+{
+ destroy();
+}
+
+void QD3D12GraphicsPipeline::destroy()
+{
+ if (handle.isNull())
+ return;
+
+ QRHI_RES_RHI(QRhiD3D12);
+ if (rhiD) {
+ rhiD->releaseQueue.deferredReleasePipeline(handle);
+ rhiD->releaseQueue.deferredReleaseRootSignature(rootSigHandle);
+ }
+
+ handle = {};
+ stageData = {};
+
+ if (rhiD)
+ rhiD->unregisterResource(this);
+}
+
+bool QD3D12GraphicsPipeline::create()
+{
+ if (!handle.isNull())
+ destroy();
+
+ QRHI_RES_RHI(QRhiD3D12);
+ if (!rhiD->sanityCheckGraphicsPipeline(this))
+ return false;
+
+ rhiD->pipelineCreationStart();
+
+ QByteArray shaderBytecode[5];
+ for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
+ const QD3D12Stage d3dStage = qd3d12_stage(shaderStage.type());
+ stageData[d3dStage].valid = true;
+ stageData[d3dStage].stage = d3dStage;
+ auto cacheIt = rhiD->shaderBytecodeCache.data.constFind(shaderStage);
+ if (cacheIt != rhiD->shaderBytecodeCache.data.constEnd()) {
+ shaderBytecode[d3dStage] = cacheIt->bytecode;
+ stageData[d3dStage].nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
+ } else {
+ QString error;
+ QShaderKey shaderKey;
+ int compileFlags = 0;
+ if (m_flags.testFlag(CompileShadersWithDebugInfo))
+ compileFlags |= int(HlslCompileFlag::WithDebugInfo);
+ const QByteArray bytecode = compileHlslShaderSource(shaderStage.shader(),
+ shaderStage.shaderVariant(),
+ compileFlags,
+ &error,
+ &shaderKey);
+ if (bytecode.isEmpty()) {
+ qWarning("HLSL graphics shader compilation failed: %s", qPrintable(error));
+ return false;
+ }
+
+ shaderBytecode[d3dStage] = bytecode;
+ stageData[d3dStage].nativeResourceBindingMap = shaderStage.shader().nativeResourceBindingMap(shaderKey);
+ rhiD->shaderBytecodeCache.insertWithCapacityLimit(shaderStage,
+ { bytecode, stageData[d3dStage].nativeResourceBindingMap });
+ }
+ }
+
+ QD3D12ShaderResourceBindings *srbD = QRHI_RES(QD3D12ShaderResourceBindings, m_shaderResourceBindings);
+ if (srbD) {
+ rootSigHandle = srbD->createRootSignature(stageData.data(), 5);
+ if (rootSigHandle.isNull()) {
+ qWarning("Failed to create root signature");
+ return false;
+ }
+ }
+ ID3D12RootSignature *rootSig = nullptr;
+ if (QD3D12RootSignature *rs = rhiD->rootSignaturePool.lookupRef(rootSigHandle))
+ rootSig = rs->rootSig;
+ if (!rootSig) {
+ qWarning("Cannot create graphics pipeline state without root signature");
+ return false;
+ }
+
+ QD3D12RenderPassDescriptor *rpD = QRHI_RES(QD3D12RenderPassDescriptor, m_renderPassDesc);
+ const DXGI_SAMPLE_DESC sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, DXGI_FORMAT(rpD->colorFormat[0]));
+
+ struct {
+ QD3D12PipelineStateSubObject<ID3D12RootSignature *, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE> rootSig;
+ QD3D12PipelineStateSubObject<D3D12_INPUT_LAYOUT_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT> inputLayout;
+ QD3D12PipelineStateSubObject<D3D12_PRIMITIVE_TOPOLOGY_TYPE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY> primitiveTopology;
+ QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS> VS;
+ QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_HS> HS;
+ QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DS> DS;
+ QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_GS> GS;
+ QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS> PS;
+ QD3D12PipelineStateSubObject<D3D12_RASTERIZER_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER> rasterizerState;
+ QD3D12PipelineStateSubObject<D3D12_DEPTH_STENCIL_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL> depthStencilState;
+ QD3D12PipelineStateSubObject<D3D12_BLEND_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND> blendState;
+ QD3D12PipelineStateSubObject<D3D12_RT_FORMAT_ARRAY, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS> rtFormats;
+ QD3D12PipelineStateSubObject<DXGI_FORMAT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT> dsFormat;
+ QD3D12PipelineStateSubObject<DXGI_SAMPLE_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC> sampleDesc;
+ QD3D12PipelineStateSubObject<UINT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK> sampleMask;
+ QD3D12PipelineStateSubObject<D3D12_VIEW_INSTANCING_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING> viewInstancingDesc;
+ } stream;
+
+ stream.rootSig.object = rootSig;
+
+ QVarLengthArray<D3D12_INPUT_ELEMENT_DESC, 4> inputDescs;
+ QByteArrayList matrixSliceSemantics;
+ if (!shaderBytecode[VS].isEmpty()) {
+ for (auto it = m_vertexInputLayout.cbeginAttributes(), itEnd = m_vertexInputLayout.cendAttributes();
+ it != itEnd; ++it)
+ {
+ D3D12_INPUT_ELEMENT_DESC desc = {};
+ // The output from SPIRV-Cross uses TEXCOORD<location> as the
+ // semantic, except for matrices that are unrolled into consecutive
+ // vec2/3/4s attributes and need TEXCOORD<location>_ as
+ // SemanticName and row/column index as SemanticIndex.
+ const int matrixSlice = it->matrixSlice();
+ if (matrixSlice < 0) {
+ desc.SemanticName = "TEXCOORD";
+ desc.SemanticIndex = UINT(it->location());
+ } else {
+ QByteArray sem;
+ sem.resize(16);
+ qsnprintf(sem.data(), sem.size(), "TEXCOORD%d_", it->location() - matrixSlice);
+ matrixSliceSemantics.append(sem);
+ desc.SemanticName = matrixSliceSemantics.last().constData();
+ desc.SemanticIndex = UINT(matrixSlice);
+ }
+ desc.Format = toD3DAttributeFormat(it->format());
+ desc.InputSlot = UINT(it->binding());
+ desc.AlignedByteOffset = it->offset();
+ const QRhiVertexInputBinding *inputBinding = m_vertexInputLayout.bindingAt(it->binding());
+ if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance) {
+ desc.InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA;
+ desc.InstanceDataStepRate = inputBinding->instanceStepRate();
+ } else {
+ desc.InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
+ }
+ inputDescs.append(desc);
+ }
+ }
+
+ stream.inputLayout.object.NumElements = inputDescs.count();
+ stream.inputLayout.object.pInputElementDescs = inputDescs.isEmpty() ? nullptr : inputDescs.constData();
+
+ stream.primitiveTopology.object = toD3DTopologyType(m_topology);
+ topology = toD3DTopology(m_topology, m_patchControlPointCount);
+
+ for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
+ const int d3dStage = qd3d12_stage(shaderStage.type());
+ switch (d3dStage) {
+ case VS:
+ stream.VS.object.pShaderBytecode = shaderBytecode[d3dStage].constData();
+ stream.VS.object.BytecodeLength = shaderBytecode[d3dStage].size();
+ break;
+ case HS:
+ stream.HS.object.pShaderBytecode = shaderBytecode[d3dStage].constData();
+ stream.HS.object.BytecodeLength = shaderBytecode[d3dStage].size();
+ break;
+ case DS:
+ stream.DS.object.pShaderBytecode = shaderBytecode[d3dStage].constData();
+ stream.DS.object.BytecodeLength = shaderBytecode[d3dStage].size();
+ break;
+ case GS:
+ stream.GS.object.pShaderBytecode = shaderBytecode[d3dStage].constData();
+ stream.GS.object.BytecodeLength = shaderBytecode[d3dStage].size();
+ break;
+ case PS:
+ stream.PS.object.pShaderBytecode = shaderBytecode[d3dStage].constData();
+ stream.PS.object.BytecodeLength = shaderBytecode[d3dStage].size();
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ stream.rasterizerState.object.FillMode = toD3DFillMode(m_polygonMode);
+ stream.rasterizerState.object.CullMode = toD3DCullMode(m_cullMode);
+ stream.rasterizerState.object.FrontCounterClockwise = m_frontFace == CCW;
+ stream.rasterizerState.object.DepthBias = m_depthBias;
+ stream.rasterizerState.object.SlopeScaledDepthBias = m_slopeScaledDepthBias;
+ stream.rasterizerState.object.DepthClipEnable = TRUE;
+ stream.rasterizerState.object.MultisampleEnable = sampleDesc.Count > 1;
+
+ stream.depthStencilState.object.DepthEnable = m_depthTest;
+ stream.depthStencilState.object.DepthWriteMask = m_depthWrite ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
+ stream.depthStencilState.object.DepthFunc = toD3DCompareOp(m_depthOp);
+ stream.depthStencilState.object.StencilEnable = m_stencilTest;
+ if (m_stencilTest) {
+ stream.depthStencilState.object.StencilReadMask = UINT8(m_stencilReadMask);
+ stream.depthStencilState.object.StencilWriteMask = UINT8(m_stencilWriteMask);
+ stream.depthStencilState.object.FrontFace.StencilFailOp = toD3DStencilOp(m_stencilFront.failOp);
+ stream.depthStencilState.object.FrontFace.StencilDepthFailOp = toD3DStencilOp(m_stencilFront.depthFailOp);
+ stream.depthStencilState.object.FrontFace.StencilPassOp = toD3DStencilOp(m_stencilFront.passOp);
+ stream.depthStencilState.object.FrontFace.StencilFunc = toD3DCompareOp(m_stencilFront.compareOp);
+ stream.depthStencilState.object.BackFace.StencilFailOp = toD3DStencilOp(m_stencilBack.failOp);
+ stream.depthStencilState.object.BackFace.StencilDepthFailOp = toD3DStencilOp(m_stencilBack.depthFailOp);
+ stream.depthStencilState.object.BackFace.StencilPassOp = toD3DStencilOp(m_stencilBack.passOp);
+ stream.depthStencilState.object.BackFace.StencilFunc = toD3DCompareOp(m_stencilBack.compareOp);
+ }
+
+ stream.blendState.object.IndependentBlendEnable = m_targetBlends.count() > 1;
+ for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) {
+ const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]);
+ D3D12_RENDER_TARGET_BLEND_DESC blend = {};
+ blend.BlendEnable = b.enable;
+ blend.SrcBlend = toD3DBlendFactor(b.srcColor, true);
+ blend.DestBlend = toD3DBlendFactor(b.dstColor, true);
+ blend.BlendOp = toD3DBlendOp(b.opColor);
+ blend.SrcBlendAlpha = toD3DBlendFactor(b.srcAlpha, false);
+ blend.DestBlendAlpha = toD3DBlendFactor(b.dstAlpha, false);
+ blend.BlendOpAlpha = toD3DBlendOp(b.opAlpha);
+ blend.RenderTargetWriteMask = toD3DColorWriteMask(b.colorWrite);
+ stream.blendState.object.RenderTarget[i] = blend;
+ }
+ if (m_targetBlends.isEmpty()) {
+ D3D12_RENDER_TARGET_BLEND_DESC blend = {};
+ blend.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
+ stream.blendState.object.RenderTarget[0] = blend;
+ }
+
+ stream.rtFormats.object.NumRenderTargets = rpD->colorAttachmentCount;
+ for (int i = 0; i < rpD->colorAttachmentCount; ++i)
+ stream.rtFormats.object.RTFormats[i] = DXGI_FORMAT(rpD->colorFormat[i]);
+
+ stream.dsFormat.object = rpD->hasDepthStencil ? DXGI_FORMAT(rpD->dsFormat) : DXGI_FORMAT_UNKNOWN;
+
+ stream.sampleDesc.object = sampleDesc;
+
+ stream.sampleMask.object = 0xFFFFFFFF;
+
+ viewInstanceMask = 0;
+ const bool isMultiView = m_multiViewCount >= 2;
+ stream.viewInstancingDesc.object.ViewInstanceCount = isMultiView ? m_multiViewCount : 0;
+ QVarLengthArray<D3D12_VIEW_INSTANCE_LOCATION, 4> viewInstanceLocations;
+ if (isMultiView) {
+ for (int i = 0; i < m_multiViewCount; ++i) {
+ viewInstanceMask |= (1 << i);
+ viewInstanceLocations.append({ 0, UINT(i) });
+ }
+ stream.viewInstancingDesc.object.pViewInstanceLocations = viewInstanceLocations.constData();
+ }
+
+ const D3D12_PIPELINE_STATE_STREAM_DESC streamDesc = { sizeof(stream), &stream };
+
+ ID3D12PipelineState *pso = nullptr;
+ HRESULT hr = rhiD->dev->CreatePipelineState(&streamDesc, __uuidof(ID3D12PipelineState), reinterpret_cast<void **>(&pso));
+ if (FAILED(hr)) {
+ qWarning("Failed to create graphics pipeline state: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ rhiD->rootSignaturePool.remove(rootSigHandle);
+ rootSigHandle = {};
+ return false;
+ }
+
+ handle = QD3D12Pipeline::addToPool(&rhiD->pipelinePool, QD3D12Pipeline::Graphics, pso);
+
+ rhiD->pipelineCreationEnd();
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QD3D12ComputePipeline::QD3D12ComputePipeline(QRhiImplementation *rhi)
+ : QRhiComputePipeline(rhi)
+{
+}
+
+QD3D12ComputePipeline::~QD3D12ComputePipeline()
+{
+ destroy();
+}
+
+void QD3D12ComputePipeline::destroy()
+{
+ if (handle.isNull())
+ return;
+
+ QRHI_RES_RHI(QRhiD3D12);
+ if (rhiD) {
+ rhiD->releaseQueue.deferredReleasePipeline(handle);
+ rhiD->releaseQueue.deferredReleaseRootSignature(rootSigHandle);
+ }
+
+ handle = {};
+ stageData = {};
+
+ if (rhiD)
+ rhiD->unregisterResource(this);
+}
+
+bool QD3D12ComputePipeline::create()
+{
+ if (!handle.isNull())
+ destroy();
+
+ QRHI_RES_RHI(QRhiD3D12);
+ rhiD->pipelineCreationStart();
+
+ stageData.valid = true;
+ stageData.stage = CS;
+
+ QByteArray shaderBytecode;
+ auto cacheIt = rhiD->shaderBytecodeCache.data.constFind(m_shaderStage);
+ if (cacheIt != rhiD->shaderBytecodeCache.data.constEnd()) {
+ shaderBytecode = cacheIt->bytecode;
+ stageData.nativeResourceBindingMap = cacheIt->nativeResourceBindingMap;
+ } else {
+ QString error;
+ QShaderKey shaderKey;
+ int compileFlags = 0;
+ if (m_flags.testFlag(CompileShadersWithDebugInfo))
+ compileFlags |= int(HlslCompileFlag::WithDebugInfo);
+ const QByteArray bytecode = compileHlslShaderSource(m_shaderStage.shader(),
+ m_shaderStage.shaderVariant(),
+ compileFlags,
+ &error,
+ &shaderKey);
+ if (bytecode.isEmpty()) {
+ qWarning("HLSL compute shader compilation failed: %s", qPrintable(error));
+ return false;
+ }
+
+ shaderBytecode = bytecode;
+ stageData.nativeResourceBindingMap = m_shaderStage.shader().nativeResourceBindingMap(shaderKey);
+ rhiD->shaderBytecodeCache.insertWithCapacityLimit(m_shaderStage, { bytecode,
+ stageData.nativeResourceBindingMap });
+ }
+
+ QD3D12ShaderResourceBindings *srbD = QRHI_RES(QD3D12ShaderResourceBindings, m_shaderResourceBindings);
+ if (srbD) {
+ rootSigHandle = srbD->createRootSignature(&stageData, 1);
+ if (rootSigHandle.isNull()) {
+ qWarning("Failed to create root signature");
+ return false;
+ }
+ }
+ ID3D12RootSignature *rootSig = nullptr;
+ if (QD3D12RootSignature *rs = rhiD->rootSignaturePool.lookupRef(rootSigHandle))
+ rootSig = rs->rootSig;
+ if (!rootSig) {
+ qWarning("Cannot create compute pipeline state without root signature");
+ return false;
+ }
+
+ struct {
+ QD3D12PipelineStateSubObject<ID3D12RootSignature *, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE> rootSig;
+ QD3D12PipelineStateSubObject<D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CS> CS;
+ } stream;
+ stream.rootSig.object = rootSig;
+ stream.CS.object.pShaderBytecode = shaderBytecode.constData();
+ stream.CS.object.BytecodeLength = shaderBytecode.size();
+ const D3D12_PIPELINE_STATE_STREAM_DESC streamDesc = { sizeof(stream), &stream };
+ ID3D12PipelineState *pso = nullptr;
+ HRESULT hr = rhiD->dev->CreatePipelineState(&streamDesc, __uuidof(ID3D12PipelineState), reinterpret_cast<void **>(&pso));
+ if (FAILED(hr)) {
+ qWarning("Failed to create compute pipeline state: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ rhiD->rootSignaturePool.remove(rootSigHandle);
+ rootSigHandle = {};
+ return false;
+ }
+
+ handle = QD3D12Pipeline::addToPool(&rhiD->pipelinePool, QD3D12Pipeline::Compute, pso);
+
+ rhiD->pipelineCreationEnd();
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+// This is a lot like in the Metal backend: we need to now the rtv and dsv
+// formats to create a graphics pipeline, and that's exactly what our
+// "renderpass descriptor" is going to hold.
+QD3D12RenderPassDescriptor::QD3D12RenderPassDescriptor(QRhiImplementation *rhi)
+ : QRhiRenderPassDescriptor(rhi)
+{
+ serializedFormatData.reserve(16);
+}
+
+QD3D12RenderPassDescriptor::~QD3D12RenderPassDescriptor()
+{
+ destroy();
+}
+
+void QD3D12RenderPassDescriptor::destroy()
+{
+ QRHI_RES_RHI(QRhiD3D12);
+ if (rhiD)
+ rhiD->unregisterResource(this);
+}
+
+bool QD3D12RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
+{
+ if (!other)
+ return false;
+
+ const QD3D12RenderPassDescriptor *o = QRHI_RES(const QD3D12RenderPassDescriptor, other);
+
+ if (colorAttachmentCount != o->colorAttachmentCount)
+ return false;
+
+ if (hasDepthStencil != o->hasDepthStencil)
+ return false;
+
+ for (int i = 0; i < colorAttachmentCount; ++i) {
+ if (colorFormat[i] != o->colorFormat[i])
+ return false;
+ }
+
+ if (hasDepthStencil) {
+ if (dsFormat != o->dsFormat)
+ return false;
+ }
+
+ return true;
+}
+
+void QD3D12RenderPassDescriptor::updateSerializedFormat()
+{
+ serializedFormatData.clear();
+ auto p = std::back_inserter(serializedFormatData);
+
+ *p++ = colorAttachmentCount;
+ *p++ = hasDepthStencil;
+ for (int i = 0; i < colorAttachmentCount; ++i)
+ *p++ = colorFormat[i];
+ *p++ = hasDepthStencil ? dsFormat : 0;
+}
+
+QRhiRenderPassDescriptor *QD3D12RenderPassDescriptor::newCompatibleRenderPassDescriptor() const
+{
+ QD3D12RenderPassDescriptor *rpD = new QD3D12RenderPassDescriptor(m_rhi);
+ rpD->colorAttachmentCount = colorAttachmentCount;
+ rpD->hasDepthStencil = hasDepthStencil;
+ memcpy(rpD->colorFormat, colorFormat, sizeof(colorFormat));
+ rpD->dsFormat = dsFormat;
+
+ rpD->updateSerializedFormat();
+
+ QRHI_RES_RHI(QRhiD3D12);
+ rhiD->registerResource(rpD);
+ return rpD;
+}
+
+QVector<quint32> QD3D12RenderPassDescriptor::serializedFormat() const
+{
+ return serializedFormatData;
+}
+
+QD3D12CommandBuffer::QD3D12CommandBuffer(QRhiImplementation *rhi)
+ : QRhiCommandBuffer(rhi)
+{
+ resetState();
+}
+
+QD3D12CommandBuffer::~QD3D12CommandBuffer()
+{
+ destroy();
+}
+
+void QD3D12CommandBuffer::destroy()
+{
+ // nothing to do here, the command list is not owned by us
+}
+
+const QRhiNativeHandles *QD3D12CommandBuffer::nativeHandles()
+{
+ nativeHandlesStruct.commandList = cmdList;
+ return &nativeHandlesStruct;
+}
+
+QD3D12SwapChainRenderTarget::QD3D12SwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
+ : QRhiSwapChainRenderTarget(rhi, swapchain),
+ d(rhi)
+{
+}
+
+QD3D12SwapChainRenderTarget::~QD3D12SwapChainRenderTarget()
+{
+ destroy();
+}
+
+void QD3D12SwapChainRenderTarget::destroy()
+{
+ // nothing to do here
+}
+
+QSize QD3D12SwapChainRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QD3D12SwapChainRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QD3D12SwapChainRenderTarget::sampleCount() const
+{
+ return d.sampleCount;
+}
+
+QD3D12SwapChain::QD3D12SwapChain(QRhiImplementation *rhi)
+ : QRhiSwapChain(rhi),
+ rtWrapper(rhi, this),
+ rtWrapperRight(rhi, this),
+ cbWrapper(rhi)
+{
+}
+
+QD3D12SwapChain::~QD3D12SwapChain()
+{
+ destroy();
+}
+
+void QD3D12SwapChain::destroy()
+{
+ if (!swapChain)
+ return;
+
+ releaseBuffers();
+
+ swapChain->Release();
+ swapChain = nullptr;
+ sourceSwapChain1->Release();
+ sourceSwapChain1 = nullptr;
+
+ for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
+ FrameResources &fr(frameRes[i]);
+ if (fr.fence)
+ fr.fence->Release();
+ if (fr.fenceEvent)
+ CloseHandle(fr.fenceEvent);
+ if (fr.cmdList)
+ fr.cmdList->Release();
+ fr = {};
+ }
+
+ if (dcompVisual) {
+ dcompVisual->Release();
+ dcompVisual = nullptr;
+ }
+
+ if (dcompTarget) {
+ dcompTarget->Release();
+ dcompTarget = nullptr;
+ }
+
+ QRHI_RES_RHI(QRhiD3D12);
+ if (rhiD) {
+ rhiD->swapchains.remove(this);
+ rhiD->unregisterResource(this);
+ }
+}
+
+void QD3D12SwapChain::releaseBuffers()
+{
+ QRHI_RES_RHI(QRhiD3D12);
+ rhiD->waitGpu();
+ for (UINT i = 0; i < BUFFER_COUNT; ++i) {
+ rhiD->resourcePool.remove(colorBuffers[i]);
+ rhiD->rtvPool.release(rtvs[i], 1);
+ if (stereo)
+ rhiD->rtvPool.release(rtvsRight[i], 1);
+ if (!msaaBuffers[i].isNull())
+ rhiD->resourcePool.remove(msaaBuffers[i]);
+ if (msaaRtvs[i].isValid())
+ rhiD->rtvPool.release(msaaRtvs[i], 1);
+ }
+}
+
+void QD3D12SwapChain::waitCommandCompletionForFrameSlot(int frameSlot)
+{
+ FrameResources &fr(frameRes[frameSlot]);
+ if (fr.fence->GetCompletedValue() < fr.fenceCounter) {
+ fr.fence->SetEventOnCompletion(fr.fenceCounter, fr.fenceEvent);
+ WaitForSingleObject(fr.fenceEvent, INFINITE);
+ }
+}
+
+void QD3D12SwapChain::addCommandCompletionSignalForCurrentFrameSlot()
+{
+ QRHI_RES_RHI(QRhiD3D12);
+ FrameResources &fr(frameRes[currentFrameSlot]);
+ fr.fenceCounter += 1u;
+ rhiD->cmdQueue->Signal(fr.fence, fr.fenceCounter);
+}
+
+QRhiCommandBuffer *QD3D12SwapChain::currentFrameCommandBuffer()
+{
+ return &cbWrapper;
+}
+
+QRhiRenderTarget *QD3D12SwapChain::currentFrameRenderTarget()
+{
+ return &rtWrapper;
+}
+
+QRhiRenderTarget *QD3D12SwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer)
+{
+ return !stereo || targetBuffer == StereoTargetBuffer::LeftBuffer ? &rtWrapper : &rtWrapperRight;
+}
+
+QSize QD3D12SwapChain::surfacePixelSize()
+{
+ Q_ASSERT(m_window);
+ return m_window->size() * m_window->devicePixelRatio();
+}
+
+bool QD3D12SwapChain::isFormatSupported(Format f)
+{
+ if (f == SDR)
+ return true;
+
+ if (!m_window) {
+ qWarning("Attempted to call isFormatSupported() without a window set");
+ return false;
+ }
+
+ QRHI_RES_RHI(QRhiD3D12);
+ DXGI_OUTPUT_DESC1 desc1;
+ if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &desc1)) {
+ if (desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020)
+ return f == QRhiSwapChain::HDRExtendedSrgbLinear || f == QRhiSwapChain::HDR10;
+ }
+
+ return false;
+}
+
+QRhiSwapChainHdrInfo QD3D12SwapChain::hdrInfo()
+{
+ QRhiSwapChainHdrInfo info = QRhiSwapChain::hdrInfo();
+ // Must use m_window, not window, given this may be called before createOrResize().
+ if (m_window) {
+ QRHI_RES_RHI(QRhiD3D12);
+ DXGI_OUTPUT_DESC1 hdrOutputDesc;
+ if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc)) {
+ info.limitsType = QRhiSwapChainHdrInfo::LuminanceInNits;
+ info.limits.luminanceInNits.minLuminance = hdrOutputDesc.MinLuminance;
+ info.limits.luminanceInNits.maxLuminance = hdrOutputDesc.MaxLuminance;
+ info.luminanceBehavior = QRhiSwapChainHdrInfo::SceneReferred; // 1.0 = 80 nits
+ info.sdrWhiteLevel = QRhiD3D::sdrWhiteLevelInNits(hdrOutputDesc);
+ }
+ }
+ return info;
+}
+
+QRhiRenderPassDescriptor *QD3D12SwapChain::newCompatibleRenderPassDescriptor()
+{
+ // not yet built so cannot rely on data computed in createOrResize()
+ chooseFormats();
+
+ QD3D12RenderPassDescriptor *rpD = new QD3D12RenderPassDescriptor(m_rhi);
+ rpD->colorAttachmentCount = 1;
+ rpD->hasDepthStencil = m_depthStencil != nullptr;
+ rpD->colorFormat[0] = int(srgbAdjustedColorFormat);
+ rpD->dsFormat = QD3D12RenderBuffer::DS_FORMAT;
+ rpD->updateSerializedFormat();
+
+ QRHI_RES_RHI(QRhiD3D12);
+ rhiD->registerResource(rpD);
+ return rpD;
+}
+
+bool QRhiD3D12::ensureDirectCompositionDevice()
+{
+ if (dcompDevice)
+ return true;
+
+ qCDebug(QRHI_LOG_INFO, "Creating Direct Composition device (needed for semi-transparent windows)");
+ dcompDevice = QRhiD3D::createDirectCompositionDevice();
+ return dcompDevice ? true : false;
+}
+
+static const DXGI_FORMAT DEFAULT_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM;
+static const DXGI_FORMAT DEFAULT_SRGB_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
+
+void QD3D12SwapChain::chooseFormats()
+{
+ colorFormat = DEFAULT_FORMAT;
+ srgbAdjustedColorFormat = m_flags.testFlag(sRGB) ? DEFAULT_SRGB_FORMAT : DEFAULT_FORMAT;
+ hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // SDR
+ DXGI_OUTPUT_DESC1 hdrOutputDesc;
+ QRHI_RES_RHI(QRhiD3D12);
+ if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc) && m_format != SDR) {
+ // https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range
+ if (hdrOutputDesc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
+ switch (m_format) {
+ case HDRExtendedSrgbLinear:
+ colorFormat = DXGI_FORMAT_R16G16B16A16_FLOAT;
+ hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
+ srgbAdjustedColorFormat = colorFormat;
+ break;
+ case HDR10:
+ colorFormat = DXGI_FORMAT_R10G10B10A2_UNORM;
+ hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
+ srgbAdjustedColorFormat = colorFormat;
+ break;
+ default:
+ break;
+ }
+ } else {
+ // This happens also when Use HDR is set to Off in the Windows
+ // Display settings. Show a helpful warning, but continue with the
+ // default non-HDR format.
+ qWarning("The output associated with the window is not HDR capable "
+ "(or Use HDR is Off in the Display Settings), ignoring HDR format request");
+ }
+ }
+ sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, colorFormat);
+}
+
+bool QD3D12SwapChain::createOrResize()
+{
+ // Can be called multiple times due to window resizes - that is not the
+ // same as a simple destroy+create (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)
+ destroy();
+
+ window = m_window;
+ m_currentPixelSize = surfacePixelSize();
+ pixelSize = m_currentPixelSize;
+
+ if (pixelSize.isEmpty())
+ return false;
+
+ HWND hwnd = reinterpret_cast<HWND>(window->winId());
+ HRESULT hr;
+ QRHI_RES_RHI(QRhiD3D12);
+ stereo = m_window->format().stereo() && rhiD->dxgiFactory->IsWindowedStereoEnabled();
+
+ if (m_flags.testFlag(SurfaceHasPreMulAlpha) || m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
+ if (rhiD->ensureDirectCompositionDevice()) {
+ if (!dcompTarget) {
+ hr = rhiD->dcompDevice->CreateTargetForHwnd(hwnd, false, &dcompTarget);
+ if (FAILED(hr)) {
+ qWarning("Failed to create Direct Composition target for the window: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ }
+ }
+ if (dcompTarget && !dcompVisual) {
+ hr = rhiD->dcompDevice->CreateVisual(&dcompVisual);
+ if (FAILED(hr)) {
+ qWarning("Failed to create DirectComposition visual: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ }
+ }
+ }
+ // simple consistency check
+ if (window->requestedFormat().alphaBufferSize() <= 0)
+ qWarning("Swapchain says surface has alpha but the window has no alphaBufferSize set. "
+ "This may lead to problems.");
+ }
+
+ swapInterval = m_flags.testFlag(QRhiSwapChain::NoVSync) ? 0 : 1;
+ swapChainFlags = 0;
+ if (swapInterval == 0 && rhiD->supportsAllowTearing)
+ swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
+
+ if (!swapChain) {
+ chooseFormats();
+
+ DXGI_SWAP_CHAIN_DESC1 desc = {};
+ desc.Width = UINT(pixelSize.width());
+ desc.Height = UINT(pixelSize.height());
+ desc.Format = colorFormat;
+ desc.SampleDesc.Count = 1;
+ desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ desc.BufferCount = BUFFER_COUNT;
+ desc.Flags = swapChainFlags;
+ desc.Scaling = DXGI_SCALING_NONE;
+ desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ desc.Stereo = stereo;
+
+ if (dcompVisual) {
+ // With DirectComposition setting AlphaMode to STRAIGHT fails the
+ // swapchain creation, whereas the result seems to be identical
+ // with any of the other values, including IGNORE. (?)
+ desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
+
+ // DirectComposition has its own limitations, cannot use
+ // SCALING_NONE. So with semi-transparency requested we are forced
+ // to SCALING_STRETCH.
+ desc.Scaling = DXGI_SCALING_STRETCH;
+ }
+
+ if (dcompVisual)
+ hr = rhiD->dxgiFactory->CreateSwapChainForComposition(rhiD->cmdQueue, &desc, nullptr, &sourceSwapChain1);
+ else
+ hr = rhiD->dxgiFactory->CreateSwapChainForHwnd(rhiD->cmdQueue, hwnd, &desc, nullptr, nullptr, &sourceSwapChain1);
+
+ // If failed and we tried a HDR format, then try with SDR. This
+ // matches other backends, such as Vulkan where if the format is
+ // not supported, the default one is used instead.
+ if (FAILED(hr) && m_format != SDR) {
+ colorFormat = DEFAULT_FORMAT;
+ desc.Format = DEFAULT_FORMAT;
+ if (dcompVisual)
+ hr = rhiD->dxgiFactory->CreateSwapChainForComposition(rhiD->cmdQueue, &desc, nullptr, &sourceSwapChain1);
+ else
+ hr = rhiD->dxgiFactory->CreateSwapChainForHwnd(rhiD->cmdQueue, hwnd, &desc, nullptr, nullptr, &sourceSwapChain1);
+ }
+
+ if (SUCCEEDED(hr)) {
+ if (FAILED(sourceSwapChain1->QueryInterface(__uuidof(IDXGISwapChain3), reinterpret_cast<void **>(&swapChain)))) {
+ qWarning("IDXGISwapChain3 not available");
+ return false;
+ }
+ if (m_format != SDR) {
+ hr = swapChain->SetColorSpace1(hdrColorSpace);
+ if (FAILED(hr)) {
+ qWarning("Failed to set color space on swapchain: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ }
+ }
+ if (dcompVisual) {
+ hr = dcompVisual->SetContent(swapChain);
+ if (SUCCEEDED(hr)) {
+ hr = dcompTarget->SetRoot(dcompVisual);
+ if (FAILED(hr)) {
+ qWarning("Failed to associate Direct Composition visual with the target: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ }
+ } else {
+ qWarning("Failed to set content for Direct Composition visual: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ }
+ } else {
+ // disable Alt+Enter; not relevant when using DirectComposition
+ rhiD->dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
+ }
+ }
+ if (FAILED(hr)) {
+ qWarning("Failed to create D3D12 swapchain: %s"
+ " (Width=%u Height=%u Format=%u SampleCount=%u BufferCount=%u Scaling=%u SwapEffect=%u Stereo=%u)",
+ qPrintable(QSystemError::windowsComString(hr)),
+ desc.Width, desc.Height, UINT(desc.Format), desc.SampleDesc.Count,
+ desc.BufferCount, UINT(desc.Scaling), UINT(desc.SwapEffect), UINT(desc.Stereo));
+ return false;
+ }
+
+ for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) {
+ hr = rhiD->dev->CreateFence(0,
+ D3D12_FENCE_FLAG_NONE,
+ __uuidof(ID3D12Fence),
+ reinterpret_cast<void **>(&frameRes[i].fence));
+ if (FAILED(hr)) {
+ qWarning("Failed to create fence for swapchain: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ frameRes[i].fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+
+ frameRes[i].fenceCounter = 0;
+ }
+ } else {
+ releaseBuffers();
+ hr = swapChain->ResizeBuffers(BUFFER_COUNT,
+ UINT(pixelSize.width()),
+ UINT(pixelSize.height()),
+ colorFormat,
+ swapChainFlags);
+ if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
+ qWarning("Device loss detected in ResizeBuffers()");
+ rhiD->deviceLost = true;
+ return false;
+ } else if (FAILED(hr)) {
+ qWarning("Failed to resize D3D12 swapchain: %s", qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ }
+
+ for (UINT i = 0; i < BUFFER_COUNT; ++i) {
+ ID3D12Resource *colorBuffer;
+ hr = swapChain->GetBuffer(i, __uuidof(ID3D12Resource), reinterpret_cast<void **>(&colorBuffer));
+ if (FAILED(hr)) {
+ qWarning("Failed to get buffer %u for D3D12 swapchain: %s",
+ i, qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ colorBuffers[i] = QD3D12Resource::addToPool(&rhiD->resourcePool, colorBuffer, D3D12_RESOURCE_STATE_PRESENT);
+ rtvs[i] = rhiD->rtvPool.allocate(1);
+ D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
+ rtvDesc.Format = srgbAdjustedColorFormat;
+ rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
+ rhiD->dev->CreateRenderTargetView(colorBuffer, &rtvDesc, rtvs[i].cpuHandle);
+
+ if (stereo) {
+ rtvsRight[i] = rhiD->rtvPool.allocate(1);
+ D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
+ rtvDesc.Format = srgbAdjustedColorFormat;
+ rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
+ rtvDesc.Texture2DArray.ArraySize = 1;
+ rtvDesc.Texture2DArray.FirstArraySlice = 1;
+ rhiD->dev->CreateRenderTargetView(colorBuffer, &rtvDesc, rtvsRight[i].cpuHandle);
+ }
+ }
+
+ 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) {
+ if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
+ m_depthStencil->setPixelSize(pixelSize);
+ if (!m_depthStencil->create())
+ qWarning("Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
+ pixelSize.width(), pixelSize.height());
+ } else {
+ 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());
+ }
+ }
+
+ ds = m_depthStencil ? QRHI_RES(QD3D12RenderBuffer, m_depthStencil) : nullptr;
+
+ if (sampleDesc.Count > 1) {
+ for (UINT i = 0; i < BUFFER_COUNT; ++i) {
+ D3D12_RESOURCE_DESC resourceDesc = {};
+ resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ resourceDesc.Width = UINT64(pixelSize.width());
+ resourceDesc.Height = UINT(pixelSize.height());
+ resourceDesc.DepthOrArraySize = 1;
+ resourceDesc.MipLevels = 1;
+ resourceDesc.Format = srgbAdjustedColorFormat;
+ resourceDesc.SampleDesc = sampleDesc;
+ resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+ resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
+ D3D12_CLEAR_VALUE clearValue = {};
+ clearValue.Format = colorFormat;
+ ID3D12Resource *resource = nullptr;
+ D3D12MA::Allocation *allocation = nullptr;
+ HRESULT hr = rhiD->vma.createResource(D3D12_HEAP_TYPE_DEFAULT,
+ &resourceDesc,
+ D3D12_RESOURCE_STATE_RENDER_TARGET,
+ &clearValue,
+ &allocation,
+ __uuidof(ID3D12Resource),
+ reinterpret_cast<void **>(&resource));
+ if (FAILED(hr)) {
+ qWarning("Failed to create MSAA color buffer: %s", qPrintable(QSystemError::windowsComString(hr)));
+ return false;
+ }
+ msaaBuffers[i] = QD3D12Resource::addToPool(&rhiD->resourcePool, resource, D3D12_RESOURCE_STATE_RENDER_TARGET, allocation);
+ msaaRtvs[i] = rhiD->rtvPool.allocate(1);
+ if (!msaaRtvs[i].isValid())
+ return false;
+ D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
+ rtvDesc.Format = srgbAdjustedColorFormat;
+ rtvDesc.ViewDimension = sampleDesc.Count > 1 ? D3D12_RTV_DIMENSION_TEXTURE2DMS
+ : D3D12_RTV_DIMENSION_TEXTURE2D;
+ rhiD->dev->CreateRenderTargetView(resource, &rtvDesc, msaaRtvs[i].cpuHandle);
+ }
+ }
+
+ currentBackBufferIndex = swapChain->GetCurrentBackBufferIndex();
+ currentFrameSlot = 0;
+
+ rtWrapper.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
+ QD3D12SwapChainRenderTarget *rtD = QRHI_RES(QD3D12SwapChainRenderTarget, &rtWrapper);
+ rtD->d.rp = QRHI_RES(QD3D12RenderPassDescriptor, m_renderPassDesc);
+ rtD->d.pixelSize = pixelSize;
+ rtD->d.dpr = float(window->devicePixelRatio());
+ rtD->d.sampleCount = int(sampleDesc.Count);
+ rtD->d.colorAttCount = 1;
+ rtD->d.dsAttCount = m_depthStencil ? 1 : 0;
+
+ rtWrapperRight.setRenderPassDescriptor(m_renderPassDesc);
+ QD3D12SwapChainRenderTarget *rtDr = QRHI_RES(QD3D12SwapChainRenderTarget, &rtWrapperRight);
+ rtDr->d.rp = QRHI_RES(QD3D12RenderPassDescriptor, m_renderPassDesc);
+ rtDr->d.pixelSize = pixelSize;
+ rtDr->d.dpr = float(window->devicePixelRatio());
+ rtDr->d.sampleCount = int(sampleDesc.Count);
+ rtDr->d.colorAttCount = 1;
+ rtDr->d.dsAttCount = m_depthStencil ? 1 : 0;
+
+ if (needsRegistration) {
+ rhiD->swapchains.insert(this);
+ rhiD->registerResource(this);
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // __ID3D12Device2_INTERFACE_DEFINED__
diff --git a/src/gui/rhi/qrhid3d12_p.h b/src/gui/rhi/qrhid3d12_p.h
new file mode 100644
index 0000000000..3f9abbb5ac
--- /dev/null
+++ b/src/gui/rhi/qrhid3d12_p.h
@@ -0,0 +1,1248 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRHID3D12_P_H
+#define QRHID3D12_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 <QWindow>
+#include <QBitArray>
+
+#include <optional>
+#include <array>
+
+#include <d3d12.h>
+#include <d3d12sdklayers.h>
+#include <dxgi1_6.h>
+#include <dcomp.h>
+
+#include "D3D12MemAlloc.h"
+
+// ID3D12Device2 and ID3D12GraphicsCommandList1 and types and enums introduced
+// with those are hard requirements now. These should be declared in any
+// moderately recent d3d12.h, but if it is an SDK from before Windows 10
+// version 1703 then these types could be missing. In the absence of other
+// options, handle this by skipping all the code and making QRhi::create() fail
+// in such builds.
+#ifdef __ID3D12Device2_INTERFACE_DEFINED__
+#define QRHI_D3D12_AVAILABLE
+
+QT_BEGIN_NAMESPACE
+
+static const int QD3D12_FRAMES_IN_FLIGHT = 2;
+
+class QRhiD3D12;
+
+struct QD3D12Descriptor
+{
+ D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = {};
+ D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = {};
+
+ bool isValid() const { return cpuHandle.ptr != 0; }
+};
+
+struct QD3D12ReleaseQueue;
+
+struct QD3D12DescriptorHeap
+{
+ bool isValid() const { return heap && capacity; }
+ bool create(ID3D12Device *device,
+ quint32 descriptorCount,
+ D3D12_DESCRIPTOR_HEAP_TYPE heapType,
+ D3D12_DESCRIPTOR_HEAP_FLAGS heapFlags);
+ void createWithExisting(const QD3D12DescriptorHeap &other,
+ quint32 offsetInDescriptors,
+ quint32 descriptorCount);
+ void destroy();
+ void destroyWithDeferredRelease(QD3D12ReleaseQueue *releaseQueue);
+
+ QD3D12Descriptor get(quint32 count);
+ QD3D12Descriptor at(quint32 index) const;
+ quint32 remainingCapacity() const { return capacity - head; }
+
+ QD3D12Descriptor incremented(const QD3D12Descriptor &descriptor, quint32 offsetInDescriptors) const
+ {
+ D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = descriptor.cpuHandle;
+ cpuHandle.ptr += offsetInDescriptors * descriptorByteSize;
+ D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = descriptor.gpuHandle;
+ if (gpuHandle.ptr)
+ gpuHandle.ptr += offsetInDescriptors * descriptorByteSize;
+ return { cpuHandle, gpuHandle };
+ }
+
+ ID3D12DescriptorHeap *heap = nullptr;
+ quint32 capacity = 0;
+ QD3D12Descriptor heapStart;
+ quint32 head = 0;
+ quint32 descriptorByteSize = 0;
+ D3D12_DESCRIPTOR_HEAP_TYPE heapType;
+ D3D12_DESCRIPTOR_HEAP_FLAGS heapFlags;
+};
+
+struct QD3D12CpuDescriptorPool
+{
+ bool isValid() const { return !heaps.isEmpty(); }
+ bool create(ID3D12Device *device, D3D12_DESCRIPTOR_HEAP_TYPE heapType, const char *debugName = "");
+ void destroy();
+
+ QD3D12Descriptor allocate(quint32 count);
+ void release(const QD3D12Descriptor &descriptor, quint32 count);
+
+ static const int DESCRIPTORS_PER_HEAP = 256;
+
+ struct HeapWithMap {
+ QD3D12DescriptorHeap heap;
+ QBitArray map;
+ static HeapWithMap init(const QD3D12DescriptorHeap &heap, quint32 descriptorCount) {
+ HeapWithMap result;
+ result.heap = heap;
+ result.map.resize(descriptorCount);
+ return result;
+ }
+ };
+
+ ID3D12Device *device;
+ quint32 descriptorByteSize;
+ QVector<HeapWithMap> heaps;
+ const char *debugName;
+};
+
+struct QD3D12QueryHeap
+{
+ bool isValid() const { return heap && capacity; }
+ bool create(ID3D12Device *device,
+ quint32 queryCount,
+ D3D12_QUERY_HEAP_TYPE heapType);
+ void destroy();
+
+ ID3D12QueryHeap *heap = nullptr;
+ quint32 capacity = 0;
+};
+
+struct QD3D12StagingArea
+{
+ static const quint32 ALIGNMENT = D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT; // 512 so good enough both for cb and texdata
+
+ struct Allocation {
+ quint8 *p = nullptr;
+ D3D12_GPU_VIRTUAL_ADDRESS gpuAddr = 0;
+ ID3D12Resource *buffer = nullptr;
+ quint32 bufferOffset = 0;
+ bool isValid() const { return p != nullptr; }
+ };
+
+ bool isValid() const { return allocation && mem.isValid(); }
+ bool create(QRhiD3D12 *rhi, quint32 capacity, D3D12_HEAP_TYPE heapType);
+ void destroy();
+ void destroyWithDeferredRelease(QD3D12ReleaseQueue *releaseQueue);
+
+ Allocation get(quint32 byteSize);
+
+ quint32 remainingCapacity() const
+ {
+ return capacity - head;
+ }
+
+ static quint32 allocSizeForArray(quint32 size, int count = 1)
+ {
+ return count * ((size + ALIGNMENT - 1) & ~(ALIGNMENT - 1));
+ }
+
+ Allocation mem;
+ ID3D12Resource *resource = nullptr;
+ D3D12MA::Allocation *allocation = nullptr;
+ quint32 head;
+ quint32 capacity;
+};
+
+struct QD3D12ObjectHandle
+{
+ quint32 index = 0;
+ quint32 generation = 0;
+
+ // the default, null handle is guaranteed to give ObjectPool::isValid() == false
+ bool isNull() const { return index == 0 && generation == 0; }
+};
+
+inline bool operator==(const QD3D12ObjectHandle &a, const QD3D12ObjectHandle &b) noexcept
+{
+ return a.index == b.index && a.generation == b.generation;
+}
+
+inline bool operator!=(const QD3D12ObjectHandle &a, const QD3D12ObjectHandle &b) noexcept
+{
+ return !(a == b);
+}
+
+template<typename T>
+struct QD3D12ObjectPool
+{
+ void create(const char *debugName = "")
+ {
+ this->debugName = debugName;
+ Q_ASSERT(data.isEmpty());
+ data.append(Data()); // index 0 is always invalid
+ }
+
+ void destroy() {
+ int leakCount = 0; // will nicely destroy everything here, but warn about it if enabled
+ for (Data &d : data) {
+ if (d.object.has_value()) {
+ leakCount += 1;
+ d.object->releaseResources();
+ }
+ }
+ data.clear();
+#ifndef QT_NO_DEBUG
+ // debug builds: just do it always
+ static bool leakCheck = true;
+#else
+ // release builds: opt-in
+ static bool leakCheck = qEnvironmentVariableIntValue("QT_RHI_LEAK_CHECK");
+#endif
+ if (leakCheck) {
+ if (leakCount > 0) {
+ qWarning("QD3D12ObjectPool::destroy(): Pool %p '%s' had %d unreleased objects",
+ this, debugName, leakCount);
+ }
+ }
+ }
+
+ bool isValid(const QD3D12ObjectHandle &handle) const
+ {
+ return handle.index > 0
+ && handle.index < quint32(data.count())
+ && handle.generation > 0
+ && handle.generation == data[handle.index].generation
+ && data[handle.index].object.has_value();
+ }
+
+ T lookup(const QD3D12ObjectHandle &handle) const
+ {
+ return isValid(handle) ? *data[handle.index].object : T();
+ }
+
+ const T *lookupRef(const QD3D12ObjectHandle &handle) const
+ {
+ return isValid(handle) ? &*data[handle.index].object : nullptr;
+ }
+
+ T *lookupRef(const QD3D12ObjectHandle &handle)
+ {
+ return isValid(handle) ? &*data[handle.index].object : nullptr;
+ }
+
+ QD3D12ObjectHandle add(const T &object)
+ {
+ Q_ASSERT(!data.isEmpty());
+ const quint32 count = quint32(data.count());
+ quint32 index = 1; // index 0 is always invalid
+ for (; index < count; ++index) {
+ if (!data[index].object.has_value())
+ break;
+ }
+ if (index < count) {
+ data[index].object = object;
+ quint32 &generation = data[index].generation;
+ generation += 1u;
+ return { index, generation };
+ } else {
+ data.append({ object, 1 });
+ return { count, 1 };
+ }
+ }
+
+ void remove(const QD3D12ObjectHandle &handle)
+ {
+ if (T *object = lookupRef(handle)) {
+ object->releaseResources();
+ data[handle.index].object.reset();
+ }
+ }
+
+ const char *debugName;
+ struct Data {
+ std::optional<T> object;
+ quint32 generation = 0;
+ };
+ QVector<Data> data;
+};
+
+struct QD3D12Resource
+{
+ ID3D12Resource *resource;
+ D3D12_RESOURCE_STATES state;
+ D3D12_RESOURCE_DESC desc;
+ D3D12MA::Allocation *allocation;
+ void *cpuMapPtr;
+ enum { UavUsageRead = 0x01, UavUsageWrite = 0x02 };
+ int uavUsage;
+ bool owns;
+
+ // note that this assumes the allocation (if there is one) and the resource
+ // are separately releaseable, see D3D12MemAlloc docs
+ static QD3D12ObjectHandle addToPool(QD3D12ObjectPool<QD3D12Resource> *pool,
+ ID3D12Resource *resource,
+ D3D12_RESOURCE_STATES state,
+ D3D12MA::Allocation *allocation = nullptr,
+ void *cpuMapPtr = nullptr)
+ {
+ Q_ASSERT(resource);
+ return pool->add({ resource, state, resource->GetDesc(), allocation, cpuMapPtr, 0, true });
+ }
+
+ // for QRhiTexture::createFrom() where the ID3D12Resource is not owned by us
+ static QD3D12ObjectHandle addNonOwningToPool(QD3D12ObjectPool<QD3D12Resource> *pool,
+ ID3D12Resource *resource,
+ D3D12_RESOURCE_STATES state)
+ {
+ Q_ASSERT(resource);
+ return pool->add({ resource, state, resource->GetDesc(), nullptr, nullptr, 0, false });
+ }
+
+ void releaseResources()
+ {
+ if (owns) {
+ // order matters: resource first, then the allocation
+ resource->Release();
+ if (allocation)
+ allocation->Release();
+ }
+ }
+};
+
+struct QD3D12Pipeline
+{
+ enum Type {
+ Graphics,
+ Compute
+ };
+ Type type;
+ ID3D12PipelineState *pso;
+
+ static QD3D12ObjectHandle addToPool(QD3D12ObjectPool<QD3D12Pipeline> *pool,
+ Type type,
+ ID3D12PipelineState *pso)
+ {
+ return pool->add({ type, pso });
+ }
+
+ void releaseResources()
+ {
+ pso->Release();
+ }
+};
+
+struct QD3D12RootSignature
+{
+ ID3D12RootSignature *rootSig;
+
+ static QD3D12ObjectHandle addToPool(QD3D12ObjectPool<QD3D12RootSignature> *pool,
+ ID3D12RootSignature *rootSig)
+ {
+ return pool->add({ rootSig });
+ }
+
+ void releaseResources()
+ {
+ rootSig->Release();
+ }
+};
+
+struct QD3D12ReleaseQueue
+{
+ void create(QD3D12ObjectPool<QD3D12Resource> *resourcePool,
+ QD3D12ObjectPool<QD3D12Pipeline> *pipelinePool,
+ QD3D12ObjectPool<QD3D12RootSignature> *rootSignaturePool)
+ {
+ this->resourcePool = resourcePool;
+ this->pipelinePool = pipelinePool;
+ this->rootSignaturePool = rootSignaturePool;
+ }
+
+ void deferredReleaseResource(const QD3D12ObjectHandle &handle);
+ void deferredReleaseResourceWithViews(const QD3D12ObjectHandle &handle,
+ QD3D12CpuDescriptorPool *pool,
+ const QD3D12Descriptor &viewsStart,
+ int viewCount);
+ void deferredReleasePipeline(const QD3D12ObjectHandle &handle);
+ void deferredReleaseRootSignature(const QD3D12ObjectHandle &handle);
+ void deferredReleaseCallback(std::function<void(void*)> callback, void *userData);
+ void deferredReleaseResourceAndAllocation(ID3D12Resource *resource,
+ D3D12MA::Allocation *allocation);
+ void deferredReleaseDescriptorHeap(ID3D12DescriptorHeap *heap);
+ void deferredReleaseViews(QD3D12CpuDescriptorPool *pool,
+ const QD3D12Descriptor &viewsStart,
+ int viewCount);
+
+ void activatePendingDeferredReleaseRequests(int frameSlot);
+ void executeDeferredReleases(int frameSlot, bool forced = false);
+ void releaseAll();
+
+ struct DeferredReleaseEntry {
+ enum Type {
+ Resource,
+ Pipeline,
+ RootSignature,
+ Callback,
+ ResourceAndAllocation,
+ DescriptorHeap,
+ Views
+ };
+ Type type = Resource;
+ std::optional<int> frameSlotToBeReleasedIn;
+ QD3D12ObjectHandle handle;
+ QD3D12CpuDescriptorPool *poolForViews = nullptr;
+ QD3D12Descriptor viewsStart;
+ int viewCount = 0;
+ std::function<void(void*)> callback = nullptr;
+ void *callbackUserData = nullptr;
+ QPair<ID3D12Resource *, D3D12MA::Allocation *> resourceAndAllocation = {};
+ ID3D12DescriptorHeap *descriptorHeap = nullptr;
+ };
+ QVector<DeferredReleaseEntry> queue;
+ QD3D12ObjectPool<QD3D12Resource> *resourcePool = nullptr;
+ QD3D12ObjectPool<QD3D12Pipeline> *pipelinePool = nullptr;
+ QD3D12ObjectPool<QD3D12RootSignature> *rootSignaturePool = nullptr;
+};
+
+struct QD3D12CommandBuffer;
+
+struct QD3D12ResourceBarrierGenerator
+{
+ static const int PREALLOC = 16;
+
+ void create(QD3D12ObjectPool<QD3D12Resource> *resourcePool)
+ {
+ this->resourcePool = resourcePool;
+ }
+
+ void addTransitionBarrier(const QD3D12ObjectHandle &resourceHandle, D3D12_RESOURCE_STATES stateAfter);
+ void enqueueBufferedTransitionBarriers(QD3D12CommandBuffer *cbD);
+ void enqueueSubresourceTransitionBarrier(QD3D12CommandBuffer *cbD,
+ const QD3D12ObjectHandle &resourceHandle,
+ UINT subresource,
+ D3D12_RESOURCE_STATES stateBefore,
+ D3D12_RESOURCE_STATES stateAfter);
+ void enqueueUavBarrier(QD3D12CommandBuffer *cbD, const QD3D12ObjectHandle &resourceHandle);
+
+ struct TransitionResourceBarrier {
+ QD3D12ObjectHandle resourceHandle;
+ D3D12_RESOURCE_STATES stateBefore;
+ D3D12_RESOURCE_STATES stateAfter;
+ };
+ QVarLengthArray<TransitionResourceBarrier, PREALLOC> transitionResourceBarriers;
+ QD3D12ObjectPool<QD3D12Resource> *resourcePool = nullptr;
+};
+
+struct QD3D12ShaderBytecodeCache
+{
+ struct Shader {
+ Shader() = default;
+ Shader(const QByteArray &bytecode, const QShader::NativeResourceBindingMap &rbm)
+ : bytecode(bytecode), nativeResourceBindingMap(rbm)
+ { }
+ QByteArray bytecode;
+ QShader::NativeResourceBindingMap nativeResourceBindingMap;
+ };
+
+ QHash<QRhiShaderStage, Shader> data;
+
+ void insertWithCapacityLimit(const QRhiShaderStage &key, const Shader &s);
+};
+
+struct QD3D12ShaderVisibleDescriptorHeap
+{
+ bool create(ID3D12Device *device, D3D12_DESCRIPTOR_HEAP_TYPE type, quint32 perFrameDescriptorCount);
+ void destroy();
+ void destroyWithDeferredRelease(QD3D12ReleaseQueue *releaseQueue);
+
+ QD3D12DescriptorHeap heap;
+ QD3D12DescriptorHeap perFrameHeapSlice[QD3D12_FRAMES_IN_FLIGHT];
+};
+
+// wrap foreign struct so we can legally supply equality operators and qHash:
+struct Q_D3D12_SAMPLER_DESC
+{
+ D3D12_SAMPLER_DESC desc;
+
+ friend bool operator==(const Q_D3D12_SAMPLER_DESC &lhs, const Q_D3D12_SAMPLER_DESC &rhs) noexcept
+ {
+ return lhs.desc.Filter == rhs.desc.Filter
+ && lhs.desc.AddressU == rhs.desc.AddressU
+ && lhs.desc.AddressV == rhs.desc.AddressV
+ && lhs.desc.AddressW == rhs.desc.AddressW
+ && lhs.desc.MipLODBias == rhs.desc.MipLODBias
+ && lhs.desc.MaxAnisotropy == rhs.desc.MaxAnisotropy
+ && lhs.desc.ComparisonFunc == rhs.desc.ComparisonFunc
+ // BorderColor is never used, skip it
+ && lhs.desc.MinLOD == rhs.desc.MinLOD
+ && lhs.desc.MaxLOD == rhs.desc.MaxLOD;
+ }
+
+ friend bool operator!=(const Q_D3D12_SAMPLER_DESC &lhs, const Q_D3D12_SAMPLER_DESC &rhs) noexcept
+ {
+ return !(lhs == rhs);
+ }
+
+ friend size_t qHash(const Q_D3D12_SAMPLER_DESC &key, size_t seed = 0) noexcept
+ {
+ QtPrivate::QHashCombine hash;
+ seed = hash(seed, key.desc.Filter);
+ seed = hash(seed, key.desc.AddressU);
+ seed = hash(seed, key.desc.AddressV);
+ seed = hash(seed, key.desc.AddressW);
+ seed = hash(seed, key.desc.MipLODBias);
+ seed = hash(seed, key.desc.MaxAnisotropy);
+ seed = hash(seed, key.desc.ComparisonFunc);
+ // BorderColor is never used, skip it
+ seed = hash(seed, key.desc.MinLOD);
+ seed = hash(seed, key.desc.MaxLOD);
+ return seed;
+ }
+};
+
+struct QD3D12SamplerManager
+{
+ const quint32 MAX_SAMPLERS = 512;
+
+ bool create(ID3D12Device *device);
+ void destroy();
+
+ QD3D12Descriptor getShaderVisibleDescriptor(const D3D12_SAMPLER_DESC &desc);
+
+ ID3D12Device *device = nullptr;
+ QD3D12ShaderVisibleDescriptorHeap shaderVisibleSamplerHeap;
+ QHash<Q_D3D12_SAMPLER_DESC, QD3D12Descriptor> gpuMap;
+};
+
+enum QD3D12Stage { VS = 0, HS, DS, GS, PS, CS };
+
+static inline QD3D12Stage qd3d12_stage(QRhiShaderStage::Type type)
+{
+ switch (type) {
+ case QRhiShaderStage::Vertex:
+ return VS;
+ case QRhiShaderStage::TessellationControl:
+ return HS;
+ case QRhiShaderStage::TessellationEvaluation:
+ return DS;
+ case QRhiShaderStage::Geometry:
+ return GS;
+ case QRhiShaderStage::Fragment:
+ return PS;
+ case QRhiShaderStage::Compute:
+ return CS;
+ }
+ Q_UNREACHABLE_RETURN(VS);
+}
+
+static inline D3D12_SHADER_VISIBILITY qd3d12_stageToVisibility(QD3D12Stage s)
+{
+ switch (s) {
+ case VS:
+ return D3D12_SHADER_VISIBILITY_VERTEX;
+ case HS:
+ return D3D12_SHADER_VISIBILITY_HULL;
+ case DS:
+ return D3D12_SHADER_VISIBILITY_DOMAIN;
+ case GS:
+ return D3D12_SHADER_VISIBILITY_GEOMETRY;
+ case PS:
+ return D3D12_SHADER_VISIBILITY_PIXEL;
+ case CS:
+ return D3D12_SHADER_VISIBILITY_ALL;
+ }
+ Q_UNREACHABLE_RETURN(D3D12_SHADER_VISIBILITY_ALL);
+}
+
+static inline QRhiShaderResourceBinding::StageFlag qd3d12_stageToSrb(QD3D12Stage s)
+{
+ switch (s) {
+ case VS:
+ return QRhiShaderResourceBinding::VertexStage;
+ case HS:
+ return QRhiShaderResourceBinding::TessellationControlStage;
+ case DS:
+ return QRhiShaderResourceBinding::TessellationEvaluationStage;
+ case GS:
+ return QRhiShaderResourceBinding::GeometryStage;
+ case PS:
+ return QRhiShaderResourceBinding::FragmentStage;
+ case CS:
+ return QRhiShaderResourceBinding::ComputeStage;
+ }
+ Q_UNREACHABLE_RETURN(QRhiShaderResourceBinding::VertexStage);
+}
+
+struct QD3D12ShaderStageData
+{
+ bool valid = false; // to allow simple arrays where unused stages are indicated by !valid
+ QD3D12Stage stage = VS;
+ QShader::NativeResourceBindingMap nativeResourceBindingMap;
+};
+
+struct QD3D12ShaderResourceBindings;
+
+struct QD3D12ShaderResourceVisitor
+{
+ enum StorageOp { Load = 0, Store, LoadStore };
+
+ QD3D12ShaderResourceVisitor(const QD3D12ShaderResourceBindings *srb,
+ const QD3D12ShaderStageData *stageData,
+ int stageCount)
+ : srb(srb),
+ stageData(stageData),
+ stageCount(stageCount)
+ {
+ }
+
+ std::function<void(QD3D12Stage, const QRhiShaderResourceBinding::Data::UniformBufferData &, int, int)> uniformBuffer = nullptr;
+ std::function<void(QD3D12Stage, const QRhiShaderResourceBinding::TextureAndSampler &, int)> texture = nullptr;
+ std::function<void(QD3D12Stage, const QRhiShaderResourceBinding::TextureAndSampler &, int)> sampler = nullptr;
+ std::function<void(QD3D12Stage, const QRhiShaderResourceBinding::Data::StorageImageData &, StorageOp, int)> storageImage = nullptr;
+ std::function<void(QD3D12Stage, const QRhiShaderResourceBinding::Data::StorageBufferData &, StorageOp, int)> storageBuffer = nullptr;
+
+ void visit();
+
+ const QD3D12ShaderResourceBindings *srb;
+ const QD3D12ShaderStageData *stageData;
+ int stageCount;
+};
+
+struct QD3D12Readback
+{
+ // common
+ int frameSlot = -1;
+ QRhiReadbackResult *result = nullptr;
+ QD3D12StagingArea staging;
+ quint32 byteSize = 0;
+ // textures
+ quint32 bytesPerLine = 0;
+ QSize pixelSize;
+ QRhiTexture::Format format = QRhiTexture::UnknownFormat;
+ quint32 stagingRowPitch = 0;
+};
+
+struct QD3D12MipmapGenerator
+{
+ bool create(QRhiD3D12 *rhiD);
+ void destroy();
+ void generate(QD3D12CommandBuffer *cbD, const QD3D12ObjectHandle &textureHandle);
+
+ QRhiD3D12 *rhiD;
+ QD3D12ObjectHandle rootSigHandle;
+ QD3D12ObjectHandle pipelineHandle;
+};
+
+struct QD3D12MemoryAllocator
+{
+ bool create(ID3D12Device *device, IDXGIAdapter1 *adapter);
+ void destroy();
+
+ HRESULT createResource(D3D12_HEAP_TYPE heapType,
+ const D3D12_RESOURCE_DESC *resourceDesc,
+ D3D12_RESOURCE_STATES initialState,
+ const D3D12_CLEAR_VALUE *optimizedClearValue,
+ D3D12MA::Allocation **maybeAllocation,
+ REFIID riidResource,
+ void **ppvResource);
+
+ void getBudget(D3D12MA::Budget *localBudget, D3D12MA::Budget *nonLocalBudget);
+
+ bool isUsingD3D12MA() const { return allocator != nullptr; }
+
+ ID3D12Device *device = nullptr;
+ D3D12MA::Allocator *allocator = nullptr;
+};
+
+struct QD3D12Buffer : public QRhiBuffer
+{
+ QD3D12Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size);
+ ~QD3D12Buffer();
+
+ void destroy() override;
+ bool create() override;
+ QRhiBuffer::NativeBuffer nativeBuffer() override;
+ char *beginFullDynamicBufferUpdateForCurrentFrame() override;
+ void endFullDynamicBufferUpdateForCurrentFrame() override;
+
+ void executeHostWritesForFrameSlot(int frameSlot);
+
+ QD3D12ObjectHandle handles[QD3D12_FRAMES_IN_FLIGHT] = {};
+ struct HostWrite {
+ quint32 offset;
+ QRhiBufferData data;
+ };
+ QVarLengthArray<HostWrite, 16> pendingHostWrites[QD3D12_FRAMES_IN_FLIGHT];
+ friend class QRhiD3D12;
+ friend struct QD3D12CommandBuffer;
+};
+
+struct QD3D12RenderBuffer : public QRhiRenderBuffer
+{
+ QD3D12RenderBuffer(QRhiImplementation *rhi,
+ Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ Flags flags,
+ QRhiTexture::Format backingFormatHint);
+ ~QD3D12RenderBuffer();
+ void destroy() override;
+ bool create() override;
+ QRhiTexture::Format backingFormat() const override;
+
+ static const DXGI_FORMAT DS_FORMAT = DXGI_FORMAT_D24_UNORM_S8_UINT;
+
+ QD3D12ObjectHandle handle;
+ QD3D12Descriptor rtv;
+ QD3D12Descriptor dsv;
+ DXGI_FORMAT dxgiFormat;
+ DXGI_SAMPLE_DESC sampleDesc;
+ uint generation = 0;
+ friend class QRhiD3D12;
+};
+
+struct QD3D12Texture : public QRhiTexture
+{
+ QD3D12Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
+ int arraySize, int sampleCount, Flags flags);
+ ~QD3D12Texture();
+ void destroy() override;
+ bool create() override;
+ bool createFrom(NativeTexture src) override;
+ NativeTexture nativeTexture() override;
+ void setNativeLayout(int layout) override;
+
+ bool prepareCreate(QSize *adjustedSize = nullptr);
+ bool finishCreate();
+
+ QD3D12ObjectHandle handle;
+ QD3D12Descriptor srv;
+ DXGI_FORMAT dxgiFormat;
+ DXGI_FORMAT srvFormat;
+ DXGI_FORMAT rtFormat; // RTV/DSV/UAV
+ uint mipLevelCount;
+ DXGI_SAMPLE_DESC sampleDesc;
+ uint generation = 0;
+ friend class QRhiD3D12;
+ friend struct QD3D12CommandBuffer;
+};
+
+struct QD3D12Sampler : public QRhiSampler
+{
+ QD3D12Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v, AddressMode w);
+ ~QD3D12Sampler();
+ void destroy() override;
+ bool create() override;
+
+ QD3D12Descriptor lookupOrCreateShaderVisibleDescriptor();
+
+ D3D12_SAMPLER_DESC desc = {};
+ QD3D12Descriptor shaderVisibleDescriptor;
+};
+
+struct QD3D12RenderPassDescriptor : public QRhiRenderPassDescriptor
+{
+ QD3D12RenderPassDescriptor(QRhiImplementation *rhi);
+ ~QD3D12RenderPassDescriptor();
+ void destroy() override;
+ bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
+ QVector<quint32> serializedFormat() const override;
+
+ void updateSerializedFormat();
+
+ static const int MAX_COLOR_ATTACHMENTS = 8;
+ int colorAttachmentCount = 0;
+ bool hasDepthStencil = false;
+ int colorFormat[MAX_COLOR_ATTACHMENTS];
+ int dsFormat;
+ QVector<quint32> serializedFormatData;
+};
+
+struct QD3D12RenderTargetData
+{
+ QD3D12RenderTargetData(QRhiImplementation *) { }
+
+ QD3D12RenderPassDescriptor *rp = nullptr;
+ QSize pixelSize;
+ float dpr = 1;
+ int sampleCount = 1;
+ int colorAttCount = 0;
+ int dsAttCount = 0;
+ QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList;
+ static const int MAX_COLOR_ATTACHMENTS = QD3D12RenderPassDescriptor::MAX_COLOR_ATTACHMENTS;
+ D3D12_CPU_DESCRIPTOR_HANDLE rtv[MAX_COLOR_ATTACHMENTS];
+ D3D12_CPU_DESCRIPTOR_HANDLE dsv;
+};
+
+struct QD3D12SwapChainRenderTarget : public QRhiSwapChainRenderTarget
+{
+ QD3D12SwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain);
+ ~QD3D12SwapChainRenderTarget();
+ void destroy() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QD3D12RenderTargetData d;
+};
+
+struct QD3D12TextureRenderTarget : public QRhiTextureRenderTarget
+{
+ QD3D12TextureRenderTarget(QRhiImplementation *rhi,
+ const QRhiTextureRenderTargetDescription &desc,
+ Flags flags);
+ ~QD3D12TextureRenderTarget();
+ void destroy() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool create() override;
+
+ QD3D12RenderTargetData d;
+ bool ownsRtv[QD3D12RenderTargetData::MAX_COLOR_ATTACHMENTS];
+ QD3D12Descriptor rtv[QD3D12RenderTargetData::MAX_COLOR_ATTACHMENTS];
+ bool ownsDsv = false;
+ QD3D12Descriptor dsv;
+ friend class QRhiD3D12;
+};
+
+struct QD3D12ShaderResourceBindings : public QRhiShaderResourceBindings
+{
+ QD3D12ShaderResourceBindings(QRhiImplementation *rhi);
+ ~QD3D12ShaderResourceBindings();
+ void destroy() override;
+ bool create() override;
+ void updateResources(UpdateFlags flags) override;
+
+ QD3D12ObjectHandle createRootSignature(const QD3D12ShaderStageData *stageData, int stageCount);
+
+ struct VisitorData {
+ QVarLengthArray<D3D12_ROOT_PARAMETER1, 2> cbParams[6];
+
+ D3D12_ROOT_PARAMETER1 srvTables[6] = {};
+ QVarLengthArray<D3D12_DESCRIPTOR_RANGE1, 4> srvRanges[6];
+ quint32 currentSrvRangeOffset[6] = {};
+
+ QVarLengthArray<D3D12_ROOT_PARAMETER1, 4> samplerTables[6];
+ std::array<D3D12_DESCRIPTOR_RANGE1, 16> samplerRanges[6] = {};
+ int samplerRangeHeads[6] = {};
+
+ D3D12_ROOT_PARAMETER1 uavTables[6] = {};
+ QVarLengthArray<D3D12_DESCRIPTOR_RANGE1, 4> uavRanges[6];
+ quint32 currentUavRangeOffset[6] = {};
+ } visitorData;
+
+
+ void visitUniformBuffer(QD3D12Stage s,
+ const QRhiShaderResourceBinding::Data::UniformBufferData &d,
+ int shaderRegister,
+ int binding);
+ void visitTexture(QD3D12Stage s,
+ const QRhiShaderResourceBinding::TextureAndSampler &d,
+ int shaderRegister);
+ void visitSampler(QD3D12Stage s,
+ const QRhiShaderResourceBinding::TextureAndSampler &d,
+ int shaderRegister);
+ void visitStorageBuffer(QD3D12Stage s,
+ const QRhiShaderResourceBinding::Data::StorageBufferData &d,
+ QD3D12ShaderResourceVisitor::StorageOp op,
+ int shaderRegister);
+ void visitStorageImage(QD3D12Stage s,
+ const QRhiShaderResourceBinding::Data::StorageImageData &d,
+ QD3D12ShaderResourceVisitor::StorageOp op,
+ int shaderRegister);
+
+ bool hasDynamicOffset = false;
+ uint generation = 0;
+
+ friend class QRhiD3D12;
+ friend struct QD3D12ShaderResourceVisitor;
+};
+
+struct QD3D12GraphicsPipeline : public QRhiGraphicsPipeline
+{
+ QD3D12GraphicsPipeline(QRhiImplementation *rhi);
+ ~QD3D12GraphicsPipeline();
+ void destroy() override;
+ bool create() override;
+
+ QD3D12ObjectHandle handle;
+ QD3D12ObjectHandle rootSigHandle;
+ std::array<QD3D12ShaderStageData, 5> stageData;
+ D3D12_PRIMITIVE_TOPOLOGY topology;
+ UINT viewInstanceMask = 0;
+ uint generation = 0;
+ friend class QRhiD3D12;
+};
+
+struct QD3D12ComputePipeline : public QRhiComputePipeline
+{
+ QD3D12ComputePipeline(QRhiImplementation *rhi);
+ ~QD3D12ComputePipeline();
+ void destroy() override;
+ bool create() override;
+
+ QD3D12ObjectHandle handle;
+ QD3D12ObjectHandle rootSigHandle;
+ QD3D12ShaderStageData stageData;
+ uint generation = 0;
+ friend class QRhiD3D12;
+};
+
+struct QD3D12CommandBuffer : public QRhiCommandBuffer
+{
+ QD3D12CommandBuffer(QRhiImplementation *rhi);
+ ~QD3D12CommandBuffer();
+ void destroy() override;
+
+ const QRhiNativeHandles *nativeHandles();
+
+ ID3D12GraphicsCommandList1 *cmdList = nullptr; // not owned
+ QRhiD3D12CommandBufferNativeHandles nativeHandlesStruct;
+
+ enum PassType {
+ NoPass,
+ RenderPass,
+ ComputePass
+ };
+
+ void resetState()
+ {
+ recordingPass = NoPass;
+ currentTarget = nullptr;
+
+ resetPerPassState();
+ }
+
+ void resetPerPassState()
+ {
+ currentGraphicsPipeline = nullptr;
+ currentComputePipeline = nullptr;
+ currentPipelineGeneration = 0;
+ currentGraphicsSrb = nullptr;
+ currentComputeSrb = nullptr;
+ currentSrbGeneration = 0;
+ currentIndexBuffer = {};
+ currentIndexOffset = 0;
+ currentIndexFormat = DXGI_FORMAT_R16_UINT;
+ currentVertexBuffers = {};
+ currentVertexOffsets = {};
+ }
+
+ // per-frame
+ PassType recordingPass;
+ QRhiRenderTarget *currentTarget;
+
+ // per-pass
+ QD3D12GraphicsPipeline *currentGraphicsPipeline;
+ QD3D12ComputePipeline *currentComputePipeline;
+ uint currentPipelineGeneration;
+ QRhiShaderResourceBindings *currentGraphicsSrb;
+ QRhiShaderResourceBindings *currentComputeSrb;
+ uint currentSrbGeneration;
+ QD3D12ObjectHandle currentIndexBuffer;
+ quint32 currentIndexOffset;
+ DXGI_FORMAT currentIndexFormat;
+ std::array<QD3D12ObjectHandle, D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> currentVertexBuffers;
+ std::array<quint32, D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> currentVertexOffsets;
+
+ // global
+ double lastGpuTime = 0;
+
+ // per-setShaderResources
+ struct VisitorData {
+ QVarLengthArray<QPair<QD3D12ObjectHandle, quint32>, 4> cbufs[6];
+ QVarLengthArray<QD3D12Descriptor, 8> srvs[6];
+ QVarLengthArray<QD3D12Descriptor, 8> samplers[6];
+ QVarLengthArray<QPair<QD3D12ObjectHandle, D3D12_UNORDERED_ACCESS_VIEW_DESC>, 4> uavs[6];
+ } visitorData;
+
+ void visitUniformBuffer(QD3D12Stage s,
+ const QRhiShaderResourceBinding::Data::UniformBufferData &d,
+ int shaderRegister,
+ int binding,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets);
+ void visitTexture(QD3D12Stage s,
+ const QRhiShaderResourceBinding::TextureAndSampler &d,
+ int shaderRegister);
+ void visitSampler(QD3D12Stage s,
+ const QRhiShaderResourceBinding::TextureAndSampler &d,
+ int shaderRegister);
+ void visitStorageBuffer(QD3D12Stage s,
+ const QRhiShaderResourceBinding::Data::StorageBufferData &d,
+ QD3D12ShaderResourceVisitor::StorageOp op,
+ int shaderRegister);
+ void visitStorageImage(QD3D12Stage s,
+ const QRhiShaderResourceBinding::Data::StorageImageData &d,
+ QD3D12ShaderResourceVisitor::StorageOp op,
+ int shaderRegister);
+};
+
+struct QD3D12SwapChain : public QRhiSwapChain
+{
+ QD3D12SwapChain(QRhiImplementation *rhi);
+ ~QD3D12SwapChain();
+ void destroy() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+ QRhiRenderTarget *currentFrameRenderTarget(StereoTargetBuffer targetBuffer) override;
+
+ QSize surfacePixelSize() override;
+ bool isFormatSupported(Format f) override;
+ QRhiSwapChainHdrInfo hdrInfo() override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool createOrResize() override;
+
+ void releaseBuffers();
+ void waitCommandCompletionForFrameSlot(int frameSlot);
+ void addCommandCompletionSignalForCurrentFrameSlot();
+ void chooseFormats();
+
+ QWindow *window = nullptr;
+ IDXGISwapChain1 *sourceSwapChain1 = nullptr;
+ IDXGISwapChain3 *swapChain = nullptr;
+ QSize pixelSize;
+ UINT swapInterval = 1;
+ UINT swapChainFlags = 0;
+ BOOL stereo = false;
+ DXGI_FORMAT colorFormat;
+ DXGI_FORMAT srgbAdjustedColorFormat;
+ DXGI_COLOR_SPACE_TYPE hdrColorSpace;
+ IDCompositionTarget *dcompTarget = nullptr;
+ IDCompositionVisual *dcompVisual = nullptr;
+ static const UINT BUFFER_COUNT = 3;
+ QD3D12ObjectHandle colorBuffers[BUFFER_COUNT];
+ QD3D12Descriptor rtvs[BUFFER_COUNT];
+ QD3D12Descriptor rtvsRight[BUFFER_COUNT];
+ DXGI_SAMPLE_DESC sampleDesc;
+ QD3D12ObjectHandle msaaBuffers[BUFFER_COUNT];
+ QD3D12Descriptor msaaRtvs[BUFFER_COUNT];
+ QD3D12RenderBuffer *ds = nullptr;
+ UINT currentBackBufferIndex = 0;
+ QD3D12SwapChainRenderTarget rtWrapper;
+ QD3D12SwapChainRenderTarget rtWrapperRight;
+ QD3D12CommandBuffer cbWrapper;
+
+ struct FrameResources {
+ ID3D12Fence *fence = nullptr;
+ HANDLE fenceEvent = nullptr;
+ UINT64 fenceCounter = 0;
+ ID3D12GraphicsCommandList1 *cmdList = nullptr;
+ } frameRes[QD3D12_FRAMES_IN_FLIGHT];
+
+ int currentFrameSlot = 0; // index in frameRes
+};
+
+template<typename T, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type>
+struct alignas(void*) QD3D12PipelineStateSubObject
+{
+ D3D12_PIPELINE_STATE_SUBOBJECT_TYPE type = Type;
+ T object = {};
+};
+
+class QRhiD3D12 : public QRhiImplementation
+{
+public:
+ // 16MB * QD3D12_FRAMES_IN_FLIGHT; buffer and texture upload staging data that
+ // gets no space from this will get their own temporary staging areas.
+ static const quint32 SMALL_STAGING_AREA_BYTES_PER_FRAME = 16 * 1024 * 1024;
+
+ static const quint32 SHADER_VISIBLE_CBV_SRV_UAV_HEAP_PER_FRAME_START_SIZE = 16384;
+
+ QRhiD3D12(QRhiD3D12InitParams *params, QRhiD3D12NativeHandles *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,
+ quint32 size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags,
+ QRhiTexture::Format backingFormatHint) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int depth,
+ int arraySize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
+ QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u,
+ QRhiSampler::AddressMode v,
+ QRhiSampler::AddressMode w) 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, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) 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,
+ QRhiCommandBuffer::BeginPassFlags flags) 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,
+ QRhiCommandBuffer::BeginPassFlags flags) 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;
+ double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
+
+ QList<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;
+ QRhiDriverInfo driverInfo() const override;
+ QRhiStats statistics() override;
+ bool makeThreadLocalNativeContextCurrent() override;
+ void releaseCachedResources() override;
+ bool isDeviceLost() const override;
+
+ QByteArray pipelineCacheData() override;
+ void setPipelineCacheData(const QByteArray &data) override;
+
+ void waitGpu();
+ DXGI_SAMPLE_DESC effectiveSampleDesc(int sampleCount, DXGI_FORMAT format) const;
+ bool ensureDirectCompositionDevice();
+ bool startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList1 **cmdList);
+ void enqueueResourceUpdates(QD3D12CommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates);
+ void finishActiveReadbacks(bool forced = false);
+ bool ensureShaderVisibleDescriptorHeapCapacity(QD3D12ShaderVisibleDescriptorHeap *h,
+ D3D12_DESCRIPTOR_HEAP_TYPE type,
+ int frameSlot,
+ quint32 neededDescriptorCount,
+ bool *gotNew);
+ void bindShaderVisibleHeaps(QD3D12CommandBuffer *cbD);
+
+ bool debugLayer = false;
+ ID3D12Device2 *dev = nullptr;
+ D3D_FEATURE_LEVEL minimumFeatureLevel = D3D_FEATURE_LEVEL(0);
+ LUID adapterLuid = {};
+ bool importedDevice = false;
+ bool importedCommandQueue = false;
+ QRhi::Flags rhiFlags;
+ IDXGIFactory2 *dxgiFactory = nullptr;
+ bool supportsAllowTearing = false;
+ IDXGIAdapter1 *activeAdapter = nullptr;
+ QRhiDriverInfo driverInfoStruct;
+ QRhiD3D12NativeHandles nativeHandlesStruct;
+ bool deviceLost = false;
+ ID3D12CommandQueue *cmdQueue = nullptr;
+ ID3D12Fence *fullFence = nullptr;
+ HANDLE fullFenceEvent = nullptr;
+ UINT64 fullFenceCounter = 0;
+ ID3D12CommandAllocator *cmdAllocators[QD3D12_FRAMES_IN_FLIGHT] = {};
+ QD3D12MemoryAllocator vma;
+ QD3D12CpuDescriptorPool rtvPool;
+ QD3D12CpuDescriptorPool dsvPool;
+ QD3D12CpuDescriptorPool cbvSrvUavPool;
+ QD3D12ObjectPool<QD3D12Resource> resourcePool;
+ QD3D12ObjectPool<QD3D12Pipeline> pipelinePool;
+ QD3D12ObjectPool<QD3D12RootSignature> rootSignaturePool;
+ QD3D12ReleaseQueue releaseQueue;
+ QD3D12ResourceBarrierGenerator barrierGen;
+ QD3D12SamplerManager samplerMgr;
+ QD3D12MipmapGenerator mipmapGen;
+ QD3D12StagingArea smallStagingAreas[QD3D12_FRAMES_IN_FLIGHT];
+ QD3D12ShaderVisibleDescriptorHeap shaderVisibleCbvSrvUavHeap;
+ UINT64 timestampTicksPerSecond = 0;
+ QD3D12QueryHeap timestampQueryHeap;
+ QD3D12StagingArea timestampReadbackArea;
+ IDCompositionDevice *dcompDevice = nullptr;
+ QD3D12SwapChain *currentSwapChain = nullptr;
+ QSet<QD3D12SwapChain *> swapchains;
+ QD3D12ShaderBytecodeCache shaderBytecodeCache;
+ QVarLengthArray<QD3D12Readback, 4> activeReadbacks;
+ bool offscreenActive = false;
+ QD3D12CommandBuffer *offscreenCb[QD3D12_FRAMES_IN_FLIGHT] = {};
+
+ struct {
+ bool multiView = false;
+ bool textureViewFormat = false;
+ } caps;
+};
+
+QT_END_NAMESPACE
+
+#endif // __ID3D12Device2_INTERFACE_DEFINED__
+
+#endif
diff --git a/src/gui/rhi/qrhid3dhelpers.cpp b/src/gui/rhi/qrhid3dhelpers.cpp
new file mode 100644
index 0000000000..216c358cbe
--- /dev/null
+++ b/src/gui/rhi/qrhid3dhelpers.cpp
@@ -0,0 +1,172 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qrhid3dhelpers_p.h"
+#include <QtCore/private/qsystemlibrary_p.h>
+#include <QtCore/private/qsystemerror_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QRhiD3D {
+
+bool output6ForWindow(QWindow *w, IDXGIAdapter1 *adapter, IDXGIOutput6 **result)
+{
+ bool ok = false;
+ QRect wr = w->geometry();
+ wr = QRect(wr.topLeft() * w->devicePixelRatio(), wr.size() * w->devicePixelRatio());
+ const QPoint center = wr.center();
+ IDXGIOutput *currentOutput = nullptr;
+ IDXGIOutput *output = nullptr;
+ for (UINT i = 0; adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; ++i) {
+ DXGI_OUTPUT_DESC desc;
+ output->GetDesc(&desc);
+ const RECT r = desc.DesktopCoordinates;
+ const QRect dr(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1));
+ if (dr.contains(center)) {
+ currentOutput = output;
+ break;
+ } else {
+ output->Release();
+ }
+ }
+ if (currentOutput) {
+ ok = SUCCEEDED(currentOutput->QueryInterface(__uuidof(IDXGIOutput6), reinterpret_cast<void **>(result)));
+ currentOutput->Release();
+ }
+ return ok;
+}
+
+bool outputDesc1ForWindow(QWindow *w, IDXGIAdapter1 *adapter, DXGI_OUTPUT_DESC1 *result)
+{
+ bool ok = false;
+ IDXGIOutput6 *out6 = nullptr;
+ if (output6ForWindow(w, adapter, &out6)) {
+ ok = SUCCEEDED(out6->GetDesc1(result));
+ out6->Release();
+ }
+ return ok;
+}
+
+float sdrWhiteLevelInNits(const DXGI_OUTPUT_DESC1 &outputDesc)
+{
+ QVector<DISPLAYCONFIG_PATH_INFO> pathInfos;
+ uint32_t pathInfoCount, modeInfoCount;
+ LONG result;
+ do {
+ if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathInfoCount, &modeInfoCount) == ERROR_SUCCESS) {
+ pathInfos.resize(pathInfoCount);
+ QVector<DISPLAYCONFIG_MODE_INFO> modeInfos(modeInfoCount);
+ result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathInfoCount, pathInfos.data(), &modeInfoCount, modeInfos.data(), nullptr);
+ } else {
+ return 200.0f;
+ }
+ } while (result == ERROR_INSUFFICIENT_BUFFER);
+
+ MONITORINFOEX monitorInfo = {};
+ monitorInfo.cbSize = sizeof(monitorInfo);
+ GetMonitorInfo(outputDesc.Monitor, &monitorInfo);
+
+ for (const DISPLAYCONFIG_PATH_INFO &info : pathInfos) {
+ DISPLAYCONFIG_SOURCE_DEVICE_NAME deviceName = {};
+ deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
+ deviceName.header.size = sizeof(deviceName);
+ deviceName.header.adapterId = info.sourceInfo.adapterId;
+ deviceName.header.id = info.sourceInfo.id;
+ if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) {
+ if (!wcscmp(monitorInfo.szDevice, deviceName.viewGdiDeviceName)) {
+ DISPLAYCONFIG_SDR_WHITE_LEVEL whiteLevel = {};
+ whiteLevel.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
+ whiteLevel.header.size = sizeof(DISPLAYCONFIG_SDR_WHITE_LEVEL);
+ whiteLevel.header.adapterId = info.targetInfo.adapterId;
+ whiteLevel.header.id = info.targetInfo.id;
+ if (DisplayConfigGetDeviceInfo(&whiteLevel.header) == ERROR_SUCCESS)
+ return whiteLevel.SDRWhiteLevel * 80 / 1000.0f;
+ }
+ }
+ }
+
+ return 200.0f;
+}
+
+pD3DCompile resolveD3DCompile()
+{
+ for (const wchar_t *libraryName : {L"D3DCompiler_47", L"D3DCompiler_43"}) {
+ QSystemLibrary library(libraryName);
+ if (library.load()) {
+ if (auto symbol = library.resolve("D3DCompile"))
+ return reinterpret_cast<pD3DCompile>(symbol);
+ } else {
+ qWarning("Failed to load D3DCompiler_47/43.dll");
+ }
+ }
+ return nullptr;
+}
+
+IDCompositionDevice *createDirectCompositionDevice()
+{
+ QSystemLibrary dcomplib(QStringLiteral("dcomp"));
+ typedef HRESULT (__stdcall *DCompositionCreateDeviceFuncPtr)(
+ _In_opt_ IDXGIDevice *dxgiDevice,
+ _In_ REFIID iid,
+ _Outptr_ void **dcompositionDevice);
+ DCompositionCreateDeviceFuncPtr func = reinterpret_cast<DCompositionCreateDeviceFuncPtr>(
+ dcomplib.resolve("DCompositionCreateDevice"));
+ if (!func) {
+ qWarning("Unable to resolve DCompositionCreateDevice, perhaps dcomp.dll is missing?");
+ return nullptr;
+ }
+ IDCompositionDevice *device = nullptr;
+ HRESULT hr = func(nullptr, __uuidof(IDCompositionDevice), reinterpret_cast<void **>(&device));
+ if (FAILED(hr)) {
+ qWarning("Failed to create Direct Composition device: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return nullptr;
+ }
+ return device;
+}
+
+#ifdef QRHI_D3D12_HAS_DXC
+std::pair<IDxcCompiler *, IDxcLibrary *> createDxcCompiler()
+{
+ QSystemLibrary dxclib(QStringLiteral("dxcompiler"));
+ // this will not be in the system library location, hence onlySystemDirectory==false
+ if (!dxclib.load(false)) {
+ qWarning("Failed to load dxcompiler.dll");
+ return {};
+ }
+ DxcCreateInstanceProc func = reinterpret_cast<DxcCreateInstanceProc>(dxclib.resolve("DxcCreateInstance"));
+ if (!func) {
+ qWarning("Unable to resolve DxcCreateInstance");
+ return {};
+ }
+ IDxcCompiler *compiler = nullptr;
+ HRESULT hr = func(CLSID_DxcCompiler, __uuidof(IDxcCompiler), reinterpret_cast<void**>(&compiler));
+ if (FAILED(hr)) {
+ qWarning("Failed to create dxc compiler instance: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return {};
+ }
+ IDxcLibrary *library = nullptr;
+ hr = func(CLSID_DxcLibrary, __uuidof(IDxcLibrary), reinterpret_cast<void**>(&library));
+ if (FAILED(hr)) {
+ qWarning("Failed to create dxc library instance: %s",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return {};
+ }
+ return { compiler, library };
+}
+#endif
+
+void fillDriverInfo(QRhiDriverInfo *info, const DXGI_ADAPTER_DESC1 &desc)
+{
+ const QString name = QString::fromUtf16(reinterpret_cast<const char16_t *>(desc.Description));
+ info->deviceName = name.toUtf8();
+ info->deviceId = desc.DeviceId;
+ info->vendorId = desc.VendorId;
+ info->deviceType = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) ? QRhiDriverInfo::CpuDevice
+ : QRhiDriverInfo::UnknownDevice;
+}
+
+} // namespace
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhid3dhelpers_p.h b/src/gui/rhi/qrhid3dhelpers_p.h
new file mode 100644
index 0000000000..f31cdc8d11
--- /dev/null
+++ b/src/gui/rhi/qrhid3dhelpers_p.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRHID3DHELPERS_P_H
+#define QRHID3DHELPERS_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 <rhi/qrhi.h>
+
+#include <QtGui/qwindow.h>
+
+#include <dxgi1_6.h>
+#include <dcomp.h>
+#include <d3dcompiler.h>
+
+#if __has_include(<dxcapi.h>)
+#include <dxcapi.h>
+#define QRHI_D3D12_HAS_DXC
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace QRhiD3D {
+
+bool output6ForWindow(QWindow *w, IDXGIAdapter1 *adapter, IDXGIOutput6 **result);
+bool outputDesc1ForWindow(QWindow *w, IDXGIAdapter1 *adapter, DXGI_OUTPUT_DESC1 *result);
+float sdrWhiteLevelInNits(const DXGI_OUTPUT_DESC1 &outputDesc);
+
+pD3DCompile resolveD3DCompile();
+
+IDCompositionDevice *createDirectCompositionDevice();
+
+#ifdef QRHI_D3D12_HAS_DXC
+std::pair<IDxcCompiler *, IDxcLibrary *> createDxcCompiler();
+#endif
+
+void fillDriverInfo(QRhiDriverInfo *info, const DXGI_ADAPTER_DESC1 &desc);
+
+} // namespace
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index 13a7b997ea..62830c291d 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -1,13 +1,13 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qrhigles2_p_p.h"
-#include <QWindow>
+#include "qrhigles2_p.h"
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include <QtCore/qmap.h>
#include <QtGui/private/qopenglextensions_p.h>
#include <QtGui/private/qopenglprogrambinarycache_p.h>
+#include <QtGui/private/qwindow_p.h>
#include <qpa/qplatformopenglcontext.h>
#include <qmath.h>
@@ -28,10 +28,13 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiGles2InitParams
- \internal
\inmodule QtGui
+ \since 6.6
\brief OpenGL specific initialization parameters.
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
An OpenGL-based QRhi needs an already created QSurface that can be used in
combination with QOpenGLContext. Most commonly, this is a QOffscreenSurface
in practice. Additionally, while optional, it is recommended that the QWindow
@@ -52,18 +55,18 @@ QT_BEGIN_NAMESPACE
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
+ The QSurfaceFormat for the context is specified in \c format. The
constructor sets this to QSurfaceFormat::defaultFormat() so applications
- that use QSurfaceFormat::setDefaultFormat() do not need to set the format
- again.
+ that call QSurfaceFormat::setDefaultFormat() with the appropriate settings
+ before the constructor runs will not need to change value of \c format.
- \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.
+ \note Remember to set the depth and stencil buffer sizes to 24 and 8 when
+ the renderer relies on depth or stencil testing, either in the global
+ default QSurfaceFormat, or, alternatively, separately in all the involved
+ QSurfaceFormat instances: in \c format, the format argument passed to
+ newFallbackSurface(), and on any QWindow that is used with the QRhi.
- A QSurface has to be specified in \l fallbackSurface. In order to prevent
+ A QSurface has to be specified in \c fallbackSurface. In order to prevent
mistakes in threaded situations, this is never created automatically by the
QRhi because, like QWindow, instances of QSurface subclasses can often be
created on the gui/main thread only.
@@ -78,14 +81,14 @@ QT_BEGIN_NAMESPACE
instances that have their surface type set to QSurface::OpenGLSurface or
QSurface::RasterGLSurface.
- \note \l window is optional. It is recommended to specify it whenever
+ \note \c 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
+ systems. When \c window is not set, the very first
+ QOpenGLContext::makeCurrent() happens with \c fallbackSurface which may be
an invisible window on some platforms (for example, Windows) and that may
trigger unexpected problems in some cases.
- In case resource sharing with an existing QOpenGLContext is desired, \l
+ In case resource sharing with an existing QOpenGLContext is desired, \c
shareContext can be set to an existing QOpenGLContext. Alternatively,
Qt::AA_ShareOpenGLContexts is honored as well, when enabled.
@@ -94,8 +97,7 @@ QT_BEGIN_NAMESPACE
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.
+ \c{QRhiGles2NativeHandles::context} must be set to a non-null value then.
An alternative approach is to create a QOpenGLContext that
\l{QOpenGLContext::setShareContext()}{shares resources} with the other
@@ -106,12 +108,50 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \variable QRhiGles2InitParams::format
+
+ The QSurfaceFormat, initialized to QSurfaceFormat::defaultFormat() by default.
+*/
+
+/*!
+ \variable QRhiGles2InitParams::fallbackSurface
+
+ A QSurface compatible with \l format. Typically a QOffscreenSurface.
+ Providing this is mandatory. Be aware of the threading implications: a
+ QOffscreenSurface, like QWindow, must only ever be created and destroyed on
+ the main (gui) thread, even if the QRhi is created and operates on another
+ thread.
+*/
+
+/*!
+ \variable QRhiGles2InitParams::window
+
+ Optional, but setting it is recommended when targeting a QWindow with the
+ QRhi.
+*/
+
+/*!
+ \variable QRhiGles2InitParams::shareContext
+
+ Optional, the QOpenGLContext to share resource with. QRhi creates its own
+ context, and setting this member to a valid QOpenGLContext leads to calling
+ \l{QOpenGLContext::setShareContext()}{setShareContext()} with it.
+*/
+
+/*!
\class QRhiGles2NativeHandles
- \internal
\inmodule QtGui
+ \since 6.6
\brief Holds the OpenGL context used by the QRhi.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
+/*!
+ \variable QRhiGles2NativeHandles::context
+*/
+
#ifndef GL_BGRA
#define GL_BGRA 0x80E1
#endif
@@ -176,6 +216,10 @@ QT_BEGIN_NAMESPACE
#define GL_DEPTH_COMPONENT32F 0x8CAC
#endif
+#ifndef GL_UNSIGNED_INT_24_8
+#define GL_UNSIGNED_INT_24_8 0x84FA
+#endif
+
#ifndef GL_STENCIL_INDEX
#define GL_STENCIL_INDEX 0x1901
#endif
@@ -201,7 +245,7 @@ QT_BEGIN_NAMESPACE
#endif
#ifndef GL_FRAMEBUFFER_SRGB
-#define GL_FRAMEBUFFER_SRGB 0x8DB9
+#define GL_FRAMEBUFFER_SRGB 0x8DB9
#endif
#ifndef GL_READ_FRAMEBUFFER
@@ -316,6 +360,10 @@ QT_BEGIN_NAMESPACE
#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
#endif
+#ifndef GL_TEXTURE_2D_MULTISAMPLE_ARRAY
+#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102
+#endif
+
#ifndef GL_TEXTURE_EXTERNAL_OES
#define GL_TEXTURE_EXTERNAL_OES 0x8D65
#endif
@@ -436,6 +484,50 @@ QT_BEGIN_NAMESPACE
#define GL_GEOMETRY_SHADER 0x8DD9
#endif
+#ifndef GL_BACK_LEFT
+#define GL_BACK_LEFT 0x0402
+#endif
+
+#ifndef GL_BACK_RIGHT
+#define GL_BACK_RIGHT 0x0403
+#endif
+
+#ifndef GL_TEXTURE_1D
+# define GL_TEXTURE_1D 0x0DE0
+#endif
+
+#ifndef GL_TEXTURE_1D_ARRAY
+# define GL_TEXTURE_1D_ARRAY 0x8C18
+#endif
+
+#ifndef GL_HALF_FLOAT
+#define GL_HALF_FLOAT 0x140B
+#endif
+
+#ifndef GL_MAX_VERTEX_OUTPUT_COMPONENTS
+#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122
+#endif
+
+#ifndef GL_TIMESTAMP
+#define GL_TIMESTAMP 0x8E28
+#endif
+
+#ifndef GL_QUERY_RESULT
+#define GL_QUERY_RESULT 0x8866
+#endif
+
+#ifndef GL_QUERY_RESULT_AVAILABLE
+#define GL_QUERY_RESULT_AVAILABLE 0x8867
+#endif
+
+#ifndef GL_BUFFER
+#define GL_BUFFER 0x82E0
+#endif
+
+#ifndef GL_PROGRAM
+#define GL_PROGRAM 0x82E2
+#endif
+
/*!
Constructs a new QRhiGles2InitParams.
@@ -447,28 +539,12 @@ QRhiGles2InitParams::QRhiGles2InitParams()
}
/*!
- \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.
+ When \a format is not specified, its default value is the global default
+ format settable via QSurfaceFormat::setDefaultFormat().
+
\a format is adjusted as appropriate in order to avoid having problems
afterwards due to an incompatible context and surface.
@@ -480,7 +556,7 @@ QSurfaceFormat QRhiGles2InitParams::adjustedFormat(const QSurfaceFormat &format)
*/
QOffscreenSurface *QRhiGles2InitParams::newFallbackSurface(const QSurfaceFormat &format)
{
- QSurfaceFormat fmt = adjustedFormat(format);
+ QSurfaceFormat fmt = 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.
@@ -501,7 +577,7 @@ QOffscreenSurface *QRhiGles2InitParams::newFallbackSurface(const QSurfaceFormat
QRhiGles2::QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice)
: ofr(this)
{
- requestedFormat = QRhiGles2InitParams::adjustedFormat(params->format);
+ requestedFormat = params->format;
fallbackSurface = params->fallbackSurface;
maybeWindow = params->window; // may be null
maybeShareContext = params->shareContext; // may be null
@@ -531,15 +607,32 @@ static inline QSurface *currentSurfaceForCurrentContext(QOpenGLContext *ctx)
return currentSurface;
}
+QSurface *QRhiGles2::evaluateFallbackSurface() const
+{
+ // With Apple's deprecated OpenGL support we need to minimize the usage of
+ // QOffscreenSurface since delicate problems can pop up with
+ // NSOpenGLContext and drawables.
+#if defined(Q_OS_MACOS)
+ return maybeWindow && maybeWindow->handle() ? static_cast<QSurface *>(maybeWindow) : fallbackSurface;
+#else
+ return fallbackSurface;
+#endif
+}
+
bool QRhiGles2::ensureContext(QSurface *surface) const
{
if (!surface) {
+ // null means any surface is good because not going to render
if (currentSurfaceForCurrentContext(ctx))
return true;
- surface = fallbackSurface;
+ // if the context is not already current with a valid surface, use our
+ // fallback surface, but platform specific quirks may apply
+ surface = evaluateFallbackSurface();
} else if (surface->surfaceClass() == QSurface::Window && !surface->surfaceHandle()) {
- surface = fallbackSurface;
+ // the window is not usable anymore (no native window underneath), behave as if offscreen
+ surface = evaluateFallbackSurface();
} else if (!needsMakeCurrentDueToSwap && currentSurfaceForCurrentContext(ctx) == surface) {
+ // bail out if the makeCurrent is not necessary
return true;
}
needsMakeCurrentDueToSwap = false;
@@ -639,8 +732,40 @@ bool QRhiGles2::create(QRhi::Flags flags)
return false;
f = static_cast<QOpenGLExtensions *>(ctx->extraFunctions());
- glPolygonMode = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum)>(
- ctx->getProcAddress(QByteArrayLiteral("glPolygonMode")));
+ const QSurfaceFormat actualFormat = ctx->format();
+ caps.gles = actualFormat.renderableType() == QSurfaceFormat::OpenGLES;
+
+ if (!caps.gles) {
+ glPolygonMode = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum)>(
+ ctx->getProcAddress(QByteArrayLiteral("glPolygonMode")));
+
+ glTexImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
+ GLenum, GLint, GLint, GLsizei, GLint, GLenum, GLenum, const void *)>(
+ ctx->getProcAddress(QByteArrayLiteral("glTexImage1D")));
+
+ glTexStorage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLint, GLenum, GLsizei)>(
+ ctx->getProcAddress(QByteArrayLiteral("glTexStorage1D")));
+
+ glTexSubImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
+ GLenum, GLint, GLint, GLsizei, GLenum, GLenum, const GLvoid *)>(
+ ctx->getProcAddress(QByteArrayLiteral("glTexSubImage1D")));
+
+ glCopyTexSubImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLint, GLint, GLint,
+ GLint, GLsizei)>(
+ ctx->getProcAddress(QByteArrayLiteral("glCopyTexSubImage1D")));
+
+ glCompressedTexImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
+ GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const GLvoid *)>(
+ ctx->getProcAddress(QByteArrayLiteral("glCompressedTexImage1D")));
+
+ glCompressedTexSubImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
+ GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const GLvoid *)>(
+ ctx->getProcAddress(QByteArrayLiteral("glCompressedTexSubImage1D")));
+
+ glFramebufferTexture1D =
+ reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLenum, GLuint, GLint)>(
+ ctx->getProcAddress(QByteArrayLiteral("glFramebufferTexture1D")));
+ }
const char *vendor = reinterpret_cast<const char *>(f->glGetString(GL_VENDOR));
const char *renderer = reinterpret_cast<const char *>(f->glGetString(GL_RENDERER));
@@ -659,8 +784,6 @@ bool QRhiGles2::create(QRhi::Flags flags)
if (version)
driverInfoStruct.deviceName += QByteArray(version);
- const QSurfaceFormat actualFormat = ctx->format();
-
caps.ctxMajor = actualFormat.majorVersion();
caps.ctxMinor = actualFormat.minorVersion();
@@ -720,13 +843,19 @@ bool QRhiGles2::create(QRhi::Flags flags)
f->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &caps.maxTextureSize);
- if (caps.ctxMajor >= 3 || actualFormat.renderableType() == QSurfaceFormat::OpenGL) {
+ if (!caps.gles || caps.ctxMajor >= 3) {
+ // non-ES or ES 3.0+
f->glGetIntegerv(GL_MAX_DRAW_BUFFERS, &caps.maxDrawBuffers);
+ caps.hasDrawBuffersFunc = true;
f->glGetIntegerv(GL_MAX_SAMPLES, &caps.maxSamples);
caps.maxSamples = qMax(1, caps.maxSamples);
} else {
+ // ES 2.0 / WebGL 1
caps.maxDrawBuffers = 1;
- caps.maxSamples = 1;
+ caps.hasDrawBuffersFunc = false;
+ // This does not mean MSAA is not supported, just that we cannot query
+ // the supported sample counts. Assume that 4x is always supported.
+ caps.maxSamples = 4;
}
caps.msaaRenderBuffer = f->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
@@ -735,14 +864,20 @@ bool QRhiGles2::create(QRhi::Flags flags)
caps.npotTextureFull = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)
&& f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
- caps.gles = actualFormat.renderableType() == QSurfaceFormat::OpenGLES;
if (caps.gles)
caps.fixedIndexPrimitiveRestart = caps.ctxMajor >= 3; // ES 3.0
else
caps.fixedIndexPrimitiveRestart = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); // 4.3
- if (caps.fixedIndexPrimitiveRestart)
+ if (caps.fixedIndexPrimitiveRestart) {
+#ifdef Q_OS_WASM
+ // WebGL 2 behaves as if GL_PRIMITIVE_RESTART_FIXED_INDEX was always
+ // enabled (i.e. matching D3D/Metal), and the value cannot be passed to
+ // glEnable, so skip the call.
+#else
f->glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
+#endif
+ }
caps.bgraExternalFormat = f->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat);
caps.bgraInternalFormat = caps.bgraExternalFormat && caps.gles;
@@ -757,7 +892,13 @@ bool QRhiGles2::create(QRhi::Flags flags)
#else
caps.needsDepthStencilCombinedAttach = false;
#endif
- caps.srgbCapableDefaultFramebuffer = f->hasOpenGLExtension(QOpenGLExtensions::SRGBFrameBuffer);
+
+ // QOpenGLExtensions::SRGBFrameBuffer is not useful here. We need to know if
+ // controlling the sRGB-on-shader-write state is supported, not that if the
+ // default framebuffer is sRGB-capable. And there are two different
+ // extensions for desktop and ES.
+ caps.srgbWriteControl = ctx->hasExtension("GL_EXT_framebuffer_sRGB") || ctx->hasExtension("GL_EXT_sRGB_write_control");
+
caps.coreProfile = actualFormat.profile() == QSurfaceFormat::CoreProfile;
if (caps.gles)
@@ -833,6 +974,11 @@ bool QRhiGles2::create(QRhi::Flags flags)
caps.texture3D = caps.ctxMajor >= 3; // 3.0
if (caps.gles)
+ caps.texture1D = false; // ES
+ else
+ caps.texture1D = glTexImage1D && (caps.ctxMajor >= 2); // 2.0
+
+ if (caps.gles)
caps.tessellation = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // ES 3.2
else
caps.tessellation = caps.ctxMajor >= 4; // 4.0
@@ -873,7 +1019,7 @@ bool QRhiGles2::create(QRhi::Flags flags)
f->glGetIntegerv(GL_MAX_VARYING_VECTORS, &caps.maxVertexOutputs);
} else if (caps.ctxMajor >= 3) {
GLint components = 0;
- f->glGetIntegerv(GL_MAX_VARYING_COMPONENTS, &components);
+ f->glGetIntegerv(caps.coreProfile ? GL_MAX_VERTEX_OUTPUT_COMPONENTS : GL_MAX_VARYING_COMPONENTS, &components);
caps.maxVertexOutputs = components / 4;
} else {
// OpenGL before 3.0 only has this, and not the same as
@@ -886,7 +1032,8 @@ bool QRhiGles2::create(QRhi::Flags flags)
if (!caps.gles) {
f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
- f->glEnable(GL_POINT_SPRITE);
+ if (!caps.coreProfile)
+ f->glEnable(GL_POINT_SPRITE);
} // else (with gles) these are always on
// Match D3D and others when it comes to seamless cubemap filtering.
@@ -895,6 +1042,60 @@ bool QRhiGles2::create(QRhi::Flags flags)
if (!caps.gles && (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2)))
f->glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+ caps.halfAttributes = f->hasOpenGLExtension(QOpenGLExtensions::HalfFloatVertex);
+
+ // We always require GL_OVR_multiview2 for symmetry with other backends.
+ caps.multiView = f->hasOpenGLExtension(QOpenGLExtensions::MultiView)
+ && f->hasOpenGLExtension(QOpenGLExtensions::MultiViewExtended);
+ if (caps.multiView) {
+ glFramebufferTextureMultiviewOVR =
+ reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei)>(
+ ctx->getProcAddress(QByteArrayLiteral("glFramebufferTextureMultiviewOVR")));
+ }
+
+ // Only do timestamp queries on OpenGL 3.3+.
+ caps.timestamps = !caps.gles && (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 3));
+ if (caps.timestamps) {
+ glQueryCounter = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLuint, GLenum)>(
+ ctx->getProcAddress(QByteArrayLiteral("glQueryCounter")));
+ glGetQueryObjectui64v = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLuint, GLenum, quint64 *)>(
+ ctx->getProcAddress(QByteArrayLiteral("glGetQueryObjectui64v")));
+ if (!glQueryCounter || !glGetQueryObjectui64v)
+ caps.timestamps = false;
+ }
+
+ // glObjectLabel is available on OpenGL ES 3.2+ and OpenGL 4.3+
+ if (caps.gles)
+ caps.objectLabel = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2);
+ else
+ caps.objectLabel = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3);
+ if (caps.objectLabel) {
+ glObjectLabel = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLuint, GLsizei, const GLchar *)>(
+ ctx->getProcAddress(QByteArrayLiteral("glObjectLabel")));
+ }
+
+ if (caps.gles) {
+ // This is the third way to get multisample rendering with GLES. (1. is
+ // multisample render buffer -> resolve to texture; 2. is multisample
+ // texture with GLES 3.1; 3. is this, avoiding the explicit multisample
+ // buffer and should be more efficient with tiled architectures.
+ // Interesting also because 2. does not seem to work in practice on
+ // devices such as the Quest 3)
+ caps.glesMultisampleRenderToTexture = ctx->hasExtension("GL_EXT_multisampled_render_to_texture");
+ if (caps.glesMultisampleRenderToTexture) {
+ glFramebufferTexture2DMultisampleEXT = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLenum, GLuint, GLint, GLsizei)>(
+ ctx->getProcAddress(QByteArrayLiteral("glFramebufferTexture2DMultisampleEXT")));
+ }
+ caps.glesMultiviewMultisampleRenderToTexture = ctx->hasExtension("GL_OVR_multiview_multisampled_render_to_texture");
+ if (caps.glesMultiviewMultisampleRenderToTexture) {
+ glFramebufferTextureMultisampleMultiviewOVR = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLuint, GLint, GLsizei, GLint, GLsizei)>(
+ ctx->getProcAddress(QByteArrayLiteral("glFramebufferTextureMultisampleMultiviewOVR")));
+ }
+ } else {
+ caps.glesMultisampleRenderToTexture = false;
+ caps.glesMultiviewMultisampleRenderToTexture = false;
+ }
+
nativeHandlesStruct.context = ctx;
contextLost = false;
@@ -910,6 +1111,11 @@ void QRhiGles2::destroy()
ensureContext();
executeDeferredReleases();
+ if (ofr.tsQueries[0]) {
+ f->glDeleteQueries(2, ofr.tsQueries);
+ ofr.tsQueries[0] = ofr.tsQueries[1] = 0;
+ }
+
if (vao) {
f->glDeleteVertexArrays(1, &vao);
vao = 0;
@@ -929,7 +1135,7 @@ void QRhiGles2::destroy()
void QRhiGles2::executeDeferredReleases()
{
- for (int i = releaseQueue.count() - 1; i >= 0; --i) {
+ for (int i = releaseQueue.size() - 1; i >= 0; --i) {
const QRhiGles2::DeferredReleaseEntry &e(releaseQueue[i]);
switch (e.type) {
case QRhiGles2::DeferredReleaseEntry::Buffer:
@@ -947,6 +1153,7 @@ void QRhiGles2::executeDeferredReleases()
break;
case QRhiGles2::DeferredReleaseEntry::TextureRenderTarget:
f->glDeleteFramebuffers(1, &e.textureRenderTarget.framebuffer);
+ f->glDeleteTextures(1, &e.textureRenderTarget.nonMsaaThrowawayDepthTexture);
break;
default:
Q_UNREACHABLE();
@@ -966,23 +1173,12 @@ QList<int> QRhiGles2::supportedSampleCounts() const
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)
+QRhiBuffer *QRhiGles2::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
{
return new QGles2Buffer(this, type, usage, size);
}
@@ -1101,13 +1297,13 @@ static inline void toGlTextureFormat(QRhiTexture::Format format, const QRhiGles2
*glintformat = GL_DEPTH_COMPONENT24;
*glsizedintformat = *glintformat;
*glformat = GL_DEPTH_COMPONENT;
- *gltype = GL_UNSIGNED_SHORT;
+ *gltype = GL_UNSIGNED_INT;
break;
case QRhiTexture::D24S8:
*glintformat = GL_DEPTH24_STENCIL8;
*glsizedintformat = *glintformat;
*glformat = GL_DEPTH_STENCIL;
- *gltype = GL_UNSIGNED_SHORT;
+ *gltype = GL_UNSIGNED_INT_24_8;
break;
case QRhiTexture::D32F:
*glintformat = GL_DEPTH_COMPONENT32F;
@@ -1184,7 +1380,7 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
case QRhi::DebugMarkers:
return false;
case QRhi::Timestamps:
- return false;
+ return caps.timestamps;
case QRhi::Instancing:
return caps.instancing;
case QRhi::CustomInstanceStepRate:
@@ -1247,9 +1443,24 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
return false;
case QRhi::NonFillPolygonMode:
return !caps.gles;
- default:
- Q_UNREACHABLE();
+ case QRhi::OneDimensionalTextures:
+ return caps.texture1D;
+ case QRhi::OneDimensionalTextureMipmaps:
+ return caps.texture1D;
+ case QRhi::HalfAttributes:
+ return caps.halfAttributes;
+ case QRhi::RenderToOneDimensionalTexture:
+ return caps.texture1D;
+ case QRhi::ThreeDimensionalTextureMipmaps:
+ return caps.texture3D;
+ case QRhi::MultiView:
+ return caps.multiView && caps.maxTextureArraySize > 0;
+ case QRhi::TextureViewFormat:
return false;
+ case QRhi::ResolveDepthStencil:
+ return true;
+ default:
+ Q_UNREACHABLE_RETURN(false);
}
}
@@ -1287,8 +1498,7 @@ int QRhiGles2::resourceLimit(QRhi::ResourceLimit limit) const
case QRhi::MaxVertexOutputs:
return caps.maxVertexOutputs;
default:
- Q_UNREACHABLE();
- return 0;
+ Q_UNREACHABLE_RETURN(0);
}
}
@@ -1302,9 +1512,11 @@ QRhiDriverInfo QRhiGles2::driverInfo() const
return driverInfoStruct;
}
-QRhiMemAllocStats QRhiGles2::graphicsMemoryAllocationStatistics()
+QRhiStats QRhiGles2::statistics()
{
- return {};
+ QRhiStats result;
+ result.totalPipelineCreationTime = totalPipelineCreationTime();
+ return result;
}
bool QRhiGles2::makeThreadLocalNativeContextCurrent()
@@ -1353,8 +1565,8 @@ QByteArray QRhiGles2::pipelineCacheData()
memset(&header, 0, sizeof(header));
header.rhiId = pipelineCacheRhiId();
header.arch = quint32(sizeof(void*));
- header.programBinaryCount = m_pipelineCache.count();
- const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
+ header.programBinaryCount = m_pipelineCache.size();
+ const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.size()));
if (driverStrLen)
memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
header.driver[driverStrLen] = '\0';
@@ -1404,7 +1616,7 @@ void QRhiGles2::setPipelineCacheData(const QByteArray &data)
const size_t headerSize = sizeof(QGles2PipelineCacheDataHeader);
if (data.size() < qsizetype(headerSize)) {
- qWarning("setPipelineCacheData: Invalid blob size (header incomplete)");
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (header incomplete)");
return;
}
const size_t dataOffset = headerSize;
@@ -1413,27 +1625,27 @@ void QRhiGles2::setPipelineCacheData(const QByteArray &data)
const quint32 rhiId = pipelineCacheRhiId();
if (header.rhiId != rhiId) {
- qWarning("setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
- rhiId, header.rhiId);
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
+ rhiId, header.rhiId);
return;
}
const quint32 arch = quint32(sizeof(void*));
if (header.arch != arch) {
- qWarning("setPipelineCacheData: Architecture does not match (%u, %u)",
- arch, header.arch);
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
+ arch, header.arch);
return;
}
if (header.programBinaryCount == 0)
return;
- const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
+ const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.size()));
if (strncmp(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
- qWarning("setPipelineCacheData: OpenGL vendor/renderer/version does not match");
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: OpenGL vendor/renderer/version does not match");
return;
}
if (data.size() < qsizetype(dataOffset + header.dataSize)) {
- qWarning("setPipelineCacheData: Invalid blob size (data incomplete)");
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (data incomplete)");
return;
}
@@ -1461,7 +1673,7 @@ void QRhiGles2::setPipelineCacheData(const QByteArray &data)
m_pipelineCache.insert(key, { format, data });
}
- qCDebug(QRHI_LOG_INFO, "Seeded pipeline cache with %d program binaries", int(m_pipelineCache.count()));
+ qCDebug(QRHI_LOG_INFO, "Seeded pipeline cache with %d program binaries", int(m_pipelineCache.size()));
}
QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
@@ -1543,8 +1755,8 @@ void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, srb);
if (cbD->passNeedsResourceTracking) {
QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
- for (int i = 0, ie = srbD->m_bindings.count(); i != ie; ++i) {
- const QRhiShaderResourceBinding::Data *b = srbD->m_bindings.at(i).data();
+ for (int i = 0, ie = srbD->m_bindings.size(); i != ie; ++i) {
+ const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
switch (b->type) {
case QRhiShaderResourceBinding::UniformBuffer:
// no BufUniformRead / AccessUniform because no real uniform buffers are used
@@ -1821,10 +2033,14 @@ const QRhiNativeHandles *QRhiGles2::nativeHandles(QRhiCommandBuffer *cb)
return nullptr;
}
-static inline void addBoundaryCommand(QGles2CommandBuffer *cbD, QGles2CommandBuffer::Command::Cmd type)
+static inline void addBoundaryCommand(QGles2CommandBuffer *cbD, QGles2CommandBuffer::Command::Cmd type, GLuint tsQuery = 0)
{
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = type;
+ if (type == QGles2CommandBuffer::Command::BeginFrame)
+ cmd.args.beginFrame.timestampQuery = tsQuery;
+ else if (type == QGles2CommandBuffer::Command::EndFrame)
+ cmd.args.endFrame.timestampQuery = tsQuery;
}
void QRhiGles2::beginExternal(QRhiCommandBuffer *cb)
@@ -1853,8 +2069,14 @@ void QRhiGles2::beginExternal(QRhiCommandBuffer *cb)
cbD->resetCommands();
- if (vao)
+ if (vao) {
f->glBindVertexArray(0);
+ } else {
+ f->glBindBuffer(GL_ARRAY_BUFFER, 0);
+ f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ if (caps.compute)
+ f->glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+ }
}
void QRhiGles2::endExternal(QRhiCommandBuffer *cb)
@@ -1878,6 +2100,12 @@ void QRhiGles2::endExternal(QRhiCommandBuffer *cb)
enqueueBindFramebuffer(cbD->currentTarget, cbD);
}
+double QRhiGles2::lastCompletedGpuTime(QRhiCommandBuffer *cb)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ return cbD->lastGpuTime;
+}
+
QRhi::FrameOpResult QRhiGles2::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags)
{
QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
@@ -1891,7 +2119,17 @@ QRhi::FrameOpResult QRhiGles2::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF
executeDeferredReleases();
swapChainD->cb.resetState();
- addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::BeginFrame);
+ if (swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex]) {
+ double elapsedSec = 0;
+ if (swapChainD->timestamps.tryQueryTimestamps(swapChainD->currentTimestampPairIndex, this, &elapsedSec))
+ swapChainD->cb.lastGpuTime = elapsedSec;
+ }
+
+ GLuint tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2];
+ GLuint tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1];
+ const bool recordTimestamps = tsStart && tsEnd && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
+
+ addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::BeginFrame, recordTimestamps ? tsStart : 0);
return QRhi::FrameOpSuccess;
}
@@ -1901,7 +2139,15 @@ QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
Q_ASSERT(currentSwapChain == swapChainD);
- addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::EndFrame);
+ GLuint tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2];
+ GLuint tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1];
+ const bool recordTimestamps = tsStart && tsEnd && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
+ if (recordTimestamps) {
+ swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex] = true;
+ swapChainD->currentTimestampPairIndex = (swapChainD->currentTimestampPairIndex + 1) % QGles2SwapChainTimestamps::TIMESTAMP_PAIRS;
+ }
+
+ addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::EndFrame, recordTimestamps ? tsEnd : 0);
if (!ensureContext(swapChainD->surface))
return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
@@ -1933,7 +2179,12 @@ QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi:
executeDeferredReleases();
ofr.cbWrapper.resetState();
- addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::BeginFrame);
+ if (rhiFlags.testFlag(QRhi::EnableTimestamps) && caps.timestamps) {
+ if (!ofr.tsQueries[0])
+ f->glGenQueries(2, ofr.tsQueries);
+ }
+
+ addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::BeginFrame, ofr.tsQueries[0]);
*cb = &ofr.cbWrapper;
return QRhi::FrameOpSuccess;
@@ -1945,13 +2196,29 @@ QRhi::FrameOpResult QRhiGles2::endOffscreenFrame(QRhi::EndFrameFlags flags)
Q_ASSERT(ofr.active);
ofr.active = false;
- addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::EndFrame);
+ addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::EndFrame, ofr.tsQueries[1]);
if (!ensureContext())
return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
executeCommandBuffer(&ofr.cbWrapper);
+ // Just as endFrame() does a flush when skipping the swapBuffers(), do it
+ // here as well. This has the added benefit of playing nice when rendering
+ // to a texture from a context and then consuming that texture from
+ // another, sharing context.
+ f->glFlush();
+
+ if (ofr.tsQueries[0]) {
+ quint64 timestamps[2];
+ glGetQueryObjectui64v(ofr.tsQueries[1], GL_QUERY_RESULT, &timestamps[1]);
+ glGetQueryObjectui64v(ofr.tsQueries[0], GL_QUERY_RESULT, &timestamps[0]);
+ if (timestamps[1] >= timestamps[0]) {
+ const quint64 nanoseconds = timestamps[1] - timestamps[0];
+ ofr.cbWrapper.lastGpuTime = nanoseconds / 1000000000.0; // seconds
+ }
+ }
+
return QRhi::FrameOpSuccess;
}
@@ -1973,6 +2240,12 @@ QRhi::FrameOpResult QRhiGles2::finish()
executeCommandBuffer(&currentSwapChain->cb);
currentSwapChain->cb.resetCommands();
}
+ // Do an actual glFinish(). May seem superfluous, but this is what
+ // matches most other backends e.g. Vulkan/Metal that do a heavyweight
+ // wait-for-idle blocking in their finish(). More importantly, this
+ // allows clients simply call finish() in threaded or shared context
+ // situations where one explicitly needs to do a glFlush or Finish.
+ f->glFinish();
}
return QRhi::FrameOpSuccess;
}
@@ -2059,37 +2332,56 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
const bool isCompressed = isCompressedFormat(texD->m_format);
const bool isCubeMap = texD->m_flags.testFlag(QRhiTexture::CubeMap);
const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ const bool is1D = texD->m_flags.testFlag(QRhiTexture::OneDimensional);
const bool isArray = texD->m_flags.testFlag(QRhiTexture::TextureArray);
const GLenum faceTargetBase = isCubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
const GLenum effectiveTarget = faceTargetBase + (isCubeMap ? uint(layer) : 0u);
const QPoint dp = subresDesc.destinationTopLeft();
const QByteArray rawData = subresDesc.data();
- if (!subresDesc.image().isNull()) {
- QImage img = subresDesc.image();
- QSize size = img.size();
+
+ auto setCmdByNotCompressedData = [&](const void* data, QSize size, quint32 dataStride)
+ {
+ quint32 bytesPerLine = 0;
+ quint32 bytesPerPixel = 0;
+ textureFormatInfo(texD->m_format, size, &bytesPerLine, nullptr, &bytesPerPixel);
+
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
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 = effectiveTarget;
cmd.args.subImage.level = level;
cmd.args.subImage.dx = dp.x();
- cmd.args.subImage.dy = dp.y();
+ cmd.args.subImage.dy = is1D && isArray ? layer : dp.y();
cmd.args.subImage.dz = is3D || isArray ? layer : 0;
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.rowLength = 0;
- cmd.args.subImage.data = cbD->retainImage(img);
+
+ if (dataStride == 0)
+ dataStride = bytesPerLine;
+
+ cmd.args.subImage.rowStartAlign = (dataStride & 3) ? 1 : 4;
+ cmd.args.subImage.rowLength = bytesPerPixel ? dataStride / bytesPerPixel : 0;
+
+ cmd.args.subImage.data = data;
+ };
+
+ if (!subresDesc.image().isNull()) {
+ QImage img = subresDesc.image();
+ QSize size = img.size();
+ 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());
+ }
+
+ setCmdByNotCompressedData(cbD->retainImage(img), size, img.bytesPerLine());
} else if (!rawData.isEmpty() && isCompressed) {
+ const int depth = qMax(1, texD->m_depth);
+ const int arraySize = qMax(0, texD->m_arraySize);
if ((texD->flags().testFlag(QRhiTexture::UsedAsCompressedAtlas) || is3D || isArray)
&& !texD->zeroInitialized)
{
@@ -2101,9 +2393,9 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
quint32 byteSize = 0;
compressedFormatInfo(texD->m_format, texD->m_pixelSize, nullptr, &byteSize, nullptr);
if (is3D)
- byteSize *= texD->m_depth;
+ byteSize *= depth;
if (isArray)
- byteSize *= texD->m_arraySize;
+ byteSize *= arraySize;
QByteArray zeroBuf(byteSize, 0);
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QGles2CommandBuffer::Command::CompressedImage;
@@ -2113,8 +2405,8 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
cmd.args.compressedImage.level = level;
cmd.args.compressedImage.glintformat = texD->glintformat;
cmd.args.compressedImage.w = texD->m_pixelSize.width();
- cmd.args.compressedImage.h = texD->m_pixelSize.height();
- cmd.args.compressedImage.depth = is3D ? texD->m_depth : (isArray ? texD->m_arraySize : 0);
+ cmd.args.compressedImage.h = is1D && isArray ? arraySize : texD->m_pixelSize.height();
+ cmd.args.compressedImage.depth = is3D ? depth : (isArray ? arraySize : 0);
cmd.args.compressedImage.size = byteSize;
cmd.args.compressedImage.data = cbD->retainData(zeroBuf);
texD->zeroInitialized = true;
@@ -2130,7 +2422,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
cmd.args.compressedSubImage.faceTarget = effectiveTarget;
cmd.args.compressedSubImage.level = level;
cmd.args.compressedSubImage.dx = dp.x();
- cmd.args.compressedSubImage.dy = dp.y();
+ cmd.args.compressedSubImage.dy = is1D && isArray ? layer : dp.y();
cmd.args.compressedSubImage.dz = is3D || isArray ? layer : 0;
cmd.args.compressedSubImage.w = size.width();
cmd.args.compressedSubImage.h = size.height();
@@ -2146,39 +2438,16 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
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.depth = is3D ? texD->m_depth : (isArray ? texD->m_arraySize : 0);
+ cmd.args.compressedImage.h = is1D && isArray ? arraySize : size.height();
+ cmd.args.compressedImage.depth = is3D ? depth : (isArray ? arraySize : 0);
cmd.args.compressedImage.size = rawData.size();
cmd.args.compressedImage.data = cbD->retainData(rawData);
}
} else if (!rawData.isEmpty()) {
const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
: subresDesc.sourceSize();
- quint32 bytesPerLine = 0;
- quint32 bytesPerPixel = 0;
- textureFormatInfo(texD->m_format, size, &bytesPerLine, nullptr, &bytesPerPixel);
- QGles2CommandBuffer::Command &cmd(cbD->commands.get());
- cmd.cmd = QGles2CommandBuffer::Command::SubImage;
- cmd.args.subImage.target = texD->target;
- cmd.args.subImage.texture = texD->texture;
- cmd.args.subImage.faceTarget = effectiveTarget;
- cmd.args.subImage.level = level;
- cmd.args.subImage.dx = dp.x();
- cmd.args.subImage.dy = dp.y();
- cmd.args.subImage.dz = is3D || isArray ? layer : 0;
- 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 alignment
- // requirement) is 4. QImage guarantees 4 byte aligned
- // row starts, but our raw data here does not.
- cmd.args.subImage.rowStartAlign = (bytesPerLine & 3) ? 1 : 4;
- if (subresDesc.dataStride() && bytesPerPixel)
- cmd.args.subImage.rowLength = subresDesc.dataStride() / bytesPerPixel;
- else
- cmd.args.subImage.rowLength = 0;
- cmd.args.subImage.data = cbD->retainData(rawData);
+
+ setCmdByNotCompressedData(cbD->retainData(rawData), size, subresDesc.dataStride());
} else {
qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
}
@@ -2245,9 +2514,9 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
QGles2Texture *texD = QRHI_RES(QGles2Texture, u.dst);
- for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
+ for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
- for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
}
}
@@ -2276,6 +2545,8 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
const bool srcHasZ = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || srcD->m_flags.testFlag(QRhiTexture::TextureArray);
const bool dstHasZ = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || dstD->m_flags.testFlag(QRhiTexture::TextureArray);
+ const bool dstIs1dArray = dstD->m_flags.testFlag(QRhiTexture::OneDimensional)
+ && dstD->m_flags.testFlag(QRhiTexture::TextureArray);
cmd.args.copyTex.srcTarget = srcD->target;
cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + (srcHasZ ? 0u : uint(u.desc.sourceLayer()));
@@ -2290,7 +2561,7 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
cmd.args.copyTex.dstTexture = dstD->texture;
cmd.args.copyTex.dstLevel = u.desc.destinationLevel();
cmd.args.copyTex.dstX = dp.x();
- cmd.args.copyTex.dstY = dp.y();
+ cmd.args.copyTex.dstY = dstIs1dArray ? u.desc.destinationLayer() : dp.y();
cmd.args.copyTex.dstZ = dstHasZ ? u.desc.destinationLayer() : 0;
cmd.args.copyTex.w = copySize.width();
@@ -2352,8 +2623,7 @@ static inline GLenum toGlTopology(QRhiGraphicsPipeline::Topology t)
case QRhiGraphicsPipeline::Patches:
return GL_PATCHES;
default:
- Q_UNREACHABLE();
- return GL_TRIANGLES;
+ Q_UNREACHABLE_RETURN(GL_TRIANGLES);
}
}
@@ -2365,8 +2635,7 @@ static inline GLenum toGlCullMode(QRhiGraphicsPipeline::CullMode c)
case QRhiGraphicsPipeline::Back:
return GL_BACK;
default:
- Q_UNREACHABLE();
- return GL_BACK;
+ Q_UNREACHABLE_RETURN(GL_BACK);
}
}
@@ -2378,8 +2647,7 @@ static inline GLenum toGlFrontFace(QRhiGraphicsPipeline::FrontFace f)
case QRhiGraphicsPipeline::CW:
return GL_CW;
default:
- Q_UNREACHABLE();
- return GL_CCW;
+ Q_UNREACHABLE_RETURN(GL_CCW);
}
}
@@ -2423,8 +2691,7 @@ static inline GLenum toGlBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
qWarning("Unsupported blend factor %d", f);
return GL_ZERO;
default:
- Q_UNREACHABLE();
- return GL_ZERO;
+ Q_UNREACHABLE_RETURN(GL_ZERO);
}
}
@@ -2442,8 +2709,7 @@ static inline GLenum toGlBlendOp(QRhiGraphicsPipeline::BlendOp op)
case QRhiGraphicsPipeline::Max:
return GL_MAX;
default:
- Q_UNREACHABLE();
- return GL_FUNC_ADD;
+ Q_UNREACHABLE_RETURN(GL_FUNC_ADD);
}
}
@@ -2467,8 +2733,7 @@ static inline GLenum toGlCompareOp(QRhiGraphicsPipeline::CompareOp op)
case QRhiGraphicsPipeline::Always:
return GL_ALWAYS;
default:
- Q_UNREACHABLE();
- return GL_ALWAYS;
+ Q_UNREACHABLE_RETURN(GL_ALWAYS);
}
}
@@ -2492,8 +2757,7 @@ static inline GLenum toGlStencilOp(QRhiGraphicsPipeline::StencilOp op)
case QRhiGraphicsPipeline::DecrementAndWrap:
return GL_DECR_WRAP;
default:
- Q_UNREACHABLE();
- return GL_KEEP;
+ Q_UNREACHABLE_RETURN(GL_KEEP);
}
}
@@ -2505,8 +2769,7 @@ static inline GLenum toGlPolygonMode(QRhiGraphicsPipeline::PolygonMode mode)
case QRhiGraphicsPipeline::PolygonMode::Line:
return GL_LINE;
default:
- Q_UNREACHABLE();
- return GL_FILL;
+ Q_UNREACHABLE_RETURN(GL_FILL);
}
}
@@ -2524,8 +2787,7 @@ static inline GLenum toGlMinFilter(QRhiSampler::Filter f, QRhiSampler::Filter m)
else
return m == QRhiSampler::Nearest ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR;
default:
- Q_UNREACHABLE();
- return GL_LINEAR;
+ Q_UNREACHABLE_RETURN(GL_LINEAR);
}
}
@@ -2537,8 +2799,7 @@ static inline GLenum toGlMagFilter(QRhiSampler::Filter f)
case QRhiSampler::Linear:
return GL_LINEAR;
default:
- Q_UNREACHABLE();
- return GL_LINEAR;
+ Q_UNREACHABLE_RETURN(GL_LINEAR);
}
}
@@ -2552,8 +2813,7 @@ static inline GLenum toGlWrapMode(QRhiSampler::AddressMode m)
case QRhiSampler::Mirror:
return GL_MIRRORED_REPEAT;
default:
- Q_UNREACHABLE();
- return GL_CLAMP_TO_EDGE;
+ Q_UNREACHABLE_RETURN(GL_CLAMP_TO_EDGE);
}
}
@@ -2577,8 +2837,7 @@ static inline GLenum toGlTextureCompareFunc(QRhiSampler::CompareOp op)
case QRhiSampler::Always:
return GL_ALWAYS;
default:
- Q_UNREACHABLE();
- return GL_NEVER;
+ Q_UNREACHABLE_RETURN(GL_NEVER);
}
}
@@ -2707,6 +2966,8 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
const QGles2CommandBuffer::Command &cmd(*it);
switch (cmd.cmd) {
case QGles2CommandBuffer::Command::BeginFrame:
+ if (cmd.args.beginFrame.timestampQuery)
+ glQueryCounter(cmd.args.beginFrame.timestampQuery, GL_TIMESTAMP);
if (caps.coreProfile) {
if (!vao)
f->glGenVertexArrays(1, &vao);
@@ -2733,6 +2994,8 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
#endif
if (vao)
f->glBindVertexArray(0);
+ if (cmd.args.endFrame.timestampQuery)
+ glQueryCounter(cmd.args.endFrame.timestampQuery, GL_TIMESTAMP);
break;
case QGles2CommandBuffer::Command::ResetFrame:
if (vao)
@@ -2861,6 +3124,54 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
type = GL_INT;
size = 1;
break;
+ case QRhiVertexInputAttribute::Half4:
+ type = GL_HALF_FLOAT;
+ size = 4;
+ break;
+ case QRhiVertexInputAttribute::Half3:
+ type = GL_HALF_FLOAT;
+ size = 3;
+ break;
+ case QRhiVertexInputAttribute::Half2:
+ type = GL_HALF_FLOAT;
+ size = 2;
+ break;
+ case QRhiVertexInputAttribute::Half:
+ type = GL_HALF_FLOAT;
+ size = 1;
+ break;
+ case QRhiVertexInputAttribute::UShort4:
+ type = GL_UNSIGNED_SHORT;
+ size = 4;
+ break;
+ case QRhiVertexInputAttribute::UShort3:
+ type = GL_UNSIGNED_SHORT;
+ size = 3;
+ break;
+ case QRhiVertexInputAttribute::UShort2:
+ type = GL_UNSIGNED_SHORT;
+ size = 2;
+ break;
+ case QRhiVertexInputAttribute::UShort:
+ type = GL_UNSIGNED_SHORT;
+ size = 1;
+ break;
+ case QRhiVertexInputAttribute::SShort4:
+ type = GL_SHORT;
+ size = 4;
+ break;
+ case QRhiVertexInputAttribute::SShort3:
+ type = GL_SHORT;
+ size = 3;
+ break;
+ case QRhiVertexInputAttribute::SShort2:
+ type = GL_SHORT;
+ size = 2;
+ break;
+ case QRhiVertexInputAttribute::SShort:
+ type = GL_SHORT;
+ size = 1;
+ break;
default:
break;
}
@@ -2887,7 +3198,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
f->glEnableVertexAttribArray(GLuint(locationIdx));
}
if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance && caps.instancing) {
- f->glVertexAttribDivisor(GLuint(locationIdx), GLuint(inputBinding->instanceStepRate()));
+ f->glVertexAttribDivisor(GLuint(locationIdx), inputBinding->instanceStepRate());
if (Q_LIKELY(locationIdx < CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT))
state.nonzeroAttribDivisor[locationIdx] = true;
else
@@ -2984,24 +3295,32 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
cmd.args.bindShaderResources.dynamicOffsetCount);
break;
case QGles2CommandBuffer::Command::BindFramebuffer:
+ {
+ QVarLengthArray<GLenum, 8> bufs;
if (cmd.args.bindFramebuffer.fbo) {
f->glBindFramebuffer(GL_FRAMEBUFFER, cmd.args.bindFramebuffer.fbo);
+ const int colorAttCount = cmd.args.bindFramebuffer.colorAttCount;
+ bufs.append(colorAttCount > 0 ? GL_COLOR_ATTACHMENT0 : GL_NONE);
if (caps.maxDrawBuffers > 1) {
- const int colorAttCount = cmd.args.bindFramebuffer.colorAttCount;
- QVarLengthArray<GLenum, 8> bufs;
- for (int i = 0; i < colorAttCount; ++i)
+ for (int i = 1; i < colorAttCount; ++i)
bufs.append(GL_COLOR_ATTACHMENT0 + uint(i));
- f->glDrawBuffers(colorAttCount, bufs.constData());
}
} else {
f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
+ if (cmd.args.bindFramebuffer.stereo && cmd.args.bindFramebuffer.stereoTarget == QRhiSwapChain::RightBuffer)
+ bufs.append(GL_BACK_RIGHT);
+ else
+ bufs.append(caps.gles ? GL_BACK : GL_BACK_LEFT);
}
- if (caps.srgbCapableDefaultFramebuffer) {
+ if (caps.hasDrawBuffersFunc)
+ f->glDrawBuffers(bufs.count(), bufs.constData());
+ if (caps.srgbWriteControl) {
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);
@@ -3013,8 +3332,10 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
f->glDepthMask(GL_TRUE);
f->glClearDepthf(cmd.args.clear.d);
}
- if (cmd.args.clear.mask & GL_STENCIL_BUFFER_BIT)
+ if (cmd.args.clear.mask & GL_STENCIL_BUFFER_BIT) {
+ f->glStencilMask(0xFF);
f->glClearStencil(GLint(cmd.args.clear.s));
+ }
f->glClear(cmd.args.clear.mask);
cbD->graphicsPassState.reset(); // altered depth/color write, invalidate in order to avoid confusing the state tracking
break;
@@ -3025,7 +3346,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
break;
case QGles2CommandBuffer::Command::GetBufferSubData:
{
- QRhiBufferReadbackResult *result = cmd.args.getBufferSubData.result;
+ QRhiReadbackResult *result = cmd.args.getBufferSubData.result;
bindVertexIndexBufferWithStateReset(&state, f, cmd.args.getBufferSubData.target, cmd.args.getBufferSubData.buffer);
if (caps.gles) {
if (caps.properMapBuffer) {
@@ -3055,9 +3376,15 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
GLuint fbo;
f->glGenFramebuffers(1, &fbo);
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- if (cmd.args.copyTex.srcTarget == GL_TEXTURE_3D || cmd.args.copyTex.srcTarget == GL_TEXTURE_2D_ARRAY) {
+ if (cmd.args.copyTex.srcTarget == GL_TEXTURE_3D
+ || cmd.args.copyTex.srcTarget == GL_TEXTURE_2D_ARRAY
+ || cmd.args.copyTex.srcTarget == GL_TEXTURE_1D_ARRAY) {
f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.copyTex.srcTexture,
cmd.args.copyTex.srcLevel, cmd.args.copyTex.srcZ);
+ } else if (cmd.args.copyTex.srcTarget == GL_TEXTURE_1D) {
+ glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ cmd.args.copyTex.srcTarget, cmd.args.copyTex.srcTexture,
+ cmd.args.copyTex.srcLevel);
} else {
f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel);
@@ -3068,6 +3395,10 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
cmd.args.copyTex.dstX, cmd.args.copyTex.dstY, cmd.args.copyTex.dstZ,
cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
cmd.args.copyTex.w, cmd.args.copyTex.h);
+ } else if (cmd.args.copyTex.dstTarget == GL_TEXTURE_1D) {
+ glCopyTexSubImage1D(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstLevel,
+ cmd.args.copyTex.dstX, cmd.args.copyTex.srcX,
+ cmd.args.copyTex.srcY, cmd.args.copyTex.w);
} else {
f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel,
cmd.args.copyTex.dstX, cmd.args.copyTex.dstY,
@@ -3094,6 +3425,9 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
if (cmd.args.readPixels.slice3D >= 0) {
f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
tex, mipLevel, cmd.args.readPixels.slice3D);
+ } else if (cmd.args.readPixels.readTarget == GL_TEXTURE_1D) {
+ glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ cmd.args.readPixels.readTarget, tex, mipLevel);
} else {
f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
cmd.args.readPixels.readTarget, tex, mipLevel);
@@ -3135,6 +3469,14 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
result->data.resize(w * h * 8);
f->glReadPixels(0, 0, w, h, GL_RGBA, GL_HALF_FLOAT, result->data.data());
break;
+ case QRhiTexture::R16F:
+ result->data.resize(w * h * 2);
+ f->glReadPixels(0, 0, w, h, GL_RED, GL_HALF_FLOAT, result->data.data());
+ break;
+ case QRhiTexture::R32F:
+ result->data.resize(w * h * 4);
+ f->glReadPixels(0, 0, w, h, GL_RED, GL_FLOAT, result->data.data());
+ break;
case QRhiTexture::RGBA32F:
result->data.resize(w * h * 16);
f->glReadPixels(0, 0, w, h, GL_RGBA, GL_FLOAT, result->data.data());
@@ -3173,6 +3515,11 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
cmd.args.subImage.w, cmd.args.subImage.h, 1,
cmd.args.subImage.glformat, cmd.args.subImage.gltype,
cmd.args.subImage.data);
+ } else if (cmd.args.subImage.target == GL_TEXTURE_1D) {
+ glTexSubImage1D(cmd.args.subImage.target, cmd.args.subImage.level,
+ cmd.args.subImage.dx, cmd.args.subImage.w,
+ cmd.args.subImage.glformat, cmd.args.subImage.gltype,
+ cmd.args.subImage.data);
} else {
f->glTexSubImage2D(cmd.args.subImage.faceTarget, cmd.args.subImage.level,
cmd.args.subImage.dx, cmd.args.subImage.dy,
@@ -3192,6 +3539,11 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
cmd.args.compressedImage.glintformat,
cmd.args.compressedImage.w, cmd.args.compressedImage.h, cmd.args.compressedImage.depth,
0, cmd.args.compressedImage.size, cmd.args.compressedImage.data);
+ } else if (cmd.args.compressedImage.target == GL_TEXTURE_1D) {
+ glCompressedTexImage1D(
+ cmd.args.compressedImage.target, cmd.args.compressedImage.level,
+ cmd.args.compressedImage.glintformat, cmd.args.compressedImage.w, 0,
+ cmd.args.compressedImage.size, cmd.args.compressedImage.data);
} else {
f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level,
cmd.args.compressedImage.glintformat,
@@ -3207,6 +3559,12 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h, 1,
cmd.args.compressedSubImage.glintformat,
cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
+ } else if (cmd.args.compressedImage.target == GL_TEXTURE_1D) {
+ glCompressedTexSubImage1D(
+ cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.level,
+ cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.w,
+ cmd.args.compressedSubImage.glintformat, cmd.args.compressedSubImage.size,
+ cmd.args.compressedSubImage.data);
} else {
f->glCompressedTexSubImage2D(cmd.args.compressedSubImage.faceTarget, cmd.args.compressedSubImage.level,
cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy,
@@ -3217,23 +3575,128 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
break;
case QGles2CommandBuffer::Command::BlitFromRenderbuffer:
{
+ // Altering the scissor state, so reset the stored state, although
+ // not strictly required as long as blit is done in endPass() only.
+ cbD->graphicsPassState.reset();
+ f->glDisable(GL_SCISSOR_TEST);
+ GLuint fbo[2];
+ f->glGenFramebuffers(2, fbo);
+ f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]);
+ const bool ds = cmd.args.blitFromRenderbuffer.isDepthStencil;
+ if (ds) {
+ f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
+ f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
+ } else {
+ f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
+ }
+ f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
+ if (cmd.args.blitFromRenderbuffer.target == GL_TEXTURE_3D || cmd.args.blitFromRenderbuffer.target == GL_TEXTURE_2D_ARRAY) {
+ if (ds) {
+ f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+ cmd.args.blitFromRenderbuffer.dstTexture,
+ cmd.args.blitFromRenderbuffer.dstLevel,
+ cmd.args.blitFromRenderbuffer.dstLayer);
+ f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+ cmd.args.blitFromRenderbuffer.dstTexture,
+ cmd.args.blitFromRenderbuffer.dstLevel,
+ cmd.args.blitFromRenderbuffer.dstLayer);
+ } else {
+ f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ cmd.args.blitFromRenderbuffer.dstTexture,
+ cmd.args.blitFromRenderbuffer.dstLevel,
+ cmd.args.blitFromRenderbuffer.dstLayer);
+ }
+ } else {
+ if (ds) {
+ f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromRenderbuffer.target,
+ cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
+ f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromRenderbuffer.target,
+ cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
+ } else {
+ f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRenderbuffer.target,
+ cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
+ }
+ }
+ f->glBlitFramebuffer(0, 0, cmd.args.blitFromRenderbuffer.w, cmd.args.blitFromRenderbuffer.h,
+ 0, 0, cmd.args.blitFromRenderbuffer.w, cmd.args.blitFromRenderbuffer.h,
+ ds ? GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT : GL_COLOR_BUFFER_BIT,
+ GL_NEAREST); // Qt 5 used Nearest when resolving samples, stick to that
+ f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
+ f->glDeleteFramebuffers(2, fbo);
+ }
+ break;
+ case QGles2CommandBuffer::Command::BlitFromTexture:
+ {
+ // Altering the scissor state, so reset the stored state, although
+ // not strictly required as long as blit is done in endPass() only.
+ cbD->graphicsPassState.reset();
+ f->glDisable(GL_SCISSOR_TEST);
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);
+ const bool ds = cmd.args.blitFromTexture.isDepthStencil;
+ if (cmd.args.blitFromTexture.srcTarget == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) {
+ if (ds) {
+ f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+ cmd.args.blitFromTexture.srcTexture,
+ cmd.args.blitFromTexture.srcLevel,
+ cmd.args.blitFromTexture.srcLayer);
+ f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+ cmd.args.blitFromTexture.srcTexture,
+ cmd.args.blitFromTexture.srcLevel,
+ cmd.args.blitFromTexture.srcLayer);
+ } else {
+ f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ cmd.args.blitFromTexture.srcTexture,
+ cmd.args.blitFromTexture.srcLevel,
+ cmd.args.blitFromTexture.srcLayer);
+ }
+ } else {
+ if (ds) {
+ f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromTexture.srcTarget,
+ cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
+ f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromTexture.srcTarget,
+ cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
+ } else {
+ f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.srcTarget,
+ cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
+ }
+ }
f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
- if (cmd.args.blitFromRb.target == GL_TEXTURE_3D || cmd.args.blitFromRb.target == GL_TEXTURE_2D_ARRAY) {
- f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- cmd.args.blitFromRb.texture, cmd.args.blitFromRb.dstLevel, cmd.args.blitFromRb.dstLayer);
+ if (cmd.args.blitFromTexture.dstTarget == GL_TEXTURE_3D || cmd.args.blitFromTexture.dstTarget == GL_TEXTURE_2D_ARRAY) {
+ if (ds) {
+ f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+ cmd.args.blitFromTexture.dstTexture,
+ cmd.args.blitFromTexture.dstLevel,
+ cmd.args.blitFromTexture.dstLayer);
+ f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+ cmd.args.blitFromTexture.dstTexture,
+ cmd.args.blitFromTexture.dstLevel,
+ cmd.args.blitFromTexture.dstLayer);
+ } else {
+ f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ cmd.args.blitFromTexture.dstTexture,
+ cmd.args.blitFromTexture.dstLevel,
+ cmd.args.blitFromTexture.dstLayer);
+ }
} else {
- f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRb.target,
- cmd.args.blitFromRb.texture, cmd.args.blitFromRb.dstLevel);
+ if (ds) {
+ f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromTexture.dstTarget,
+ cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
+ f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromTexture.dstTarget,
+ cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
+ } else {
+ f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.dstTarget,
+ cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.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->glBlitFramebuffer(0, 0, cmd.args.blitFromTexture.w, cmd.args.blitFromTexture.h,
+ 0, 0, cmd.args.blitFromTexture.w, cmd.args.blitFromTexture.h,
+ ds ? GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT : GL_COLOR_BUFFER_BIT,
+ GL_NEAREST); // Qt 5 used Nearest when resolving samples, stick to that
f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
f->glDeleteFramebuffers(2, fbo);
}
@@ -3282,6 +3745,13 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
if (caps.compute)
f->glMemoryBarrier(cmd.args.barrier.barriers);
break;
+ case QGles2CommandBuffer::Command::InvalidateFramebuffer:
+ if (caps.gles && caps.ctxMajor >= 3) {
+ f->glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER,
+ cmd.args.invalidateFramebuffer.attCount,
+ cmd.args.invalidateFramebuffer.att);
+ }
+ break;
default:
break;
}
@@ -3487,9 +3957,10 @@ void QRhiGles2::executeBindGraphicsPipeline(QGles2CommandBuffer *cbD, QGles2Grap
f->glUseProgram(psD->program);
}
-static inline void qrhi_std140_to_packed(float *dst, int vecSize, int elemCount, const void *src)
+template <typename T>
+static inline void qrhi_std140_to_packed(T *dst, int vecSize, int elemCount, const void *src)
{
- const float *p = reinterpret_cast<const float *>(src);
+ const T *p = reinterpret_cast<const T *>(src);
for (int i = 0; i < elemCount; ++i) {
for (int j = 0; j < vecSize; ++j)
dst[vecSize * i + j] = *p++;
@@ -3558,7 +4029,11 @@ void QRhiGles2::bindShaderResources(QGles2CommandBuffer *cbD,
QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, srb);
int texUnit = 1; // start from unit 1, keep 0 for resource mgmt stuff to avoid clashes
bool activeTexUnitAltered = false;
- QVarLengthArray<float, 256> packedFloatArray;
+ union data32_t {
+ float f;
+ qint32 i;
+ };
+ QVarLengthArray<data32_t, 256> packedArray;
QGles2UniformDescriptionVector &uniforms(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->uniforms
: QRHI_RES(QGles2ComputePipeline, maybeComputePs)->uniforms);
QGles2UniformState *uniformState = maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->uniformState
@@ -3575,8 +4050,8 @@ void QRhiGles2::bindShaderResources(QGles2CommandBuffer *cbD,
};
QVarLengthArray<SeparateSampler, 4> separateSamplerBindings;
- for (int i = 0, ie = srbD->m_bindings.count(); i != ie; ++i) {
- const QRhiShaderResourceBinding::Data *b = srbD->m_bindings.at(i).data();
+ for (int i = 0, ie = srbD->m_bindings.size(); i != ie; ++i) {
+ const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
switch (b->type) {
case QRhiShaderResourceBinding::UniformBuffer:
@@ -3590,7 +4065,7 @@ void QRhiGles2::bindShaderResources(QGles2CommandBuffer *cbD,
}
QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.ubuf.buf);
const char *bufView = bufD->data.constData() + viewOffset;
- for (const QGles2UniformDescription &uniform : qAsConst(uniforms)) {
+ for (const QGles2UniformDescription &uniform : std::as_const(uniforms)) {
if (uniform.binding == b->binding) {
// in a uniform buffer everything is at least 4 byte aligned
// so this should not cause unaligned reads
@@ -3602,11 +4077,16 @@ void QRhiGles2::bindShaderResources(QGles2CommandBuffer *cbD,
&& uniform.type != QShaderDescription::Vec2
&& uniform.type != QShaderDescription::Vec3
&& uniform.type != QShaderDescription::Vec4
+ && uniform.type != QShaderDescription::Int
+ && uniform.type != QShaderDescription::Int2
+ && uniform.type != QShaderDescription::Int3
+ && uniform.type != QShaderDescription::Int4
&& uniform.type != QShaderDescription::Mat3
&& uniform.type != QShaderDescription::Mat4)
{
qWarning("Uniform with buffer binding %d, buffer offset %d, type %d is an array, "
- "but arrays are only supported for float, vec2, vec3, vec4, mat3 and mat4. "
+ "but arrays are only supported for float, vec2, vec3, vec4, int, "
+ "ivec2, ivec3, ivec4, mat3 and mat4. "
"Only the first element will be set.",
uniform.binding, uniform.offset, uniform.type);
}
@@ -3636,9 +4116,9 @@ void QRhiGles2::bindShaderResources(QGles2CommandBuffer *cbD,
}
} else {
// input is 16 bytes per element as per std140, have to convert to packed
- packedFloatArray.resize(elemCount);
- qrhi_std140_to_packed(packedFloatArray.data(), 1, elemCount, src);
- f->glUniform1fv(uniform.glslLocation, elemCount, packedFloatArray.constData());
+ packedArray.resize(elemCount);
+ qrhi_std140_to_packed(&packedArray.data()->f, 1, elemCount, src);
+ f->glUniform1fv(uniform.glslLocation, elemCount, &packedArray.constData()->f);
}
}
break;
@@ -3662,9 +4142,9 @@ void QRhiGles2::bindShaderResources(QGles2CommandBuffer *cbD,
f->glUniform2fv(uniform.glslLocation, 1, v);
}
} else {
- packedFloatArray.resize(elemCount * 2);
- qrhi_std140_to_packed(packedFloatArray.data(), 2, elemCount, src);
- f->glUniform2fv(uniform.glslLocation, elemCount, packedFloatArray.constData());
+ packedArray.resize(elemCount * 2);
+ qrhi_std140_to_packed(&packedArray.data()->f, 2, elemCount, src);
+ f->glUniform2fv(uniform.glslLocation, elemCount, &packedArray.constData()->f);
}
}
break;
@@ -3690,9 +4170,9 @@ void QRhiGles2::bindShaderResources(QGles2CommandBuffer *cbD,
f->glUniform3fv(uniform.glslLocation, 1, v);
}
} else {
- packedFloatArray.resize(elemCount * 3);
- qrhi_std140_to_packed(packedFloatArray.data(), 3, elemCount, src);
- f->glUniform3fv(uniform.glslLocation, elemCount, packedFloatArray.constData());
+ packedArray.resize(elemCount * 3);
+ qrhi_std140_to_packed(&packedArray.data()->f, 3, elemCount, src);
+ f->glUniform3fv(uniform.glslLocation, elemCount, &packedArray.constData()->f);
}
}
break;
@@ -3720,7 +4200,7 @@ void QRhiGles2::bindShaderResources(QGles2CommandBuffer *cbD,
f->glUniform4fv(uniform.glslLocation, 1, v);
}
} else {
- f->glUniform4fv(uniform.glslLocation, qMax(1, uniform.arrayDim), reinterpret_cast<const float *>(src));
+ f->glUniform4fv(uniform.glslLocation, elemCount, reinterpret_cast<const float *>(src));
}
}
break;
@@ -3739,9 +4219,9 @@ void QRhiGles2::bindShaderResources(QGles2CommandBuffer *cbD,
memcpy(mat + 6, srcMat + 8, 3 * sizeof(float));
f->glUniformMatrix3fv(uniform.glslLocation, 1, GL_FALSE, mat);
} else {
- packedFloatArray.resize(elemCount * 9);
- qrhi_std140_to_packed(packedFloatArray.data(), 3, elemCount * 3, src);
- f->glUniformMatrix3fv(uniform.glslLocation, elemCount, GL_FALSE, packedFloatArray.constData());
+ packedArray.resize(elemCount * 9);
+ qrhi_std140_to_packed(&packedArray.data()->f, 3, elemCount * 3, src);
+ f->glUniformMatrix3fv(uniform.glslLocation, elemCount, GL_FALSE, &packedArray.constData()->f);
}
}
break;
@@ -3749,16 +4229,43 @@ void QRhiGles2::bindShaderResources(QGles2CommandBuffer *cbD,
f->glUniformMatrix4fv(uniform.glslLocation, qMax(1, uniform.arrayDim), GL_FALSE, reinterpret_cast<const float *>(src));
break;
case QShaderDescription::Int:
- f->glUniform1i(uniform.glslLocation, *reinterpret_cast<const qint32 *>(src));
+ {
+ const int elemCount = uniform.arrayDim;
+ if (elemCount < 1) {
+ f->glUniform1i(uniform.glslLocation, *reinterpret_cast<const qint32 *>(src));
+ } else {
+ packedArray.resize(elemCount);
+ qrhi_std140_to_packed(&packedArray.data()->i, 1, elemCount, src);
+ f->glUniform1iv(uniform.glslLocation, elemCount, &packedArray.constData()->i);
+ }
+ }
break;
case QShaderDescription::Int2:
- f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
+ {
+ const int elemCount = uniform.arrayDim;
+ if (elemCount < 1) {
+ f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
+ } else {
+ packedArray.resize(elemCount * 2);
+ qrhi_std140_to_packed(&packedArray.data()->i, 2, elemCount, src);
+ f->glUniform2iv(uniform.glslLocation, elemCount, &packedArray.constData()->i);
+ }
+ }
break;
case QShaderDescription::Int3:
- f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
+ {
+ const int elemCount = uniform.arrayDim;
+ if (elemCount < 1) {
+ f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
+ } else {
+ packedArray.resize(elemCount * 3);
+ qrhi_std140_to_packed(&packedArray.data()->i, 3, elemCount, src);
+ f->glUniform3iv(uniform.glslLocation, elemCount, &packedArray.constData()->i);
+ }
+ }
break;
case QShaderDescription::Int4:
- f->glUniform4iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
+ f->glUniform4iv(uniform.glslLocation, qMax(1, uniform.arrayDim), reinterpret_cast<const qint32 *>(src));
break;
case QShaderDescription::Uint:
f->glUniform1ui(uniform.glslLocation, *reinterpret_cast<const quint32 *>(src));
@@ -3928,6 +4435,9 @@ QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt,
*wantsDsClear = doClearBuffers;
fbCmd.args.bindFramebuffer.fbo = 0;
fbCmd.args.bindFramebuffer.colorAttCount = 1;
+ fbCmd.args.bindFramebuffer.stereo = rtD->stereoTarget.has_value();
+ if (fbCmd.args.bindFramebuffer.stereo)
+ fbCmd.args.bindFramebuffer.stereoTarget = rtD->stereoTarget.value();
break;
case QRhiResource::TextureRenderTarget:
{
@@ -3939,6 +4449,7 @@ QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt,
*wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
fbCmd.args.bindFramebuffer.fbo = rtTex->framebuffer;
fbCmd.args.bindFramebuffer.colorAttCount = rtD->colorAttCount;
+ fbCmd.args.bindFramebuffer.stereo = false;
for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
it != itEnd; ++it)
@@ -3979,7 +4490,7 @@ QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt,
void QRhiGles2::enqueueBarriersForPass(QGles2CommandBuffer *cbD)
{
cbD->passResTrackers.append(QRhiPassResourceTracker());
- cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
+ cbD->currentPassResTrackerIndex = cbD->passResTrackers.size() - 1;
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QGles2CommandBuffer::Command::BarriersForPass;
cmd.args.barriersForPass.trackerIndex = cbD->currentPassResTrackerIndex;
@@ -4039,32 +4550,129 @@ void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource
if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
QGles2TextureRenderTarget *rtTex = QRHI_RES(QGles2TextureRenderTarget, cbD->currentTarget);
- if (rtTex->m_desc.cbeginColorAttachments() != rtTex->m_desc.cendColorAttachments()) {
- // handle only 1 color attachment and only (msaa) renderbuffer
- const QRhiColorAttachment &colorAtt(*rtTex->m_desc.cbeginColorAttachments());
- if (colorAtt.resolveTexture()) {
- Q_ASSERT(colorAtt.renderBuffer());
+ for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
+ it != itEnd; ++it)
+ {
+ const QRhiColorAttachment &colorAtt(*it);
+ if (!colorAtt.resolveTexture())
+ continue;
+
+ QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
+ const QSize size = resolveTexD->pixelSize();
+ if (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(cbD->commands.get());
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());
- if (colorTexD->m_flags.testFlag(QRhiTexture::CubeMap))
- cmd.args.blitFromRb.target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(colorAtt.resolveLayer());
+ cmd.args.blitFromRenderbuffer.renderbuffer = rbD->renderbuffer;
+ cmd.args.blitFromRenderbuffer.w = size.width();
+ cmd.args.blitFromRenderbuffer.h = size.height();
+ if (resolveTexD->m_flags.testFlag(QRhiTexture::CubeMap))
+ cmd.args.blitFromRenderbuffer.target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(colorAtt.resolveLayer());
else
- cmd.args.blitFromRb.target = colorTexD->target;
- cmd.args.blitFromRb.texture = colorTexD->texture;
- cmd.args.blitFromRb.dstLevel = colorAtt.resolveLevel();
- const bool hasZ = colorTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional)
- || colorTexD->m_flags.testFlag(QRhiTexture::TextureArray);
- cmd.args.blitFromRb.dstLayer = hasZ ? colorAtt.resolveLayer() : 0;
+ cmd.args.blitFromRenderbuffer.target = resolveTexD->target;
+ cmd.args.blitFromRenderbuffer.dstTexture = resolveTexD->texture;
+ cmd.args.blitFromRenderbuffer.dstLevel = colorAtt.resolveLevel();
+ const bool hasZ = resolveTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional)
+ || resolveTexD->m_flags.testFlag(QRhiTexture::TextureArray);
+ cmd.args.blitFromRenderbuffer.dstLayer = hasZ ? colorAtt.resolveLayer() : 0;
+ cmd.args.blitFromRenderbuffer.isDepthStencil = false;
+ } else if (caps.glesMultisampleRenderToTexture) {
+ // Nothing to do, resolving into colorAtt.resolveTexture() is automatic,
+ // colorAtt.texture() is in fact not used for anything.
+ } else {
+ Q_ASSERT(colorAtt.texture());
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, colorAtt.texture());
+ if (texD->pixelSize() != size) {
+ qWarning("Resolve source (%dx%d) and target (%dx%d) size does not match",
+ texD->pixelSize().width(), texD->pixelSize().height(), size.width(), size.height());
+ }
+ const int resolveCount = colorAtt.multiViewCount() >= 2 ? colorAtt.multiViewCount() : 1;
+ for (int resolveIdx = 0; resolveIdx < resolveCount; ++resolveIdx) {
+ const int srcLayer = colorAtt.layer() + resolveIdx;
+ const int dstLayer = colorAtt.resolveLayer() + resolveIdx;
+ QGles2CommandBuffer::Command &cmd(cbD->commands.get());
+ cmd.cmd = QGles2CommandBuffer::Command::BlitFromTexture;
+ if (texD->m_flags.testFlag(QRhiTexture::CubeMap))
+ cmd.args.blitFromTexture.srcTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(srcLayer);
+ else
+ cmd.args.blitFromTexture.srcTarget = texD->target;
+ cmd.args.blitFromTexture.srcTexture = texD->texture;
+ cmd.args.blitFromTexture.srcLevel = colorAtt.level();
+ cmd.args.blitFromTexture.srcLayer = 0;
+ if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || texD->m_flags.testFlag(QRhiTexture::TextureArray))
+ cmd.args.blitFromTexture.srcLayer = srcLayer;
+ cmd.args.blitFromTexture.w = size.width();
+ cmd.args.blitFromTexture.h = size.height();
+ if (resolveTexD->m_flags.testFlag(QRhiTexture::CubeMap))
+ cmd.args.blitFromTexture.dstTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(dstLayer);
+ else
+ cmd.args.blitFromTexture.dstTarget = resolveTexD->target;
+ cmd.args.blitFromTexture.dstTexture = resolveTexD->texture;
+ cmd.args.blitFromTexture.dstLevel = colorAtt.resolveLevel();
+ cmd.args.blitFromTexture.dstLayer = 0;
+ if (resolveTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || resolveTexD->m_flags.testFlag(QRhiTexture::TextureArray))
+ cmd.args.blitFromTexture.dstLayer = dstLayer;
+ cmd.args.blitFromTexture.isDepthStencil = false;
+ }
+ }
+ }
+
+ if (rtTex->m_desc.depthResolveTexture()) {
+ QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, rtTex->m_desc.depthResolveTexture());
+ const QSize size = depthResolveTexD->pixelSize();
+ if (rtTex->m_desc.depthStencilBuffer()) {
+ QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, rtTex->m_desc.depthStencilBuffer());
+ QGles2CommandBuffer::Command &cmd(cbD->commands.get());
+ cmd.cmd = QGles2CommandBuffer::Command::BlitFromRenderbuffer;
+ cmd.args.blitFromRenderbuffer.renderbuffer = rbD->renderbuffer;
+ cmd.args.blitFromRenderbuffer.w = size.width();
+ cmd.args.blitFromRenderbuffer.h = size.height();
+ cmd.args.blitFromRenderbuffer.target = depthResolveTexD->target;
+ cmd.args.blitFromRenderbuffer.dstTexture = depthResolveTexD->texture;
+ cmd.args.blitFromRenderbuffer.dstLevel = 0;
+ cmd.args.blitFromRenderbuffer.dstLayer = 0;
+ cmd.args.blitFromRenderbuffer.isDepthStencil = true;
+ } else if (caps.glesMultisampleRenderToTexture) {
+ // Nothing to do, resolving into depthResolveTexture() is automatic.
+ } else {
+ QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, rtTex->m_desc.depthTexture());
+ const int resolveCount = depthTexD->arraySize() >= 2 ? depthTexD->arraySize() : 1;
+ for (int resolveIdx = 0; resolveIdx < resolveCount; ++resolveIdx) {
+ QGles2CommandBuffer::Command &cmd(cbD->commands.get());
+ cmd.cmd = QGles2CommandBuffer::Command::BlitFromTexture;
+ cmd.args.blitFromTexture.srcTarget = depthTexD->target;
+ cmd.args.blitFromTexture.srcTexture = depthTexD->texture;
+ cmd.args.blitFromTexture.srcLevel = 0;
+ cmd.args.blitFromTexture.srcLayer = resolveIdx;
+ cmd.args.blitFromTexture.w = size.width();
+ cmd.args.blitFromTexture.h = size.height();
+ cmd.args.blitFromTexture.dstTarget = depthResolveTexD->target;
+ cmd.args.blitFromTexture.dstTexture = depthResolveTexD->texture;
+ cmd.args.blitFromTexture.dstLevel = 0;
+ cmd.args.blitFromTexture.dstLayer = resolveIdx;
+ cmd.args.blitFromTexture.isDepthStencil = true;
+ }
+ }
+ }
+
+ const bool mayDiscardDepthStencil =
+ (rtTex->m_desc.depthStencilBuffer()
+ || (rtTex->m_desc.depthTexture() && rtTex->m_flags.testFlag(QRhiTextureRenderTarget::DoNotStoreDepthStencilContents)))
+ && !rtTex->m_desc.depthResolveTexture();
+ if (mayDiscardDepthStencil) {
+ QGles2CommandBuffer::Command &cmd(cbD->commands.get());
+ cmd.cmd = QGles2CommandBuffer::Command::InvalidateFramebuffer;
+ if (caps.needsDepthStencilCombinedAttach) {
+ cmd.args.invalidateFramebuffer.attCount = 1;
+ cmd.args.invalidateFramebuffer.att[0] = GL_DEPTH_STENCIL_ATTACHMENT;
+ } else {
+ cmd.args.invalidateFramebuffer.attCount = 2;
+ cmd.args.invalidateFramebuffer.att[0] = GL_DEPTH_ATTACHMENT;
+ cmd.args.invalidateFramebuffer.att[1] = GL_STENCIL_ATTACHMENT;
}
}
}
@@ -4157,9 +4765,9 @@ void QRhiGles2::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
accessAndIsNewFlag = { 0, false };
QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, cbD->currentComputeSrb);
- const int bindingCount = srbD->m_bindings.count();
+ const int bindingCount = srbD->m_bindings.size();
for (int i = 0; i < bindingCount; ++i) {
- const QRhiShaderResourceBinding::Data *b = srbD->m_bindings.at(i).data();
+ const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
switch (b->type) {
case QRhiShaderResourceBinding::ImageLoad:
case QRhiShaderResourceBinding::ImageStore:
@@ -4234,8 +4842,7 @@ static inline GLenum toGlShaderType(QRhiShaderStage::Type type)
case QRhiShaderStage::Compute:
return GL_COMPUTE_SHADER;
default:
- Q_UNREACHABLE();
- return GL_VERTEX_SHADER;
+ Q_UNREACHABLE_RETURN(GL_VERTEX_SHADER);
}
}
@@ -4318,7 +4925,7 @@ bool QRhiGles2::compileShader(GLuint program, const QRhiShaderStage &shaderStage
} else {
shader = f->glCreateShader(toGlShaderType(shaderStage.type()));
const char *srcStr = source.constData();
- const GLint srcLength = source.length();
+ const GLint srcLength = source.size();
f->glShaderSource(shader, 1, &srcStr, &srcLength);
f->glCompileShader(shader);
GLint compiled = 0;
@@ -4335,7 +4942,7 @@ bool QRhiGles2::compileShader(GLuint program, const QRhiShaderStage &shaderStage
qWarning("Failed to compile shader: %s\nSource was:\n%s", log.constData(), source.constData());
return false;
}
- if (m_shaderCache.count() >= MAX_SHADER_CACHE_ENTRIES) {
+ if (m_shaderCache.size() >= MAX_SHADER_CACHE_ENTRIES) {
// Use the simplest strategy: too many cached shaders -> drop them all.
for (uint shader : m_shaderCache)
f->glDeleteShader(shader); // does not actually get released yet when attached to a not-yet-released program
@@ -4392,7 +4999,7 @@ void QRhiGles2::registerUniformIfActive(const QShaderDescription::BlockVariable
// unnecessary glUniform* calls then.
uniform.glslLocation = f->glGetUniformLocation(program, name.constData());
if (uniform.glslLocation >= 0 && !activeUniformLocations->hasSeen(uniform.glslLocation)) {
- if (var.arrayDims.count() > 1) {
+ if (var.arrayDims.size() > 1) {
qWarning("Array '%s' has more than one dimension. This is not supported.",
var.name.constData());
return;
@@ -4421,7 +5028,7 @@ void QRhiGles2::gatherUniforms(GLuint program,
registerUniformIfActive(structMember, structPrefix + ".", ub.binding,
baseOffset, program, activeUniformLocations, dst);
} else {
- if (blockMember.arrayDims.count() > 1) {
+ if (blockMember.arrayDims.size() > 1) {
qWarning("Array of struct '%s' has more than one dimension. Only the first "
"dimension is used.",
blockMember.name.constData());
@@ -4517,8 +5124,7 @@ static inline QShader::Stage toShaderStage(QRhiShaderStage::Type type)
case QRhiShaderStage::Compute:
return QShader::ComputeStage;
default:
- Q_UNREACHABLE();
- return QShader::VertexStage;
+ Q_UNREACHABLE_RETURN(QShader::VertexStage);
}
}
@@ -4641,7 +5247,7 @@ void QRhiGles2::trySaveToPipelineCache(GLuint program, const QByteArray &cacheKe
}
}
-QGles2Buffer::QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
+QGles2Buffer::QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
: QRhiBuffer(rhi, type, usage, size)
{
}
@@ -4701,6 +5307,9 @@ bool QGles2Buffer::create()
rhiD->f->glBindBuffer(targetForDataOps, buffer);
rhiD->f->glBufferData(targetForDataOps, nonZeroSize, nullptr, m_type == Dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
+ if (rhiD->glObjectLabel)
+ rhiD->glObjectLabel(GL_BUFFER, buffer, -1, m_objectName.constData());
+
usageState.access = AccessNone;
rhiD->registerResource(this);
@@ -4855,6 +5464,9 @@ bool QGles2RenderBuffer::create()
break;
}
+ if (rhiD->glObjectLabel)
+ rhiD->glObjectLabel(GL_RENDERBUFFER, renderbuffer, -1, m_objectName.constData());
+
owns = true;
generation += 1;
rhiD->registerResource(this);
@@ -4936,13 +5548,15 @@ bool QGles2Texture::prepareCreate(QSize *adjustedSize)
if (!rhiD->ensureContext())
return false;
- const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
-
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
const bool isCompressed = rhiD->isCompressedFormat(m_format);
+ const bool is1D = m_flags.testFlag(OneDimensional);
+
+ const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
+ : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
if (is3D && !rhiD->caps.texture3D) {
qWarning("3D textures are not supported");
@@ -4956,12 +5570,23 @@ bool QGles2Texture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both array and 3D");
return false;
}
- m_depth = qMax(1, m_depth);
+ if (is1D && !rhiD->caps.texture1D) {
+ qWarning("1D textures are not supported");
+ return false;
+ }
+ if (is1D && is3D) {
+ qWarning("Texture cannot be both 1D and 3D");
+ return false;
+ }
+ if (is1D && isCube) {
+ qWarning("Texture cannot be both 1D and cube");
+ return false;
+ }
+
if (m_depth > 1 && !is3D) {
qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
return false;
}
- m_arraySize = qMax(0, m_arraySize);
if (m_arraySize > 0 && !isArray) {
qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
return false;
@@ -4971,9 +5596,12 @@ bool QGles2Texture::prepareCreate(QSize *adjustedSize)
return false;
}
- target = isCube ? GL_TEXTURE_CUBE_MAP
- : m_sampleCount > 1 ? GL_TEXTURE_2D_MULTISAMPLE
- : (is3D ? GL_TEXTURE_3D : (isArray ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D));
+ target = isCube ? GL_TEXTURE_CUBE_MAP
+ : m_sampleCount > 1 ? (isArray ? GL_TEXTURE_2D_MULTISAMPLE_ARRAY : GL_TEXTURE_2D_MULTISAMPLE)
+ : (is3D ? GL_TEXTURE_3D
+ : (is1D ? (isArray ? GL_TEXTURE_1D_ARRAY : GL_TEXTURE_1D)
+ : (isArray ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D)));
+
if (m_flags.testFlag(ExternalOES))
target = GL_TEXTURE_EXTERNAL_OES;
else if (m_flags.testFlag(TextureRectangleGL))
@@ -5023,11 +5651,23 @@ bool QGles2Texture::create()
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
const bool isCompressed = rhiD->isCompressedFormat(m_format);
+ const bool is1D = m_flags.testFlag(OneDimensional);
+
if (!isCompressed) {
rhiD->f->glBindTexture(target, texture);
if (!m_flags.testFlag(UsedWithLoadStore)) {
- if (is3D || isArray) {
- const int layerCount = is3D ? m_depth : m_arraySize;
+ if (is1D) {
+ for (int level = 0; level < mipLevelCount; ++level) {
+ const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
+ if (isArray)
+ rhiD->f->glTexImage2D(target, level, GLint(glintformat), mipSize.width(),
+ qMax(0, m_arraySize), 0, glformat, gltype, nullptr);
+ else
+ rhiD->glTexImage1D(target, level, GLint(glintformat), mipSize.width(), 0,
+ glformat, gltype, nullptr);
+ }
+ } else if (is3D || isArray) {
+ const int layerCount = is3D ? qMax(1, m_depth) : qMax(0, m_arraySize);
if (hasMipMaps) {
for (int level = 0; level != mipLevelCount; ++level) {
const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
@@ -5049,17 +5689,32 @@ bool QGles2Texture::create()
}
}
} else {
- rhiD->f->glTexImage2D(target, 0, GLint(glintformat), size.width(), size.height(),
- 0, glformat, gltype, nullptr);
+ // 2D texture. For multisample textures the GLES 3.1
+ // glStorage2DMultisample must be used for portability.
+ if (m_sampleCount > 1 && rhiD->caps.multisampledTexture) {
+ // internal format must be sized
+ rhiD->f->glTexStorage2DMultisample(target, m_sampleCount, glsizedintformat,
+ size.width(), size.height(), GL_TRUE);
+ } else {
+ rhiD->f->glTexImage2D(target, 0, GLint(glintformat), size.width(), size.height(),
+ 0, glformat, gltype, nullptr);
+ }
}
} else {
// Must be specified with immutable storage functions otherwise
// bindImageTexture may fail. Also, the internal format must be a
// sized format here.
- if (is3D || isArray)
- rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(), is3D ? m_depth : m_arraySize);
+ if (is1D && !isArray)
+ rhiD->glTexStorage1D(target, mipLevelCount, glsizedintformat, size.width());
+ else if (!is1D && (is3D || isArray))
+ rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(),
+ is3D ? qMax(1, m_depth) : qMax(0, m_arraySize));
+ else if (m_sampleCount > 1)
+ rhiD->f->glTexStorage2DMultisample(target, m_sampleCount, glsizedintformat,
+ size.width(), size.height(), GL_TRUE);
else
- rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(), size.height());
+ rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(),
+ is1D ? qMax(0, m_arraySize) : size.height());
}
specified = true;
} else {
@@ -5069,6 +5724,9 @@ bool QGles2Texture::create()
specified = false;
}
+ if (rhiD->glObjectLabel)
+ rhiD->glObjectLabel(GL_TEXTURE, texture, -1, m_objectName.constData());
+
owns = true;
generation += 1;
@@ -5115,7 +5773,9 @@ QGles2Sampler::~QGles2Sampler()
void QGles2Sampler::destroy()
{
- // nothing to do here
+ QRHI_RES_RHI(QRhiGles2);
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
bool QGles2Sampler::create()
@@ -5128,6 +5788,8 @@ bool QGles2Sampler::create()
d.gltexcomparefunc = toGlTextureCompareFunc(m_compareOp);
generation += 1;
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->registerResource(this, false);
return true;
}
@@ -5144,7 +5806,9 @@ QGles2RenderPassDescriptor::~QGles2RenderPassDescriptor()
void QGles2RenderPassDescriptor::destroy()
{
- // nothing to do here
+ QRHI_RES_RHI(QRhiGles2);
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
bool QGles2RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
@@ -5155,7 +5819,10 @@ bool QGles2RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *ot
QRhiRenderPassDescriptor *QGles2RenderPassDescriptor::newCompatibleRenderPassDescriptor() const
{
- return new QGles2RenderPassDescriptor(m_rhi);
+ QGles2RenderPassDescriptor *rpD = new QGles2RenderPassDescriptor(m_rhi);
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->registerResource(rpD, false);
+ return rpD;
}
QVector<quint32> QGles2RenderPassDescriptor::serializedFormat() const
@@ -5216,8 +5883,10 @@ void QGles2TextureRenderTarget::destroy()
e.type = QRhiGles2::DeferredReleaseEntry::TextureRenderTarget;
e.textureRenderTarget.framebuffer = framebuffer;
+ e.textureRenderTarget.nonMsaaThrowawayDepthTexture = nonMsaaThrowawayDepthTexture;
framebuffer = 0;
+ nonMsaaThrowawayDepthTexture = 0;
QRHI_RES_RHI(QRhiGles2);
if (rhiD) {
@@ -5228,7 +5897,10 @@ void QGles2TextureRenderTarget::destroy()
QRhiRenderPassDescriptor *QGles2TextureRenderTarget::newCompatibleRenderPassDescriptor()
{
- return new QGles2RenderPassDescriptor(m_rhi);
+ QGles2RenderPassDescriptor *rpD = new QGles2RenderPassDescriptor(m_rhi);
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->registerResource(rpD, false);
+ return rpD;
}
bool QGles2TextureRenderTarget::create()
@@ -5238,13 +5910,13 @@ bool QGles2TextureRenderTarget::create()
if (framebuffer)
destroy();
- const bool hasColorAttachments = m_desc.cbeginColorAttachments() != m_desc.cendColorAttachments();
+ const bool hasColorAttachments = m_desc.colorAttachmentCount() > 0;
Q_ASSERT(hasColorAttachments || m_desc.depthTexture());
Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
if (hasColorAttachments) {
- const int count = m_desc.cendColorAttachments() - m_desc.cbeginColorAttachments();
+ const int count = int(m_desc.colorAttachmentCount());
if (count > rhiD->caps.maxDrawBuffers) {
qWarning("QGles2TextureRenderTarget: Too many color attachments (%d, max is %d)",
count, rhiD->caps.maxDrawBuffers);
@@ -5261,6 +5933,7 @@ bool QGles2TextureRenderTarget::create()
d.colorAttCount = 0;
int attIndex = 0;
+ int multiViewCount = 0;
for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
d.colorAttCount += 1;
const QRhiColorAttachment &colorAtt(*it);
@@ -5271,16 +5944,56 @@ bool QGles2TextureRenderTarget::create()
QGles2Texture *texD = QRHI_RES(QGles2Texture, texture);
Q_ASSERT(texD->texture && texD->specified);
if (texD->flags().testFlag(QRhiTexture::ThreeDimensional) || texD->flags().testFlag(QRhiTexture::TextureArray)) {
- rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture,
- colorAtt.level(), colorAtt.layer());
+ if (colorAtt.multiViewCount() < 2) {
+ rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture,
+ colorAtt.level(), colorAtt.layer());
+ } else {
+ multiViewCount = colorAtt.multiViewCount();
+ if (texD->sampleCount() > 1 && rhiD->caps.glesMultiviewMultisampleRenderToTexture && colorAtt.resolveTexture()) {
+ // Special path for GLES and GL_OVR_multiview_multisampled_render_to_texture:
+ // ignore the color attachment's (multisample) texture
+ // array and give the resolve texture array to GL. (no
+ // explicit resolving is needed by us later on)
+ QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
+ rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0 + uint(attIndex),
+ resolveTexD->texture,
+ colorAtt.resolveLevel(),
+ texD->sampleCount(),
+ colorAtt.resolveLayer(),
+ multiViewCount);
+ } else {
+ rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0 + uint(attIndex),
+ texD->texture,
+ colorAtt.level(),
+ colorAtt.layer(),
+ multiViewCount);
+ }
+ }
+ } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) {
+ rhiD->glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex),
+ texD->target + uint(colorAtt.layer()), texD->texture,
+ colorAtt.level());
} else {
- const GLenum faceTargetBase = texD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
- rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.layer()),
- texD->texture, colorAtt.level());
+ if (texD->sampleCount() > 1 && rhiD->caps.glesMultisampleRenderToTexture && colorAtt.resolveTexture()) {
+ // Special path for GLES and GL_EXT_multisampled_render_to_texture:
+ // ignore the color attachment's (multisample) texture and
+ // give the resolve texture to GL. (no explicit resolving is
+ // needed by us later on)
+ QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
+ const GLenum faceTargetBase = resolveTexD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : resolveTexD->target;
+ rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.resolveLayer()),
+ resolveTexD->texture, colorAtt.level(), texD->sampleCount());
+ } else {
+ const GLenum faceTargetBase = texD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
+ rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.layer()),
+ texD->texture, colorAtt.level());
+ }
}
if (attIndex == 0) {
d.pixelSize = rhiD->q->sizeForMipLevel(colorAtt.level(), texD->pixelSize());
- d.sampleCount = 1;
+ d.sampleCount = texD->sampleCount();
}
} else if (renderBuffer) {
QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, renderBuffer);
@@ -5301,12 +6014,14 @@ bool QGles2TextureRenderTarget::create()
} else {
rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
depthRbD->renderbuffer);
- if (depthRbD->stencilRenderbuffer)
+ if (depthRbD->stencilRenderbuffer) {
rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depthRbD->stencilRenderbuffer);
- else // packed
+ } else {
+ // packed depth-stencil
rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depthRbD->renderbuffer);
+ }
}
if (d.colorAttCount == 0) {
d.pixelSize = depthRbD->pixelSize();
@@ -5314,11 +6029,105 @@ bool QGles2TextureRenderTarget::create()
}
} else {
QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, m_desc.depthTexture());
- rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->target,
- depthTexD->texture, 0);
+ if (multiViewCount < 2) {
+ if (depthTexD->sampleCount() > 1 && rhiD->caps.glesMultisampleRenderToTexture && m_desc.depthResolveTexture()) {
+ // Special path for GLES and
+ // GL_EXT_multisampled_render_to_texture, for depth-stencil.
+ // Relevant only when depthResolveTexture is set.
+ QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, m_desc.depthResolveTexture());
+ rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthResolveTexD->target,
+ depthResolveTexD->texture, 0, depthTexD->sampleCount());
+ if (rhiD->isStencilSupportingFormat(depthResolveTexD->format())) {
+ rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthResolveTexD->target,
+ depthResolveTexD->texture, 0, depthTexD->sampleCount());
+ }
+ } else {
+ rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->target,
+ depthTexD->texture, 0);
+ if (rhiD->isStencilSupportingFormat(depthTexD->format())) {
+ rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->target,
+ depthTexD->texture, 0);
+ }
+ }
+ } else {
+ if (depthTexD->sampleCount() > 1 && rhiD->caps.glesMultiviewMultisampleRenderToTexture) {
+ // And so it turns out
+ // https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview.txt
+ // does not work with multisample 2D texture arrays. (at least
+ // that's what Issue 30 in the extension spec seems to imply)
+ //
+ // There is https://registry.khronos.org/OpenGL/extensions/EXT/EXT_multiview_texture_multisample.txt
+ // that seems to resolve that, but that does not seem to
+ // work (or not available) on GLES devices such as the Quest 3.
+ //
+ // So instead, on GLES we can use the
+ // multisample-multiview-auto-resolving version (which in
+ // turn is not supported on desktop GL e.g. by NVIDIA), too
+ // bad we have a multisample depth texture array here as
+ // every other API out there requires that. So, in absence
+ // of a depthResolveTexture, create a temporary one ignoring
+ // what the user has already created.
+ //
+ if (!m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture()) {
+ qWarning("Attempted to create a multiview+multisample QRhiTextureRenderTarget, but DoNotStoreDepthStencilContents was not set."
+ " This path has no choice but to behave as if DoNotStoreDepthStencilContents was set, because QRhi is forced to create"
+ " a throwaway non-multisample depth texture here. Set the flag to silence this warning, or set a depthResolveTexture.");
+ }
+ if (m_desc.depthResolveTexture()) {
+ QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, m_desc.depthResolveTexture());
+ rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
+ GL_DEPTH_ATTACHMENT,
+ depthResolveTexD->texture,
+ 0,
+ depthTexD->sampleCount(),
+ 0,
+ multiViewCount);
+ if (rhiD->isStencilSupportingFormat(depthResolveTexD->format())) {
+ rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
+ GL_STENCIL_ATTACHMENT,
+ depthResolveTexD->texture,
+ 0,
+ depthTexD->sampleCount(),
+ 0,
+ multiViewCount);
+ }
+ } else {
+ if (!nonMsaaThrowawayDepthTexture) {
+ rhiD->f->glGenTextures(1, &nonMsaaThrowawayDepthTexture);
+ rhiD->f->glBindTexture(GL_TEXTURE_2D_ARRAY, nonMsaaThrowawayDepthTexture);
+ rhiD->f->glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH24_STENCIL8,
+ depthTexD->pixelSize().width(), depthTexD->pixelSize().height(), multiViewCount);
+ }
+ rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
+ GL_DEPTH_ATTACHMENT,
+ nonMsaaThrowawayDepthTexture,
+ 0,
+ depthTexD->sampleCount(),
+ 0,
+ multiViewCount);
+ rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
+ GL_STENCIL_ATTACHMENT,
+ nonMsaaThrowawayDepthTexture,
+ 0,
+ depthTexD->sampleCount(),
+ 0,
+ multiViewCount);
+ }
+ } else {
+ // The depth texture here must be an array with at least
+ // multiViewCount elements, and the format should be D24 or D32F
+ // for depth only, or D24S8 for depth and stencil.
+ rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->texture,
+ 0, 0, multiViewCount);
+ if (rhiD->isStencilSupportingFormat(depthTexD->format())) {
+ rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->texture,
+ 0, 0, multiViewCount);
+ }
+ }
+ }
if (d.colorAttCount == 0) {
d.pixelSize = depthTexD->pixelSize();
- d.sampleCount = 1;
+ d.sampleCount = depthTexD->sampleCount();
}
}
d.dsAttCount = 1;
@@ -5335,6 +6144,9 @@ bool QGles2TextureRenderTarget::create()
return false;
}
+ if (rhiD->glObjectLabel)
+ rhiD->glObjectLabel(GL_FRAMEBUFFER, framebuffer, -1, m_objectName.constData());
+
QRhiRenderTargetAttachmentTracker::updateResIdList<QGles2Texture, QGles2RenderBuffer>(m_desc, &d.currentResIdList);
rhiD->registerResource(this);
@@ -5371,7 +6183,9 @@ QGles2ShaderResourceBindings::~QGles2ShaderResourceBindings()
void QGles2ShaderResourceBindings::destroy()
{
- // nothing to do here
+ QRHI_RES_RHI(QRhiGles2);
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
bool QGles2ShaderResourceBindings::create()
@@ -5381,8 +6195,8 @@ bool QGles2ShaderResourceBindings::create()
return false;
hasDynamicOffset = false;
- for (int i = 0, ie = m_bindings.count(); i != ie; ++i) {
- const QRhiShaderResourceBinding::Data *b = m_bindings.at(i).data();
+ for (int i = 0, ie = m_bindings.size(); i != ie; ++i) {
+ const QRhiShaderResourceBinding::Data *b = QRhiImplementation::shaderResourceBindingData(m_bindings.at(i));
if (b->type == QRhiShaderResourceBinding::UniformBuffer) {
if (b->u.ubuf.hasDynamicOffset) {
hasDynamicOffset = true;
@@ -5394,6 +6208,7 @@ bool QGles2ShaderResourceBindings::create()
rhiD->updateLayoutDesc(this);
generation += 1;
+ rhiD->registerResource(this, false);
return true;
}
@@ -5454,6 +6269,7 @@ bool QGles2GraphicsPipeline::create()
if (!rhiD->ensureContext())
return false;
+ rhiD->pipelineCreationStart();
if (!rhiD->sanityCheckGraphicsPipeline(this))
return false;
@@ -5484,14 +6300,16 @@ bool QGles2GraphicsPipeline::create()
default:
break;
}
- Q_UNREACHABLE();
- return VtxIdx;
+ Q_UNREACHABLE_RETURN(VtxIdx);
};
QShaderDescription desc[LastIdx];
QShader::SeparateToCombinedImageSamplerMappingList samplerMappingList[LastIdx];
- for (const QRhiShaderStage &shaderStage : qAsConst(m_shaderStages)) {
+ bool vertexFragmentOnly = true;
+ for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
if (isGraphicsStage(shaderStage)) {
const int idx = descIdxForStage(shaderStage);
+ if (idx != VtxIdx && idx != FragIdx)
+ vertexFragmentOnly = false;
QShader shader = shaderStage.shader();
QShaderVersion shaderVersion;
desc[idx] = shader.description();
@@ -5504,7 +6322,7 @@ bool QGles2GraphicsPipeline::create()
QByteArray cacheKey;
QRhiGles2::ProgramCacheResult cacheResult = rhiD->tryLoadFromDiskOrPipelineCache(m_shaderStages.constData(),
- m_shaderStages.count(),
+ m_shaderStages.size(),
program,
desc[VtxIdx].inputVariables(),
&cacheKey);
@@ -5512,7 +6330,7 @@ bool QGles2GraphicsPipeline::create()
return false;
if (cacheResult == QRhiGles2::ProgramCacheMiss) {
- for (const QRhiShaderStage &shaderStage : qAsConst(m_shaderStages)) {
+ for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
if (isGraphicsStage(shaderStage)) {
if (!rhiD->compileShader(program, shaderStage, nullptr))
return false;
@@ -5523,7 +6341,8 @@ bool QGles2GraphicsPipeline::create()
for (const QShaderDescription::InOutVariable &inVar : desc[VtxIdx].inputVariables())
rhiD->f->glBindAttribLocation(program, GLuint(inVar.location), inVar.name);
- rhiD->sanityCheckVertexFragmentInterface(desc[VtxIdx], desc[FragIdx]);
+ if (vertexFragmentOnly)
+ rhiD->sanityCheckVertexFragmentInterface(desc[VtxIdx], desc[FragIdx]);
if (!rhiD->linkProgram(program))
return false;
@@ -5551,7 +6370,7 @@ bool QGles2GraphicsPipeline::create()
// present in both shaders.
QDuplicateTracker<int, 256> activeUniformLocations;
- for (const QRhiShaderStage &shaderStage : qAsConst(m_shaderStages)) {
+ for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
if (isGraphicsStage(shaderStage)) {
const int idx = descIdxForStage(shaderStage);
for (const QShaderDescription::UniformBlock &ub : desc[idx].uniformBlocks())
@@ -5574,6 +6393,10 @@ bool QGles2GraphicsPipeline::create()
currentSrb = nullptr;
currentSrbGeneration = 0;
+ if (rhiD->glObjectLabel)
+ rhiD->glObjectLabel(GL_PROGRAM, program, -1, m_objectName.constData());
+
+ rhiD->pipelineCreationEnd();
generation += 1;
rhiD->registerResource(this);
return true;
@@ -5620,6 +6443,8 @@ bool QGles2ComputePipeline::create()
if (!rhiD->ensureContext())
return false;
+ rhiD->pipelineCreationStart();
+
const QShaderDescription csDesc = m_shaderStage.shader().description();
QShader::SeparateToCombinedImageSamplerMappingList csSamplerMappingList;
QShaderVersion shaderVersion;
@@ -5675,6 +6500,7 @@ bool QGles2ComputePipeline::create()
currentSrb = nullptr;
currentSrbGeneration = 0;
+ rhiD->pipelineCreationEnd();
generation += 1;
rhiD->registerResource(this);
return true;
@@ -5699,6 +6525,8 @@ void QGles2CommandBuffer::destroy()
QGles2SwapChain::QGles2SwapChain(QRhiImplementation *rhi)
: QRhiSwapChain(rhi),
rt(rhi, this),
+ rtLeft(rhi, this),
+ rtRight(rhi, this),
cb(rhi)
{
}
@@ -5725,10 +6553,25 @@ QRhiRenderTarget *QGles2SwapChain::currentFrameRenderTarget()
return &rt;
}
+QRhiRenderTarget *QGles2SwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer)
+{
+ if (targetBuffer == LeftBuffer)
+ return rtLeft.d.isValid() ? &rtLeft : &rt;
+ else if (targetBuffer == RightBuffer)
+ return rtRight.d.isValid() ? &rtRight : &rt;
+ else
+ Q_UNREACHABLE_RETURN(nullptr);
+}
+
QSize QGles2SwapChain::surfacePixelSize()
{
Q_ASSERT(m_window);
- return m_window->size() * m_window->devicePixelRatio();
+ if (QPlatformWindow *platformWindow = m_window->handle())
+ // Prefer using QPlatformWindow geometry and DPR in order to avoid
+ // errors due to rounded QWindow geometry.
+ return platformWindow->geometry().size() * platformWindow->devicePixelRatio();
+ else
+ return m_window->size() * m_window->devicePixelRatio();
}
bool QGles2SwapChain::isFormatSupported(Format f)
@@ -5738,7 +6581,22 @@ bool QGles2SwapChain::isFormatSupported(Format f)
QRhiRenderPassDescriptor *QGles2SwapChain::newCompatibleRenderPassDescriptor()
{
- return new QGles2RenderPassDescriptor(m_rhi);
+ QGles2RenderPassDescriptor *rpD = new QGles2RenderPassDescriptor(m_rhi);
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->registerResource(rpD, false);
+ return rpD;
+}
+
+void QGles2SwapChain::initSwapChainRenderTarget(QGles2SwapChainRenderTarget *rt)
+{
+ rt->setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
+ rt->d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc);
+ rt->d.pixelSize = pixelSize;
+ rt->d.dpr = float(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);
}
bool QGles2SwapChain::createOrResize()
@@ -5759,26 +6617,70 @@ bool QGles2SwapChain::createOrResize()
m_depthStencil->create();
}
- rt.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
- rt.d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc);
- rt.d.pixelSize = pixelSize;
- rt.d.dpr = float(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);
+ initSwapChainRenderTarget(&rt);
+
+ if (m_window->format().stereo()) {
+ initSwapChainRenderTarget(&rtLeft);
+ rtLeft.d.stereoTarget = QRhiSwapChain::LeftBuffer;
+ initSwapChainRenderTarget(&rtRight);
+ rtRight.d.stereoTarget = QRhiSwapChain::RightBuffer;
+ }
frameCount = 0;
+ QRHI_RES_RHI(QRhiGles2);
+ if (rhiD->rhiFlags.testFlag(QRhi::EnableTimestamps) && rhiD->caps.timestamps)
+ timestamps.prepare(rhiD);
+
// The only reason to register this fairly fake gl swapchain
// object with no native resources underneath is to be able to
// implement a safe destroy().
- if (needsRegistration) {
- QRHI_RES_RHI(QRhiGles2);
- rhiD->registerResource(this);
- }
+ if (needsRegistration)
+ rhiD->registerResource(this, false);
return true;
}
+void QGles2SwapChainTimestamps::prepare(QRhiGles2 *rhiD)
+{
+ if (!query[0])
+ rhiD->f->glGenQueries(TIMESTAMP_PAIRS * 2, query);
+}
+
+void QGles2SwapChainTimestamps::destroy(QRhiGles2 *rhiD)
+{
+ rhiD->f->glDeleteQueries(TIMESTAMP_PAIRS * 2, query);
+ memset(active, 0, sizeof(active));
+ memset(query, 0, sizeof(query));
+}
+
+bool QGles2SwapChainTimestamps::tryQueryTimestamps(int pairIndex, QRhiGles2 *rhiD, double *elapsedSec)
+{
+ if (!active[pairIndex])
+ return false;
+
+ GLuint tsStart = query[pairIndex * 2];
+ GLuint tsEnd = query[pairIndex * 2 + 1];
+
+ GLuint ready = GL_FALSE;
+ rhiD->f->glGetQueryObjectuiv(tsEnd, GL_QUERY_RESULT_AVAILABLE, &ready);
+
+ if (!ready)
+ return false;
+
+ bool result = false;
+ quint64 timestamps[2];
+ rhiD->glGetQueryObjectui64v(tsStart, GL_QUERY_RESULT, &timestamps[0]);
+ rhiD->glGetQueryObjectui64v(tsEnd, GL_QUERY_RESULT, &timestamps[1]);
+
+ if (timestamps[1] >= timestamps[0]) {
+ const quint64 nanoseconds = timestamps[1] - timestamps[0];
+ *elapsedSec = nanoseconds / 1000000000.0;
+ result = true;
+ }
+
+ active[pairIndex] = false;
+ return result;
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhigles2_p.h b/src/gui/rhi/qrhigles2_p.h
index f7570ae01c..4305186c02 100644
--- a/src/gui/rhi/qrhigles2_p.h
+++ b/src/gui/rhi/qrhigles2_p.h
@@ -1,8 +1,8 @@
-// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QRHIGLES2_H
-#define QRHIGLES2_H
+#ifndef QRHIGLES2_P_H
+#define QRHIGLES2_P_H
//
// W A R N I N G
@@ -15,34 +15,1121 @@
// We mean it.
//
-#include <private/qrhi_p.h>
-#include <QtGui/qsurfaceformat.h>
+#include "qrhi_p.h"
+#include <rhi/qshaderdescription.h>
+#include <qopengl.h>
+#include <QByteArray>
+#include <QWindow>
+#include <QPointer>
+#include <QtCore/private/qduplicatetracker_p.h>
+#include <optional>
QT_BEGIN_NAMESPACE
-class QOpenGLContext;
-class QOffscreenSurface;
-class QSurface;
-class QWindow;
+class QOpenGLExtensions;
+class QRhiGles2;
-struct Q_GUI_EXPORT QRhiGles2InitParams : public QRhiInitParams
+struct QGles2Buffer : public QRhiBuffer
{
- QRhiGles2InitParams();
+ QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size);
+ ~QGles2Buffer();
+ void destroy() override;
+ bool create() override;
+ QRhiBuffer::NativeBuffer nativeBuffer() override;
+ char *beginFullDynamicBufferUpdateForCurrentFrame() override;
+ void endFullDynamicBufferUpdateForCurrentFrame() override;
- QSurfaceFormat format;
- QSurface *fallbackSurface = nullptr;
- QWindow *window = nullptr;
- QOpenGLContext *shareContext = nullptr;
+ quint32 nonZeroSize = 0;
+ GLuint buffer = 0;
+ GLenum targetForDataOps;
+ QByteArray data;
+ enum Access {
+ AccessNone,
+ AccessVertex,
+ AccessIndex,
+ AccessUniform,
+ AccessStorageRead,
+ AccessStorageWrite,
+ AccessStorageReadWrite,
+ AccessUpdate
+ };
+ struct UsageState {
+ Access access;
+ };
+ UsageState usageState;
+ friend class QRhiGles2;
+};
+
+struct QGles2RenderBuffer : public QRhiRenderBuffer
+{
+ QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags,
+ QRhiTexture::Format backingFormatHint);
+ ~QGles2RenderBuffer();
+ void destroy() override;
+ bool create() override;
+ bool createFrom(NativeRenderBuffer src) override;
+ QRhiTexture::Format backingFormat() const override;
+
+ GLuint renderbuffer = 0;
+ GLuint stencilRenderbuffer = 0; // when packed depth-stencil not supported
+ int samples;
+ bool owns = true;
+ uint generation = 0;
+ 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 depth,
+ int arraySize, int sampleCount, Flags flags);
+ ~QGles2Texture();
+ void destroy() override;
+ bool create() override;
+ bool createFrom(NativeTexture src) override;
+ NativeTexture nativeTexture() override;
+
+ bool prepareCreate(QSize *adjustedSize = nullptr);
+
+ GLuint texture = 0;
+ bool owns = true;
+ GLenum target;
+ GLenum glintformat;
+ GLenum glsizedintformat;
+ GLenum glformat;
+ GLenum gltype;
+ QGles2SamplerData samplerState;
+ bool specified = false;
+ bool zeroInitialized = false;
+ int mipLevelCount = 0;
+
+ enum Access {
+ AccessNone,
+ AccessSample,
+ AccessFramebuffer,
+ AccessStorageRead,
+ AccessStorageWrite,
+ AccessStorageReadWrite,
+ AccessUpdate,
+ AccessRead
+ };
+ struct UsageState {
+ Access access;
+ };
+ UsageState usageState;
+
+ uint generation = 0;
+ friend class QRhiGles2;
+};
+
+struct QGles2Sampler : public QRhiSampler
+{
+ QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v, AddressMode w);
+ ~QGles2Sampler();
+ void destroy() override;
+ bool create() override;
+
+ QGles2SamplerData d;
+ uint generation = 0;
+ friend class QRhiGles2;
+};
+
+struct QGles2RenderPassDescriptor : public QRhiRenderPassDescriptor
+{
+ QGles2RenderPassDescriptor(QRhiImplementation *rhi);
+ ~QGles2RenderPassDescriptor();
+ void destroy() override;
+ bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
+ QVector<quint32> serializedFormat() const override;
+};
+
+struct QGles2RenderTargetData
+{
+ QGles2RenderTargetData(QRhiImplementation *) { }
+
+ bool isValid() const { return rp != nullptr; }
+
+ QGles2RenderPassDescriptor *rp = nullptr;
+ QSize pixelSize;
+ float dpr = 1;
+ int sampleCount = 1;
+ int colorAttCount = 0;
+ int dsAttCount = 0;
+ bool srgbUpdateAndBlend = false;
+ QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList;
+ std::optional<QRhiSwapChain::StereoTargetBuffer> stereoTarget;
+};
+
+struct QGles2SwapChainRenderTarget : public QRhiSwapChainRenderTarget
+{
+ QGles2SwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain);
+ ~QGles2SwapChainRenderTarget();
+ void destroy() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
- static QOffscreenSurface *newFallbackSurface(const QSurfaceFormat &format = QSurfaceFormat::defaultFormat());
- static QSurfaceFormat adjustedFormat(const QSurfaceFormat &format = QSurfaceFormat::defaultFormat());
+ QGles2RenderTargetData d;
};
-struct Q_GUI_EXPORT QRhiGles2NativeHandles : public QRhiNativeHandles
+struct QGles2TextureRenderTarget : public QRhiTextureRenderTarget
{
- QOpenGLContext *context = nullptr;
+ QGles2TextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
+ ~QGles2TextureRenderTarget();
+ void destroy() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool create() override;
+
+ QGles2RenderTargetData d;
+ GLuint framebuffer = 0;
+ GLuint nonMsaaThrowawayDepthTexture = 0;
+ friend class QRhiGles2;
+};
+
+struct QGles2ShaderResourceBindings : public QRhiShaderResourceBindings
+{
+ QGles2ShaderResourceBindings(QRhiImplementation *rhi);
+ ~QGles2ShaderResourceBindings();
+ void destroy() override;
+ bool create() override;
+ void updateResources(UpdateFlags flags) override;
+
+ bool hasDynamicOffset = false;
+ uint generation = 0;
+ friend class QRhiGles2;
};
+struct QGles2UniformDescription
+{
+ QShaderDescription::VariableType type;
+ int glslLocation;
+ int binding;
+ quint32 offset;
+ quint32 size;
+ int arrayDim;
+};
+
+Q_DECLARE_TYPEINFO(QGles2UniformDescription, Q_RELOCATABLE_TYPE);
+
+struct QGles2SamplerDescription
+{
+ int glslLocation;
+ int combinedBinding;
+ int tbinding;
+ int sbinding;
+};
+
+Q_DECLARE_TYPEINFO(QGles2SamplerDescription, Q_RELOCATABLE_TYPE);
+
+using QGles2UniformDescriptionVector = QVarLengthArray<QGles2UniformDescription, 8>;
+using QGles2SamplerDescriptionVector = QVarLengthArray<QGles2SamplerDescription, 4>;
+
+struct QGles2UniformState
+{
+ static constexpr int MAX_TRACKED_LOCATION = 1023;
+ int componentCount;
+ float v[4];
+};
+
+struct QGles2GraphicsPipeline : public QRhiGraphicsPipeline
+{
+ QGles2GraphicsPipeline(QRhiImplementation *rhi);
+ ~QGles2GraphicsPipeline();
+ void destroy() override;
+ bool create() override;
+
+ GLuint program = 0;
+ GLenum drawMode = GL_TRIANGLES;
+ QGles2UniformDescriptionVector uniforms;
+ QGles2SamplerDescriptionVector samplers;
+ QGles2UniformState uniformState[QGles2UniformState::MAX_TRACKED_LOCATION + 1];
+ QRhiShaderResourceBindings *currentSrb = nullptr;
+ uint currentSrbGeneration = 0;
+ uint generation = 0;
+ friend class QRhiGles2;
+};
+
+struct QGles2ComputePipeline : public QRhiComputePipeline
+{
+ QGles2ComputePipeline(QRhiImplementation *rhi);
+ ~QGles2ComputePipeline();
+ void destroy() override;
+ bool create() override;
+
+ GLuint program = 0;
+ QGles2UniformDescriptionVector uniforms;
+ QGles2SamplerDescriptionVector samplers;
+ QGles2UniformState uniformState[QGles2UniformState::MAX_TRACKED_LOCATION + 1];
+ QRhiShaderResourceBindings *currentSrb = nullptr;
+ uint currentSrbGeneration = 0;
+ uint generation = 0;
+ friend class QRhiGles2;
+};
+
+struct QGles2CommandBuffer : public QRhiCommandBuffer
+{
+ QGles2CommandBuffer(QRhiImplementation *rhi);
+ ~QGles2CommandBuffer();
+ void destroy() override;
+
+ // keep at a reasonably low value otherwise sizeof Command explodes
+ static const int MAX_DYNAMIC_OFFSET_COUNT = 8;
+
+ struct Command {
+ enum Cmd {
+ BeginFrame,
+ EndFrame,
+ ResetFrame,
+ Viewport,
+ Scissor,
+ BlendConstants,
+ StencilRef,
+ BindVertexBuffer,
+ BindIndexBuffer,
+ Draw,
+ DrawIndexed,
+ BindGraphicsPipeline,
+ BindShaderResources,
+ BindFramebuffer,
+ Clear,
+ BufferSubData,
+ GetBufferSubData,
+ CopyTex,
+ ReadPixels,
+ SubImage,
+ CompressedImage,
+ CompressedSubImage,
+ BlitFromRenderbuffer,
+ BlitFromTexture,
+ GenMip,
+ BindComputePipeline,
+ Dispatch,
+ BarriersForPass,
+ Barrier,
+ InvalidateFramebuffer
+ };
+ Cmd cmd;
+
+ // QRhi*/QGles2* references should be kept at minimum (so no
+ // QRhiTexture/Buffer/etc. pointers).
+ union Args {
+ struct {
+ GLuint timestampQuery;
+ } beginFrame;
+ struct {
+ GLuint timestampQuery;
+ } endFrame;
+ 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;
+ quint32 instanceCount;
+ quint32 baseInstance;
+ } draw;
+ struct {
+ QRhiGraphicsPipeline *ps;
+ quint32 indexCount;
+ quint32 firstIndex;
+ quint32 instanceCount;
+ quint32 baseInstance;
+ qint32 baseVertex;
+ } drawIndexed;
+ struct {
+ QRhiGraphicsPipeline *ps;
+ } bindGraphicsPipeline;
+ struct {
+ QRhiGraphicsPipeline *maybeGraphicsPs;
+ QRhiComputePipeline *maybeComputePs;
+ QRhiShaderResourceBindings *srb;
+ int dynamicOffsetCount;
+ uint dynamicOffsetPairs[MAX_DYNAMIC_OFFSET_COUNT * 2]; // binding, offset
+ } bindShaderResources;
+ struct {
+ GLbitfield mask;
+ float c[4];
+ float d;
+ quint32 s;
+ } clear;
+ struct {
+ GLuint fbo;
+ bool srgb;
+ int colorAttCount;
+ bool stereo;
+ QRhiSwapChain::StereoTargetBuffer stereoTarget;
+ } bindFramebuffer;
+ struct {
+ GLenum target;
+ GLuint buffer;
+ int offset;
+ int size;
+ const void *data; // must come from retainData()
+ } bufferSubData;
+ struct {
+ QRhiReadbackResult *result;
+ GLenum target;
+ GLuint buffer;
+ int offset;
+ int size;
+ } getBufferSubData;
+ struct {
+ GLenum srcTarget;
+ GLenum srcFaceTarget;
+ GLuint srcTexture;
+ int srcLevel;
+ int srcX;
+ int srcY;
+ int srcZ;
+ GLenum dstTarget;
+ GLuint dstTexture;
+ GLenum dstFaceTarget;
+ int dstLevel;
+ int dstX;
+ int dstY;
+ int dstZ;
+ int w;
+ int h;
+ } copyTex;
+ struct {
+ QRhiReadbackResult *result;
+ GLuint texture;
+ int w;
+ int h;
+ QRhiTexture::Format format;
+ GLenum readTarget;
+ int level;
+ int slice3D;
+ } readPixels;
+ struct {
+ GLenum target;
+ GLuint texture;
+ GLenum faceTarget;
+ int level;
+ int dx;
+ int dy;
+ int dz;
+ int w;
+ int h;
+ GLenum glformat;
+ GLenum gltype;
+ int rowStartAlign;
+ int rowLength;
+ const void *data; // must come from retainImage()
+ } subImage;
+ struct {
+ GLenum target;
+ GLuint texture;
+ GLenum faceTarget;
+ int level;
+ GLenum glintformat;
+ int w;
+ int h;
+ int depth;
+ int size;
+ const void *data; // must come from retainData()
+ } compressedImage;
+ struct {
+ GLenum target;
+ GLuint texture;
+ GLenum faceTarget;
+ int level;
+ int dx;
+ int dy;
+ int dz;
+ 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 dstTexture;
+ int dstLevel;
+ int dstLayer;
+ bool isDepthStencil;
+ } blitFromRenderbuffer;
+ struct {
+ GLenum srcTarget;
+ GLuint srcTexture;
+ int srcLevel;
+ int srcLayer;
+ int w;
+ int h;
+ GLenum dstTarget;
+ GLuint dstTexture;
+ int dstLevel;
+ int dstLayer;
+ bool isDepthStencil;
+ } blitFromTexture;
+ struct {
+ GLenum target;
+ GLuint texture;
+ } genMip;
+ struct {
+ QRhiComputePipeline *ps;
+ } bindComputePipeline;
+ struct {
+ GLuint x;
+ GLuint y;
+ GLuint z;
+ } dispatch;
+ struct {
+ int trackerIndex;
+ } barriersForPass;
+ struct {
+ GLbitfield barriers;
+ } barrier;
+ struct {
+ int attCount;
+ GLenum att[3];
+ } invalidateFramebuffer;
+ } args;
+ };
+
+ enum PassType {
+ NoPass,
+ RenderPass,
+ ComputePass
+ };
+
+ QRhiBackendCommandList<Command> commands;
+ QVarLengthArray<QRhiPassResourceTracker, 8> passResTrackers;
+ int currentPassResTrackerIndex;
+
+ PassType recordingPass;
+ bool passNeedsResourceTracking;
+ double lastGpuTime = 0;
+ QRhiRenderTarget *currentTarget;
+ QRhiGraphicsPipeline *currentGraphicsPipeline;
+ QRhiComputePipeline *currentComputePipeline;
+ uint currentPipelineGeneration;
+ QRhiShaderResourceBindings *currentGraphicsSrb;
+ QRhiShaderResourceBindings *currentComputeSrb;
+ uint currentSrbGeneration;
+
+ struct GraphicsPassState {
+ bool valid = false;
+ bool scissor;
+ bool cullFace;
+ GLenum cullMode;
+ GLenum frontFace;
+ bool blendEnabled;
+ struct ColorMask { bool r, g, b, a; } colorMask;
+ struct Blend {
+ GLenum srcColor;
+ GLenum dstColor;
+ GLenum srcAlpha;
+ GLenum dstAlpha;
+ GLenum opColor;
+ GLenum opAlpha;
+ } blend;
+ bool depthTest;
+ bool depthWrite;
+ GLenum depthFunc;
+ bool stencilTest;
+ GLuint stencilReadMask;
+ GLuint stencilWriteMask;
+ struct StencilFace {
+ GLenum func;
+ GLenum failOp;
+ GLenum zfailOp;
+ GLenum zpassOp;
+ } stencil[2]; // front, back
+ bool polyOffsetFill;
+ float polyOffsetFactor;
+ float polyOffsetUnits;
+ float lineWidth;
+ int cpCount;
+ GLenum polygonMode;
+ void reset() { valid = false; }
+ struct {
+ // not part of QRhiGraphicsPipeline but used by setGraphicsPipeline()
+ GLint stencilRef = 0;
+ } dynamic;
+ } graphicsPassState;
+
+ struct ComputePassState {
+ enum Access {
+ Read = 0x01,
+ Write = 0x02
+ };
+ QHash<QRhiResource *, QPair<int, bool> > writtenResources;
+ void reset() {
+ writtenResources.clear();
+ }
+ } computePassState;
+
+ struct TextureUnitState {
+ void *ps;
+ uint psGeneration;
+ uint texture;
+ } textureUnitState[16];
+
+ QVarLengthArray<QByteArray, 4> dataRetainPool;
+ QVarLengthArray<QRhiBufferData, 4> bufferDataRetainPool;
+ QVarLengthArray<QImage, 4> 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.last().constData();
+ }
+ const uchar *retainBufferData(const QRhiBufferData &data) {
+ bufferDataRetainPool.append(data);
+ return reinterpret_cast<const uchar *>(bufferDataRetainPool.last().constData());
+ }
+ const void *retainImage(const QImage &image) {
+ imageRetainPool.append(image);
+ return imageRetainPool.last().constBits();
+ }
+ void resetCommands() {
+ commands.reset();
+ dataRetainPool.clear();
+ bufferDataRetainPool.clear();
+ imageRetainPool.clear();
+
+ passResTrackers.clear();
+ currentPassResTrackerIndex = -1;
+ }
+ void resetState() {
+ recordingPass = NoPass;
+ passNeedsResourceTracking = true;
+ // do not zero lastGpuTime
+ currentTarget = nullptr;
+ resetCommands();
+ resetCachedState();
+ }
+ void resetCachedState() {
+ currentGraphicsPipeline = nullptr;
+ currentComputePipeline = nullptr;
+ currentPipelineGeneration = 0;
+ currentGraphicsSrb = nullptr;
+ currentComputeSrb = nullptr;
+ currentSrbGeneration = 0;
+ graphicsPassState.reset();
+ computePassState.reset();
+ memset(textureUnitState, 0, sizeof(textureUnitState));
+ }
+};
+
+inline bool operator==(const QGles2CommandBuffer::GraphicsPassState::StencilFace &a,
+ const QGles2CommandBuffer::GraphicsPassState::StencilFace &b)
+{
+ return a.func == b.func
+ && a.failOp == b.failOp
+ && a.zfailOp == b.zfailOp
+ && a.zpassOp == b.zpassOp;
+}
+
+inline bool operator!=(const QGles2CommandBuffer::GraphicsPassState::StencilFace &a,
+ const QGles2CommandBuffer::GraphicsPassState::StencilFace &b)
+{
+ return !(a == b);
+}
+
+inline bool operator==(const QGles2CommandBuffer::GraphicsPassState::ColorMask &a,
+ const QGles2CommandBuffer::GraphicsPassState::ColorMask &b)
+{
+ return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
+}
+
+inline bool operator!=(const QGles2CommandBuffer::GraphicsPassState::ColorMask &a,
+ const QGles2CommandBuffer::GraphicsPassState::ColorMask &b)
+{
+ return !(a == b);
+}
+
+inline bool operator==(const QGles2CommandBuffer::GraphicsPassState::Blend &a,
+ const QGles2CommandBuffer::GraphicsPassState::Blend &b)
+{
+ return a.srcColor == b.srcColor
+ && a.dstColor == b.dstColor
+ && a.srcAlpha == b.srcAlpha
+ && a.dstAlpha == b.dstAlpha
+ && a.opColor == b.opColor
+ && a.opAlpha == b.opAlpha;
+}
+
+inline bool operator!=(const QGles2CommandBuffer::GraphicsPassState::Blend &a,
+ const QGles2CommandBuffer::GraphicsPassState::Blend &b)
+{
+ return !(a == b);
+}
+
+struct QGles2SwapChainTimestamps
+{
+ static const int TIMESTAMP_PAIRS = 2;
+
+ bool active[TIMESTAMP_PAIRS] = {};
+ GLuint query[TIMESTAMP_PAIRS * 2] = {};
+
+ void prepare(QRhiGles2 *rhiD);
+ void destroy(QRhiGles2 *rhiD);
+ bool tryQueryTimestamps(int pairIndex, QRhiGles2 *rhiD, double *elapsedSec);
+};
+
+struct QGles2SwapChain : public QRhiSwapChain
+{
+ QGles2SwapChain(QRhiImplementation *rhi);
+ ~QGles2SwapChain();
+ void destroy() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+ QRhiRenderTarget *currentFrameRenderTarget(StereoTargetBuffer targetBuffer) override;
+
+ QSize surfacePixelSize() override;
+ bool isFormatSupported(Format f) override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool createOrResize() override;
+
+ void initSwapChainRenderTarget(QGles2SwapChainRenderTarget *rt);
+
+ QSurface *surface = nullptr;
+ QSize pixelSize;
+ QGles2SwapChainRenderTarget rt;
+ QGles2SwapChainRenderTarget rtLeft;
+ QGles2SwapChainRenderTarget rtRight;
+ QGles2CommandBuffer cb;
+ int frameCount = 0;
+ QGles2SwapChainTimestamps timestamps;
+ int currentTimestampPairIndex = 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,
+ quint32 size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags,
+ QRhiTexture::Format backingFormatHint) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int depth,
+ int arraySize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
+ QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u,
+ QRhiSampler::AddressMode v,
+ QRhiSampler::AddressMode w) 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, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) 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,
+ QRhiCommandBuffer::BeginPassFlags flags) 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,
+ QRhiCommandBuffer::BeginPassFlags flags) 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;
+ double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
+
+ QList<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;
+ QRhiDriverInfo driverInfo() const override;
+ QRhiStats statistics() override;
+ bool makeThreadLocalNativeContextCurrent() override;
+ void releaseCachedResources() override;
+ bool isDeviceLost() const override;
+
+ QByteArray pipelineCacheData() override;
+ void setPipelineCacheData(const QByteArray &data) override;
+
+ bool ensureContext(QSurface *surface = nullptr) const;
+ QSurface *evaluateFallbackSurface() const;
+ void executeDeferredReleases();
+ void trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *bufD, QGles2Buffer::Access access);
+ void trackedImageBarrier(QGles2CommandBuffer *cbD, QGles2Texture *texD, QGles2Texture::Access access);
+ void enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD,
+ int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
+ void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
+ void trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
+ QGles2Buffer *bufD,
+ QRhiPassResourceTracker::BufferAccess access,
+ QRhiPassResourceTracker::BufferStage stage);
+ void trackedRegisterTexture(QRhiPassResourceTracker *passResTracker,
+ QGles2Texture *texD,
+ QRhiPassResourceTracker::TextureAccess access,
+ QRhiPassResourceTracker::TextureStage stage);
+ void executeCommandBuffer(QRhiCommandBuffer *cb);
+ void executeBindGraphicsPipeline(QGles2CommandBuffer *cbD, QGles2GraphicsPipeline *psD);
+ void bindCombinedSampler(QGles2CommandBuffer *cbD, QGles2Texture *texD, QGles2Sampler *samplerD,
+ void *ps, uint psGeneration, int glslLocation,
+ int *texUnit, bool *activeTexUnitAltered);
+ void bindShaderResources(QGles2CommandBuffer *cbD,
+ QRhiGraphicsPipeline *maybeGraphicsPs, QRhiComputePipeline *maybeComputePs,
+ QRhiShaderResourceBindings *srb,
+ const uint *dynOfsPairs, int dynOfsCount);
+ QGles2RenderTargetData *enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD,
+ bool *wantsColorClear = nullptr, bool *wantsDsClear = nullptr);
+ void enqueueBarriersForPass(QGles2CommandBuffer *cbD);
+ QByteArray shaderSource(const QRhiShaderStage &shaderStage, QShaderVersion *shaderVersion);
+ bool compileShader(GLuint program, const QRhiShaderStage &shaderStage, QShaderVersion *shaderVersion);
+ bool linkProgram(GLuint program);
+ void registerUniformIfActive(const QShaderDescription::BlockVariable &var,
+ const QByteArray &namePrefix, int binding, int baseOffset,
+ GLuint program,
+ QDuplicateTracker<int, 256> *activeUniformLocations,
+ QGles2UniformDescriptionVector *dst);
+ void gatherUniforms(GLuint program, const QShaderDescription::UniformBlock &ub,
+ QDuplicateTracker<int, 256> *activeUniformLocations, QGles2UniformDescriptionVector *dst);
+ void gatherSamplers(GLuint program, const QShaderDescription::InOutVariable &v,
+ QGles2SamplerDescriptionVector *dst);
+ void gatherGeneratedSamplers(GLuint program,
+ const QShader::SeparateToCombinedImageSamplerMapping &mapping,
+ QGles2SamplerDescriptionVector *dst);
+ void sanityCheckVertexFragmentInterface(const QShaderDescription &vsDesc, const QShaderDescription &fsDesc);
+ bool isProgramBinaryDiskCacheEnabled() const;
+
+ enum ProgramCacheResult {
+ ProgramCacheHit,
+ ProgramCacheMiss,
+ ProgramCacheError
+ };
+ ProgramCacheResult tryLoadFromDiskOrPipelineCache(const QRhiShaderStage *stages,
+ int stageCount,
+ GLuint program,
+ const QVector<QShaderDescription::InOutVariable> &inputVars,
+ QByteArray *cacheKey);
+ void trySaveToDiskCache(GLuint program, const QByteArray &cacheKey);
+ void trySaveToPipelineCache(GLuint program, const QByteArray &cacheKey, bool force = false);
+
+ QRhi::Flags rhiFlags;
+ QOpenGLContext *ctx = nullptr;
+ bool importedContext = false;
+ QSurfaceFormat requestedFormat;
+ QSurface *fallbackSurface = nullptr;
+ QPointer<QWindow> maybeWindow = nullptr;
+ QOpenGLContext *maybeShareContext = nullptr;
+ mutable bool needsMakeCurrentDueToSwap = false;
+ QOpenGLExtensions *f = nullptr;
+ void (QOPENGLF_APIENTRYP glPolygonMode) (GLenum, GLenum) = nullptr;
+ void(QOPENGLF_APIENTRYP glTexImage1D)(GLenum, GLint, GLint, GLsizei, GLint, GLenum, GLenum,
+ const void *) = nullptr;
+ void(QOPENGLF_APIENTRYP glTexStorage1D)(GLenum, GLint, GLenum, GLsizei) = nullptr;
+ void(QOPENGLF_APIENTRYP glTexSubImage1D)(GLenum, GLint, GLint, GLsizei, GLenum, GLenum,
+ const GLvoid *) = nullptr;
+ void(QOPENGLF_APIENTRYP glCopyTexSubImage1D)(GLenum, GLint, GLint, GLint, GLint,
+ GLsizei) = nullptr;
+ void(QOPENGLF_APIENTRYP glCompressedTexImage1D)(GLenum, GLint, GLenum, GLsizei, GLint, GLsizei,
+ const GLvoid *) = nullptr;
+ void(QOPENGLF_APIENTRYP glCompressedTexSubImage1D)(GLenum, GLint, GLint, GLsizei, GLenum,
+ GLsizei, const GLvoid *) = nullptr;
+ void(QOPENGLF_APIENTRYP glFramebufferTexture1D)(GLenum, GLenum, GLenum, GLuint,
+ GLint) = nullptr;
+ void(QOPENGLF_APIENTRYP glFramebufferTextureMultiviewOVR)(GLenum, GLenum, GLuint, GLint,
+ GLint, GLsizei) = nullptr;
+ void (QOPENGLF_APIENTRYP glQueryCounter)(GLuint, GLenum) = nullptr;
+ void (QOPENGLF_APIENTRYP glGetQueryObjectui64v)(GLuint, GLenum, quint64 *) = nullptr;
+ void (QOPENGLF_APIENTRYP glObjectLabel)(GLenum, GLuint, GLsizei, const GLchar *) = nullptr;
+ void (QOPENGLF_APIENTRYP glFramebufferTexture2DMultisampleEXT)(GLenum, GLenum, GLenum, GLuint, GLint, GLsizei) = nullptr;
+ void (QOPENGLF_APIENTRYP glFramebufferTextureMultisampleMultiviewOVR)(GLenum, GLenum, GLuint, GLint, GLsizei, GLint, GLsizei) = nullptr;
+ uint vao = 0;
+ struct Caps {
+ Caps()
+ : ctxMajor(2),
+ ctxMinor(0),
+ maxTextureSize(2048),
+ maxDrawBuffers(4),
+ maxSamples(16),
+ maxTextureArraySize(0),
+ maxThreadGroupsPerDimension(0),
+ maxThreadsPerThreadGroup(0),
+ maxThreadGroupsX(0),
+ maxThreadGroupsY(0),
+ maxThreadGroupsZ(0),
+ maxUniformVectors(4096),
+ maxVertexInputs(8),
+ maxVertexOutputs(8),
+ msaaRenderBuffer(false),
+ multisampledTexture(false),
+ npotTextureFull(true),
+ gles(false),
+ fixedIndexPrimitiveRestart(false),
+ bgraExternalFormat(false),
+ bgraInternalFormat(false),
+ r8Format(false),
+ r16Format(false),
+ floatFormats(false),
+ rgb10Formats(false),
+ depthTexture(false),
+ packedDepthStencil(false),
+ needsDepthStencilCombinedAttach(false),
+ srgbWriteControl(false),
+ coreProfile(false),
+ uniformBuffers(false),
+ elementIndexUint(false),
+ depth24(false),
+ rgba8Format(false),
+ instancing(false),
+ baseVertex(false),
+ compute(false),
+ textureCompareMode(false),
+ properMapBuffer(false),
+ nonBaseLevelFramebufferTexture(false),
+ texelFetch(false),
+ intAttributes(true),
+ screenSpaceDerivatives(false),
+ programBinary(false),
+ texture3D(false),
+ tessellation(false),
+ geometryShader(false),
+ texture1D(false),
+ hasDrawBuffersFunc(false),
+ halfAttributes(false),
+ multiView(false),
+ timestamps(false),
+ objectLabel(false)
+ { }
+ int ctxMajor;
+ int ctxMinor;
+ int maxTextureSize;
+ int maxDrawBuffers;
+ int maxSamples;
+ int maxTextureArraySize;
+ int maxThreadGroupsPerDimension;
+ int maxThreadsPerThreadGroup;
+ int maxThreadGroupsX;
+ int maxThreadGroupsY;
+ int maxThreadGroupsZ;
+ int maxUniformVectors;
+ int maxVertexInputs;
+ int maxVertexOutputs;
+ // Multisample fb and blit are supported (GLES 3.0 or OpenGL 3.x). Not
+ // the same as multisample textures!
+ uint msaaRenderBuffer : 1;
+ uint multisampledTexture : 1;
+ uint npotTextureFull : 1;
+ uint gles : 1;
+ uint fixedIndexPrimitiveRestart : 1;
+ uint bgraExternalFormat : 1;
+ uint bgraInternalFormat : 1;
+ uint r8Format : 1;
+ uint r16Format : 1;
+ uint floatFormats : 1;
+ uint rgb10Formats : 1;
+ uint depthTexture : 1;
+ uint packedDepthStencil : 1;
+ uint needsDepthStencilCombinedAttach : 1;
+ uint srgbWriteControl : 1;
+ uint coreProfile : 1;
+ uint uniformBuffers : 1;
+ uint elementIndexUint : 1;
+ uint depth24 : 1;
+ uint rgba8Format : 1;
+ uint instancing : 1;
+ uint baseVertex : 1;
+ uint compute : 1;
+ uint textureCompareMode : 1;
+ uint properMapBuffer : 1;
+ uint nonBaseLevelFramebufferTexture : 1;
+ uint texelFetch : 1;
+ uint intAttributes : 1;
+ uint screenSpaceDerivatives : 1;
+ uint programBinary : 1;
+ uint texture3D : 1;
+ uint tessellation : 1;
+ uint geometryShader : 1;
+ uint texture1D : 1;
+ uint hasDrawBuffersFunc : 1;
+ uint halfAttributes : 1;
+ uint multiView : 1;
+ uint timestamps : 1;
+ uint objectLabel : 1;
+ uint glesMultisampleRenderToTexture : 1;
+ uint glesMultiviewMultisampleRenderToTexture : 1;
+ } caps;
+ QGles2SwapChain *currentSwapChain = nullptr;
+ QSet<GLint> supportedCompressedFormats;
+ mutable QList<int> supportedSampleCountList;
+ QRhiGles2NativeHandles nativeHandlesStruct;
+ QRhiDriverInfo driverInfoStruct;
+ mutable bool contextLost = false;
+
+ 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;
+ GLuint nonMsaaThrowawayDepthTexture;
+ } textureRenderTarget;
+ };
+ };
+ QList<DeferredReleaseEntry> releaseQueue;
+
+ struct OffscreenFrame {
+ OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
+ bool active = false;
+ QGles2CommandBuffer cbWrapper;
+ GLuint tsQueries[2] = {};
+ } ofr;
+
+ QHash<QRhiShaderStage, uint> m_shaderCache;
+
+ struct PipelineCacheData {
+ quint32 format;
+ QByteArray data;
+ };
+ QHash<QByteArray, PipelineCacheData> m_pipelineCache;
+};
+
+Q_DECLARE_TYPEINFO(QRhiGles2::DeferredReleaseEntry, Q_RELOCATABLE_TYPE);
+
QT_END_NAMESPACE
#endif
diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h
deleted file mode 100644
index 64a22c83c5..0000000000
--- a/src/gui/rhi/qrhigles2_p_p.h
+++ /dev/null
@@ -1,1044 +0,0 @@
-// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#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 <QByteArray>
-#include <QSurface>
-
-#include <QtCore/private/qduplicatetracker_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class QOpenGLExtensions;
-
-struct QGles2Buffer : public QRhiBuffer
-{
- QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
- ~QGles2Buffer();
- void destroy() override;
- bool create() override;
- QRhiBuffer::NativeBuffer nativeBuffer() override;
- char *beginFullDynamicBufferUpdateForCurrentFrame() override;
- void endFullDynamicBufferUpdateForCurrentFrame() override;
-
- int nonZeroSize = 0;
- GLuint buffer = 0;
- GLenum targetForDataOps;
- QByteArray data;
- enum Access {
- AccessNone,
- AccessVertex,
- AccessIndex,
- AccessUniform,
- AccessStorageRead,
- AccessStorageWrite,
- AccessStorageReadWrite,
- AccessUpdate
- };
- struct UsageState {
- Access access;
- };
- UsageState usageState;
- friend class QRhiGles2;
-};
-
-struct QGles2RenderBuffer : public QRhiRenderBuffer
-{
- QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
- int sampleCount, QRhiRenderBuffer::Flags flags,
- QRhiTexture::Format backingFormatHint);
- ~QGles2RenderBuffer();
- void destroy() override;
- bool create() override;
- bool createFrom(NativeRenderBuffer src) override;
- QRhiTexture::Format backingFormat() const override;
-
- GLuint renderbuffer = 0;
- GLuint stencilRenderbuffer = 0; // when packed depth-stencil not supported
- int samples;
- bool owns = true;
- uint generation = 0;
- 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 depth,
- int arraySize, int sampleCount, Flags flags);
- ~QGles2Texture();
- void destroy() override;
- bool create() override;
- bool createFrom(NativeTexture src) override;
- NativeTexture nativeTexture() override;
-
- bool prepareCreate(QSize *adjustedSize = nullptr);
-
- GLuint texture = 0;
- bool owns = true;
- GLenum target;
- GLenum glintformat;
- GLenum glsizedintformat;
- GLenum glformat;
- GLenum gltype;
- QGles2SamplerData samplerState;
- bool specified = false;
- bool zeroInitialized = false;
- int mipLevelCount = 0;
-
- enum Access {
- AccessNone,
- AccessSample,
- AccessFramebuffer,
- AccessStorageRead,
- AccessStorageWrite,
- AccessStorageReadWrite,
- AccessUpdate,
- AccessRead
- };
- struct UsageState {
- Access access;
- };
- UsageState usageState;
-
- uint generation = 0;
- friend class QRhiGles2;
-};
-
-struct QGles2Sampler : public QRhiSampler
-{
- QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
- AddressMode u, AddressMode v, AddressMode w);
- ~QGles2Sampler();
- void destroy() override;
- bool create() override;
-
- QGles2SamplerData d;
- uint generation = 0;
- friend class QRhiGles2;
-};
-
-struct QGles2RenderPassDescriptor : public QRhiRenderPassDescriptor
-{
- QGles2RenderPassDescriptor(QRhiImplementation *rhi);
- ~QGles2RenderPassDescriptor();
- void destroy() override;
- bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
- QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
- QVector<quint32> serializedFormat() const 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;
- QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList;
-};
-
-struct QGles2SwapChainRenderTarget : public QRhiSwapChainRenderTarget
-{
- QGles2SwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain);
- ~QGles2SwapChainRenderTarget();
- void destroy() 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 destroy() override;
-
- QSize pixelSize() const override;
- float devicePixelRatio() const override;
- int sampleCount() const override;
-
- QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
- bool create() override;
-
- QGles2RenderTargetData d;
- GLuint framebuffer = 0;
- friend class QRhiGles2;
-};
-
-struct QGles2ShaderResourceBindings : public QRhiShaderResourceBindings
-{
- QGles2ShaderResourceBindings(QRhiImplementation *rhi);
- ~QGles2ShaderResourceBindings();
- void destroy() override;
- bool create() override;
- void updateResources(UpdateFlags flags) override;
-
- bool hasDynamicOffset = false;
- uint generation = 0;
- friend class QRhiGles2;
-};
-
-struct QGles2UniformDescription
-{
- QShaderDescription::VariableType type;
- int glslLocation;
- int binding;
- uint offset;
- int size;
- int arrayDim;
-};
-
-Q_DECLARE_TYPEINFO(QGles2UniformDescription, Q_RELOCATABLE_TYPE);
-
-struct QGles2SamplerDescription
-{
- int glslLocation;
- int combinedBinding;
- int tbinding;
- int sbinding;
-};
-
-Q_DECLARE_TYPEINFO(QGles2SamplerDescription, Q_RELOCATABLE_TYPE);
-
-using QGles2UniformDescriptionVector = QVarLengthArray<QGles2UniformDescription, 8>;
-using QGles2SamplerDescriptionVector = QVarLengthArray<QGles2SamplerDescription, 4>;
-
-struct QGles2UniformState
-{
- static constexpr int MAX_TRACKED_LOCATION = 1023;
- int componentCount;
- float v[4];
-};
-
-struct QGles2GraphicsPipeline : public QRhiGraphicsPipeline
-{
- QGles2GraphicsPipeline(QRhiImplementation *rhi);
- ~QGles2GraphicsPipeline();
- void destroy() override;
- bool create() override;
-
- GLuint program = 0;
- GLenum drawMode = GL_TRIANGLES;
- QGles2UniformDescriptionVector uniforms;
- QGles2SamplerDescriptionVector samplers;
- QGles2UniformState uniformState[QGles2UniformState::MAX_TRACKED_LOCATION + 1];
- QRhiShaderResourceBindings *currentSrb = nullptr;
- uint currentSrbGeneration = 0;
- uint generation = 0;
- friend class QRhiGles2;
-};
-
-struct QGles2ComputePipeline : public QRhiComputePipeline
-{
- QGles2ComputePipeline(QRhiImplementation *rhi);
- ~QGles2ComputePipeline();
- void destroy() override;
- bool create() override;
-
- GLuint program = 0;
- QGles2UniformDescriptionVector uniforms;
- QGles2SamplerDescriptionVector samplers;
- QGles2UniformState uniformState[QGles2UniformState::MAX_TRACKED_LOCATION + 1];
- QRhiShaderResourceBindings *currentSrb = nullptr;
- uint currentSrbGeneration = 0;
- uint generation = 0;
- friend class QRhiGles2;
-};
-
-struct QGles2CommandBuffer : public QRhiCommandBuffer
-{
- QGles2CommandBuffer(QRhiImplementation *rhi);
- ~QGles2CommandBuffer();
- void destroy() override;
-
- // keep at a reasonably low value otherwise sizeof Command explodes
- static const int MAX_DYNAMIC_OFFSET_COUNT = 8;
-
- struct Command {
- enum Cmd {
- BeginFrame,
- EndFrame,
- ResetFrame,
- Viewport,
- Scissor,
- BlendConstants,
- StencilRef,
- BindVertexBuffer,
- BindIndexBuffer,
- Draw,
- DrawIndexed,
- BindGraphicsPipeline,
- BindShaderResources,
- BindFramebuffer,
- Clear,
- BufferSubData,
- GetBufferSubData,
- CopyTex,
- ReadPixels,
- SubImage,
- CompressedImage,
- CompressedSubImage,
- BlitFromRenderbuffer,
- GenMip,
- BindComputePipeline,
- Dispatch,
- BarriersForPass,
- Barrier
- };
- Cmd cmd;
-
- // QRhi*/QGles2* references should be kept at minimum (so no
- // QRhiTexture/Buffer/etc. pointers).
- union Args {
- 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;
- quint32 instanceCount;
- quint32 baseInstance;
- } draw;
- struct {
- QRhiGraphicsPipeline *ps;
- quint32 indexCount;
- quint32 firstIndex;
- quint32 instanceCount;
- quint32 baseInstance;
- qint32 baseVertex;
- } drawIndexed;
- struct {
- QRhiGraphicsPipeline *ps;
- } bindGraphicsPipeline;
- struct {
- QRhiGraphicsPipeline *maybeGraphicsPs;
- QRhiComputePipeline *maybeComputePs;
- QRhiShaderResourceBindings *srb;
- int dynamicOffsetCount;
- uint dynamicOffsetPairs[MAX_DYNAMIC_OFFSET_COUNT * 2]; // binding, offset
- } 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 {
- QRhiBufferReadbackResult *result;
- GLenum target;
- GLuint buffer;
- int offset;
- int size;
- } getBufferSubData;
- struct {
- GLenum srcTarget;
- GLenum srcFaceTarget;
- GLuint srcTexture;
- int srcLevel;
- int srcX;
- int srcY;
- int srcZ;
- GLenum dstTarget;
- GLuint dstTexture;
- GLenum dstFaceTarget;
- int dstLevel;
- int dstX;
- int dstY;
- int dstZ;
- int w;
- int h;
- } copyTex;
- struct {
- QRhiReadbackResult *result;
- GLuint texture;
- int w;
- int h;
- QRhiTexture::Format format;
- GLenum readTarget;
- int level;
- int slice3D;
- } readPixels;
- struct {
- GLenum target;
- GLuint texture;
- GLenum faceTarget;
- int level;
- int dx;
- int dy;
- int dz;
- int w;
- int h;
- GLenum glformat;
- GLenum gltype;
- int rowStartAlign;
- int rowLength;
- const void *data; // must come from retainImage()
- } subImage;
- struct {
- GLenum target;
- GLuint texture;
- GLenum faceTarget;
- int level;
- GLenum glintformat;
- int w;
- int h;
- int depth;
- int size;
- const void *data; // must come from retainData()
- } compressedImage;
- struct {
- GLenum target;
- GLuint texture;
- GLenum faceTarget;
- int level;
- int dx;
- int dy;
- int dz;
- 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;
- int dstLayer;
- } blitFromRb;
- struct {
- GLenum target;
- GLuint texture;
- } genMip;
- struct {
- QRhiComputePipeline *ps;
- } bindComputePipeline;
- struct {
- GLuint x;
- GLuint y;
- GLuint z;
- } dispatch;
- struct {
- int trackerIndex;
- } barriersForPass;
- struct {
- GLbitfield barriers;
- } barrier;
- } args;
- };
-
- enum PassType {
- NoPass,
- RenderPass,
- ComputePass
- };
-
- QRhiBackendCommandList<Command> commands;
- QVarLengthArray<QRhiPassResourceTracker, 8> passResTrackers;
- int currentPassResTrackerIndex;
-
- PassType recordingPass;
- bool passNeedsResourceTracking;
- QRhiRenderTarget *currentTarget;
- QRhiGraphicsPipeline *currentGraphicsPipeline;
- QRhiComputePipeline *currentComputePipeline;
- uint currentPipelineGeneration;
- QRhiShaderResourceBindings *currentGraphicsSrb;
- QRhiShaderResourceBindings *currentComputeSrb;
- uint currentSrbGeneration;
-
- struct GraphicsPassState {
- bool valid = false;
- bool scissor;
- bool cullFace;
- GLenum cullMode;
- GLenum frontFace;
- bool blendEnabled;
- struct ColorMask { bool r, g, b, a; } colorMask;
- struct Blend {
- GLenum srcColor;
- GLenum dstColor;
- GLenum srcAlpha;
- GLenum dstAlpha;
- GLenum opColor;
- GLenum opAlpha;
- } blend;
- bool depthTest;
- bool depthWrite;
- GLenum depthFunc;
- bool stencilTest;
- GLuint stencilReadMask;
- GLuint stencilWriteMask;
- struct StencilFace {
- GLenum func;
- GLenum failOp;
- GLenum zfailOp;
- GLenum zpassOp;
- } stencil[2]; // front, back
- bool polyOffsetFill;
- float polyOffsetFactor;
- float polyOffsetUnits;
- float lineWidth;
- int cpCount;
- GLenum polygonMode;
- void reset() { valid = false; }
- struct {
- // not part of QRhiGraphicsPipeline but used by setGraphicsPipeline()
- GLint stencilRef = 0;
- } dynamic;
- } graphicsPassState;
-
- struct ComputePassState {
- enum Access {
- Read = 0x01,
- Write = 0x02
- };
- QHash<QRhiResource *, QPair<int, bool> > writtenResources;
- void reset() {
- writtenResources.clear();
- }
- } computePassState;
-
- struct TextureUnitState {
- void *ps;
- uint psGeneration;
- uint texture;
- } textureUnitState[16];
-
- QVarLengthArray<QByteArray, 4> dataRetainPool;
- QVarLengthArray<QRhiBufferData, 4> bufferDataRetainPool;
- QVarLengthArray<QImage, 4> 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.last().constData();
- }
- const uchar *retainBufferData(const QRhiBufferData &data) {
- bufferDataRetainPool.append(data);
- return reinterpret_cast<const uchar *>(bufferDataRetainPool.last().constData());
- }
- const void *retainImage(const QImage &image) {
- imageRetainPool.append(image);
- return imageRetainPool.last().constBits();
- }
- void resetCommands() {
- commands.reset();
- dataRetainPool.clear();
- bufferDataRetainPool.clear();
- imageRetainPool.clear();
-
- passResTrackers.clear();
- currentPassResTrackerIndex = -1;
- }
- void resetState() {
- recordingPass = NoPass;
- passNeedsResourceTracking = true;
- currentTarget = nullptr;
- resetCommands();
- resetCachedState();
- }
- void resetCachedState() {
- currentGraphicsPipeline = nullptr;
- currentComputePipeline = nullptr;
- currentPipelineGeneration = 0;
- currentGraphicsSrb = nullptr;
- currentComputeSrb = nullptr;
- currentSrbGeneration = 0;
- graphicsPassState.reset();
- computePassState.reset();
- memset(textureUnitState, 0, sizeof(textureUnitState));
- }
-};
-
-inline bool operator==(const QGles2CommandBuffer::GraphicsPassState::StencilFace &a,
- const QGles2CommandBuffer::GraphicsPassState::StencilFace &b)
-{
- return a.func == b.func
- && a.failOp == b.failOp
- && a.zfailOp == b.zfailOp
- && a.zpassOp == b.zpassOp;
-}
-
-inline bool operator!=(const QGles2CommandBuffer::GraphicsPassState::StencilFace &a,
- const QGles2CommandBuffer::GraphicsPassState::StencilFace &b)
-{
- return !(a == b);
-}
-
-inline bool operator==(const QGles2CommandBuffer::GraphicsPassState::ColorMask &a,
- const QGles2CommandBuffer::GraphicsPassState::ColorMask &b)
-{
- return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
-}
-
-inline bool operator!=(const QGles2CommandBuffer::GraphicsPassState::ColorMask &a,
- const QGles2CommandBuffer::GraphicsPassState::ColorMask &b)
-{
- return !(a == b);
-}
-
-inline bool operator==(const QGles2CommandBuffer::GraphicsPassState::Blend &a,
- const QGles2CommandBuffer::GraphicsPassState::Blend &b)
-{
- return a.srcColor == b.srcColor
- && a.dstColor == b.dstColor
- && a.srcAlpha == b.srcAlpha
- && a.dstAlpha == b.dstAlpha
- && a.opColor == b.opColor
- && a.opAlpha == b.opAlpha;
-}
-
-inline bool operator!=(const QGles2CommandBuffer::GraphicsPassState::Blend &a,
- const QGles2CommandBuffer::GraphicsPassState::Blend &b)
-{
- return !(a == b);
-}
-
-struct QGles2SwapChain : public QRhiSwapChain
-{
- QGles2SwapChain(QRhiImplementation *rhi);
- ~QGles2SwapChain();
- void destroy() override;
-
- QRhiCommandBuffer *currentFrameCommandBuffer() override;
- QRhiRenderTarget *currentFrameRenderTarget() override;
-
- QSize surfacePixelSize() override;
- bool isFormatSupported(Format f) override;
-
- QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
- bool createOrResize() override;
-
- QSurface *surface = nullptr;
- QSize pixelSize;
- QGles2SwapChainRenderTarget 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,
- QRhiTexture::Format backingFormatHint) override;
- QRhiTexture *createTexture(QRhiTexture::Format format,
- const QSize &pixelSize,
- int depth,
- int arraySize,
- int sampleCount,
- QRhiTexture::Flags flags) override;
- QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
- QRhiSampler::Filter minFilter,
- QRhiSampler::Filter mipmapMode,
- QRhiSampler:: AddressMode u,
- QRhiSampler::AddressMode v,
- QRhiSampler::AddressMode w) 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, QRhi::BeginFrameFlags flags) override;
- QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) 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,
- QRhiCommandBuffer::BeginPassFlags flags) 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,
- QRhiCommandBuffer::BeginPassFlags flags) 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;
-
- QList<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;
- QRhiDriverInfo driverInfo() const override;
- QRhiMemAllocStats graphicsMemoryAllocationStatistics() override;
- bool makeThreadLocalNativeContextCurrent() override;
- void releaseCachedResources() override;
- bool isDeviceLost() const override;
-
- QByteArray pipelineCacheData() override;
- void setPipelineCacheData(const QByteArray &data) override;
-
- bool ensureContext(QSurface *surface = nullptr) const;
- void executeDeferredReleases();
- void trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *bufD, QGles2Buffer::Access access);
- void trackedImageBarrier(QGles2CommandBuffer *cbD, QGles2Texture *texD, QGles2Texture::Access access);
- void enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD,
- int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
- void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
- void trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
- QGles2Buffer *bufD,
- QRhiPassResourceTracker::BufferAccess access,
- QRhiPassResourceTracker::BufferStage stage);
- void trackedRegisterTexture(QRhiPassResourceTracker *passResTracker,
- QGles2Texture *texD,
- QRhiPassResourceTracker::TextureAccess access,
- QRhiPassResourceTracker::TextureStage stage);
- void executeCommandBuffer(QRhiCommandBuffer *cb);
- void executeBindGraphicsPipeline(QGles2CommandBuffer *cbD, QGles2GraphicsPipeline *psD);
- void bindCombinedSampler(QGles2CommandBuffer *cbD, QGles2Texture *texD, QGles2Sampler *samplerD,
- void *ps, uint psGeneration, int glslLocation,
- int *texUnit, bool *activeTexUnitAltered);
- void bindShaderResources(QGles2CommandBuffer *cbD,
- QRhiGraphicsPipeline *maybeGraphicsPs, QRhiComputePipeline *maybeComputePs,
- QRhiShaderResourceBindings *srb,
- const uint *dynOfsPairs, int dynOfsCount);
- QGles2RenderTargetData *enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD,
- bool *wantsColorClear = nullptr, bool *wantsDsClear = nullptr);
- void enqueueBarriersForPass(QGles2CommandBuffer *cbD);
- int effectiveSampleCount(int sampleCount) const;
- QByteArray shaderSource(const QRhiShaderStage &shaderStage, QShaderVersion *shaderVersion);
- bool compileShader(GLuint program, const QRhiShaderStage &shaderStage, QShaderVersion *shaderVersion);
- bool linkProgram(GLuint program);
- void registerUniformIfActive(const QShaderDescription::BlockVariable &var,
- const QByteArray &namePrefix, int binding, int baseOffset,
- GLuint program,
- QDuplicateTracker<int, 256> *activeUniformLocations,
- QGles2UniformDescriptionVector *dst);
- void gatherUniforms(GLuint program, const QShaderDescription::UniformBlock &ub,
- QDuplicateTracker<int, 256> *activeUniformLocations, QGles2UniformDescriptionVector *dst);
- void gatherSamplers(GLuint program, const QShaderDescription::InOutVariable &v,
- QGles2SamplerDescriptionVector *dst);
- void gatherGeneratedSamplers(GLuint program,
- const QShader::SeparateToCombinedImageSamplerMapping &mapping,
- QGles2SamplerDescriptionVector *dst);
- void sanityCheckVertexFragmentInterface(const QShaderDescription &vsDesc, const QShaderDescription &fsDesc);
- bool isProgramBinaryDiskCacheEnabled() const;
-
- enum ProgramCacheResult {
- ProgramCacheHit,
- ProgramCacheMiss,
- ProgramCacheError
- };
- ProgramCacheResult tryLoadFromDiskOrPipelineCache(const QRhiShaderStage *stages,
- int stageCount,
- GLuint program,
- const QVector<QShaderDescription::InOutVariable> &inputVars,
- QByteArray *cacheKey);
- void trySaveToDiskCache(GLuint program, const QByteArray &cacheKey);
- void trySaveToPipelineCache(GLuint program, const QByteArray &cacheKey, bool force = false);
-
- QRhi::Flags rhiFlags;
- QOpenGLContext *ctx = nullptr;
- bool importedContext = false;
- QSurfaceFormat requestedFormat;
- QSurface *fallbackSurface = nullptr;
- QWindow *maybeWindow = nullptr;
- QOpenGLContext *maybeShareContext = nullptr;
- mutable bool needsMakeCurrentDueToSwap = false;
- QOpenGLExtensions *f = nullptr;
- void (QOPENGLF_APIENTRYP glPolygonMode) (GLenum, GLenum) = nullptr;
- uint vao = 0;
- struct Caps {
- Caps()
- : ctxMajor(2),
- ctxMinor(0),
- maxTextureSize(2048),
- maxDrawBuffers(4),
- maxSamples(16),
- maxTextureArraySize(0),
- maxThreadGroupsPerDimension(0),
- maxThreadsPerThreadGroup(0),
- maxThreadGroupsX(0),
- maxThreadGroupsY(0),
- maxThreadGroupsZ(0),
- maxUniformVectors(4096),
- maxVertexInputs(8),
- maxVertexOutputs(8),
- msaaRenderBuffer(false),
- multisampledTexture(false),
- npotTextureFull(true),
- gles(false),
- fixedIndexPrimitiveRestart(false),
- bgraExternalFormat(false),
- bgraInternalFormat(false),
- r8Format(false),
- r16Format(false),
- floatFormats(false),
- rgb10Formats(false),
- depthTexture(false),
- packedDepthStencil(false),
- needsDepthStencilCombinedAttach(false),
- srgbCapableDefaultFramebuffer(false),
- coreProfile(false),
- uniformBuffers(false),
- elementIndexUint(false),
- depth24(false),
- rgba8Format(false),
- instancing(false),
- baseVertex(false),
- compute(false),
- textureCompareMode(false),
- properMapBuffer(false),
- nonBaseLevelFramebufferTexture(false),
- texelFetch(false),
- intAttributes(true),
- screenSpaceDerivatives(false),
- programBinary(false),
- texture3D(false),
- tessellation(false),
- geometryShader(false)
- { }
- int ctxMajor;
- int ctxMinor;
- int maxTextureSize;
- int maxDrawBuffers;
- int maxSamples;
- int maxTextureArraySize;
- int maxThreadGroupsPerDimension;
- int maxThreadsPerThreadGroup;
- int maxThreadGroupsX;
- int maxThreadGroupsY;
- int maxThreadGroupsZ;
- int maxUniformVectors;
- int maxVertexInputs;
- int maxVertexOutputs;
- // Multisample fb and blit are supported (GLES 3.0 or OpenGL 3.x). Not
- // the same as multisample textures!
- uint msaaRenderBuffer : 1;
- uint multisampledTexture : 1;
- uint npotTextureFull : 1;
- uint gles : 1;
- uint fixedIndexPrimitiveRestart : 1;
- uint bgraExternalFormat : 1;
- uint bgraInternalFormat : 1;
- uint r8Format : 1;
- uint r16Format : 1;
- uint floatFormats : 1;
- uint rgb10Formats : 1;
- uint depthTexture : 1;
- uint packedDepthStencil : 1;
- uint needsDepthStencilCombinedAttach : 1;
- uint srgbCapableDefaultFramebuffer : 1;
- uint coreProfile : 1;
- uint uniformBuffers : 1;
- uint elementIndexUint : 1;
- uint depth24 : 1;
- uint rgba8Format : 1;
- uint instancing : 1;
- uint baseVertex : 1;
- uint compute : 1;
- uint textureCompareMode : 1;
- uint properMapBuffer : 1;
- uint nonBaseLevelFramebufferTexture : 1;
- uint texelFetch : 1;
- uint intAttributes : 1;
- uint screenSpaceDerivatives : 1;
- uint programBinary : 1;
- uint texture3D : 1;
- uint tessellation : 1;
- uint geometryShader : 1;
- } caps;
- QGles2SwapChain *currentSwapChain = nullptr;
- QSet<GLint> supportedCompressedFormats;
- mutable QList<int> supportedSampleCountList;
- QRhiGles2NativeHandles nativeHandlesStruct;
- QRhiDriverInfo driverInfoStruct;
- mutable bool contextLost = false;
-
- 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;
- };
- };
- QList<DeferredReleaseEntry> releaseQueue;
-
- struct OffscreenFrame {
- OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
- bool active = false;
- QGles2CommandBuffer cbWrapper;
- } ofr;
-
- QHash<QRhiShaderStage, uint> m_shaderCache;
-
- struct PipelineCacheData {
- quint32 format;
- QByteArray data;
- };
- QHash<QByteArray, PipelineCacheData> m_pipelineCache;
-};
-
-Q_DECLARE_TYPEINFO(QRhiGles2::DeferredReleaseEntry, Q_RELOCATABLE_TYPE);
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index c4317e5f0f..b99afc596c 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -1,10 +1,18 @@
-// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qrhimetal_p_p.h"
+#include "qrhimetal_p.h"
+#include "qshader_p.h"
#include <QGuiApplication>
#include <QWindow>
+#include <QUrl>
+#include <QFile>
+#include <QTemporaryFile>
+#include <QFileInfo>
#include <qmath.h>
+#include <QOperatingSystemVersion>
+
+#include <QtCore/private/qcore_mac_p.h>
#ifdef Q_OS_MACOS
#include <AppKit/AppKit.h>
@@ -32,19 +40,28 @@ QT_BEGIN_NAMESPACE
#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.
+// Even though the macOS 13 MTLBinaryArchive problem (QTBUG-106703) seems
+// to be solved in later 13.x releases, we have reports from old Intel hardware
+// and older macOS versions where this causes problems (QTBUG-114338).
+// Thus we no longer do OS version based differentiation, but rather have a
+// single toggle that is currently on, and so QRhi::(set)pipelineCache()
+// does nothing with Metal.
+#define QRHI_METAL_DISABLE_BINARY_ARCHIVE
+
+// We should be able to operate with command buffers that do not automatically
+// retain/release the resources used by them. (since we have logic that mirrors
+// other backends such as the Vulkan one anyway)
+#define QRHI_METAL_COMMAND_BUFFERS_WITH_UNRETAINED_REFERENCES
/*!
\class QRhiMetalInitParams
\inmodule QtRhi
- \internal
+ \since 6.6
\brief Metal specific initialization parameters.
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
A Metal-based QRhi needs no special parameters for initialization.
\badcode
@@ -52,10 +69,13 @@ QT_BEGIN_NAMESPACE
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 Metal API validation cannot be enabled programmatically by the QRhi.
+ Instead, either run the debug build of the application in XCode, by
+ generating a \c{.xcodeproj} file via \c{cmake -G Xcode}, or set the
+ environment variable \c{METAL_DEVICE_WRAPPER_TYPE=1}. The variable needs to
+ be set early on in the environment, perferably before starting the process;
+ attempting to set it at QRhi creation time is not functional in practice.
+ (too late probably)
\note QRhiSwapChain can only target QWindow instances that have their
surface type set to QSurface::MetalSurface.
@@ -74,14 +94,30 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiMetalNativeHandles
\inmodule QtRhi
- \internal
+ \since 6.6
\brief Holds the Metal device used by the QRhi.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
+ \variable QRhiMetalNativeHandles::dev
+
+ Set to a valid MTLDevice to import an existing device.
+*/
+
+/*!
+ \variable QRhiMetalNativeHandles::cmdQueue
+
+ Set to a valid MTLCommandQueue when importing an existing command queue.
+ When \nullptr, QRhi will create a new command queue.
+*/
+
+/*!
\class QRhiMetalCommandBufferNativeHandles
\inmodule QtRhi
- \internal
+ \since 6.6
\brief Holds the MTLCommandBuffer and MTLRenderCommandEncoder objects that are backing a QRhiCommandBuffer.
\note The command buffer object is only guaranteed to be valid while
@@ -93,14 +129,28 @@ QT_BEGIN_NAMESPACE
\note The command encoder is only valid while recording a pass, that is,
between \l{QRhiCommandBuffer::beginPass()} -
\l{QRhiCommandBuffer::endPass()}.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
+/*!
+ \variable QRhiMetalCommandBufferNativeHandles::commandBuffer
+*/
+
+/*!
+ \variable QRhiMetalCommandBufferNativeHandles::encoder
+*/
+
struct QMetalShader
{
id<MTLLibrary> lib = nil;
id<MTLFunction> func = nil;
- std::array<uint, 3> localSize;
+ std::array<uint, 3> localSize = {};
+ uint outputVertexCount = 0;
+ QShaderDescription desc;
QShader::NativeResourceBindingMap nativeResourceBindingMap;
+ QShader::NativeShaderInfo nativeShaderInfo;
void destroy() {
nativeResourceBindingMap.clear();
@@ -113,11 +163,14 @@ struct QMetalShader
struct QRhiMetalData
{
- QRhiMetalData(QRhiImplementation *rhi) : ofr(rhi) { }
+ QRhiMetalData(QRhiMetal *rhi) : q(rhi), ofr(rhi) { }
+ QRhiMetal *q;
id<MTLDevice> dev = nil;
id<MTLCommandQueue> cmdQueue = nil;
+ API_AVAILABLE(macosx(11.0), ios(14.0)) id<MTLBinaryArchive> binArch = nil;
+ id<MTLCommandBuffer> newCommandBuffer();
MTLRenderPassDescriptor *createDefaultRenderPass(bool hasDepthStencil,
const QColor &colorClearValue,
const QRhiDepthStencilClearValue &depthStencilClearValue,
@@ -125,6 +178,11 @@ struct QRhiMetalData
id<MTLLibrary> createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
id<MTLFunction> createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint);
+ bool setupBinaryArchive(NSURL *sourceFileUrl = nil);
+ void addRenderPipelineToBinaryArchive(MTLRenderPipelineDescriptor *rpDesc);
+ void trySeedingRenderPipelineFromBinaryArchive(MTLRenderPipelineDescriptor *rpDesc);
+ void addComputePipelineToBinaryArchive(MTLComputePipelineDescriptor *cpDesc);
+ void trySeedingComputePipelineFromBinaryArchive(MTLComputePipelineDescriptor *cpDesc);
struct DeferredReleaseEntry {
enum Type {
@@ -132,7 +190,9 @@ struct QRhiMetalData
RenderBuffer,
Texture,
Sampler,
- StagingBuffer
+ StagingBuffer,
+ GraphicsPipeline,
+ ComputePipeline
};
Type type;
int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1
@@ -154,6 +214,15 @@ struct QRhiMetalData
struct {
id<MTLBuffer> buffer;
} stagingBuffer;
+ struct {
+ id<MTLRenderPipelineState> pipelineState;
+ id<MTLDepthStencilState> depthStencilState;
+ std::array<id<MTLComputePipelineState>, 3> tessVertexComputeState;
+ id<MTLComputePipelineState> tessTessControlComputeState;
+ } graphicsPipeline;
+ struct {
+ id<MTLComputePipelineState> pipelineState;
+ } computePipeline;
};
};
QVector<DeferredReleaseEntry> releaseQueue;
@@ -161,6 +230,7 @@ struct QRhiMetalData
struct OffscreenFrame {
OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
bool active = false;
+ double lastGpuTime = 0;
QMetalCommandBuffer cbWrapper;
} ofr;
@@ -175,6 +245,17 @@ struct QRhiMetalData
};
QVarLengthArray<TextureReadback, 2> activeTextureReadbacks;
+ struct BufferReadback
+ {
+ int activeFrameSlot = -1;
+ QRhiReadbackResult *result;
+ quint32 offset;
+ quint32 readSize;
+ id<MTLBuffer> buf;
+ };
+
+ QVarLengthArray<BufferReadback, 2> activeBufferReadbacks;
+
MTLCaptureManager *captureMgr;
id<MTLCaptureScope> captureScope = nil;
@@ -192,7 +273,7 @@ struct QMetalBufferData
bool slotted;
id<MTLBuffer> buf[QMTL_FRAMES_IN_FLIGHT];
struct BufferUpdate {
- int offset;
+ quint32 offset;
QRhiBufferData data;
};
QVarLengthArray<BufferUpdate, 16> pendingUpdates[QMTL_FRAMES_IN_FLIGHT];
@@ -223,15 +304,45 @@ struct QMetalSamplerData
id<MTLSamplerState> samplerState = nil;
};
+struct QMetalShaderResourceBindingsData {
+ struct Stage {
+ struct Buffer {
+ int nativeBinding;
+ id<MTLBuffer> mtlbuf;
+ quint32 offset;
+ };
+ struct Texture {
+ int nativeBinding;
+ id<MTLTexture> mtltex;
+ };
+ struct Sampler {
+ int nativeBinding;
+ id<MTLSamplerState> mtlsampler;
+ };
+ QVarLengthArray<Buffer, 8> buffers;
+ QVarLengthArray<Texture, 8> textures;
+ QVarLengthArray<Sampler, 8> samplers;
+ QRhiBatchedBindings<id<MTLBuffer> > bufferBatches;
+ QRhiBatchedBindings<NSUInteger> bufferOffsetBatches;
+ QRhiBatchedBindings<id<MTLTexture> > textureBatches;
+ QRhiBatchedBindings<id<MTLSamplerState> > samplerBatches;
+ } res[QRhiMetal::SUPPORTED_STAGES];
+ enum { VERTEX = 0, FRAGMENT = 1, COMPUTE = 2, TESSCTRL = 3, TESSEVAL = 4 };
+};
+
struct QMetalCommandBufferData
{
id<MTLCommandBuffer> cb;
+ double lastGpuTime = 0;
id<MTLRenderCommandEncoder> currentRenderPassEncoder;
id<MTLComputeCommandEncoder> currentComputePassEncoder;
+ id<MTLComputeCommandEncoder> tessellationComputeEncoder;
MTLRenderPassDescriptor *currentPassRpDesc;
int currentFirstVertexBinding;
QRhiBatchedBindings<id<MTLBuffer> > currentVertexInputsBuffers;
QRhiBatchedBindings<NSUInteger> currentVertexInputOffsets;
+ id<MTLDepthStencilState> currentDepthStencilState;
+ QMetalShaderResourceBindingsData currentShaderResourceBindingState;
};
struct QMetalRenderTargetData
@@ -257,8 +368,11 @@ struct QMetalRenderTargetData
struct {
ColorAtt colorAtt[QMetalRenderPassDescriptor::MAX_COLOR_ATTACHMENTS];
id<MTLTexture> dsTex = nil;
+ id<MTLTexture> dsResolveTex = nil;
bool hasStencil = false;
bool depthNeedsStore = false;
+ bool preserveColor = false;
+ bool preserveDs = false;
} fb;
QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList;
@@ -266,6 +380,7 @@ struct QMetalRenderTargetData
struct QMetalGraphicsPipelineData
{
+ QMetalGraphicsPipeline *q = nullptr;
id<MTLRenderPipelineState> ps = nil;
id<MTLDepthStencilState> ds = nil;
MTLPrimitiveType primitiveType;
@@ -276,6 +391,54 @@ struct QMetalGraphicsPipelineData
float slopeScaledDepthBias;
QMetalShader vs;
QMetalShader fs;
+ struct ExtraBufferManager {
+ enum class WorkBufType {
+ DeviceLocal,
+ HostVisible
+ };
+ QMetalBuffer *acquireWorkBuffer(QRhiMetal *rhiD, quint32 size, WorkBufType type = WorkBufType::DeviceLocal);
+ QVector<QMetalBuffer *> deviceLocalWorkBuffers;
+ QVector<QMetalBuffer *> hostVisibleWorkBuffers;
+ } extraBufMgr;
+ struct Tessellation {
+ QMetalGraphicsPipelineData *q = nullptr;
+ bool enabled = false;
+ bool failed = false;
+ uint inControlPointCount;
+ uint outControlPointCount;
+ QMetalShader compVs[3];
+ std::array<id<MTLComputePipelineState>, 3> vertexComputeState = {};
+ id<MTLComputePipelineState> tessControlComputeState = nil;
+ QMetalShader compTesc;
+ QMetalShader vertTese;
+ quint32 vsCompOutputBufferSize(quint32 vertexOrIndexCount, quint32 instanceCount) const
+ {
+ // max vertex output components = resourceLimit(MaxVertexOutputs) * 4 = 60
+ return vertexOrIndexCount * instanceCount * sizeof(float) * 60;
+ }
+ quint32 tescCompOutputBufferSize(quint32 patchCount) const
+ {
+ return outControlPointCount * patchCount * sizeof(float) * 60;
+ }
+ quint32 tescCompPatchOutputBufferSize(quint32 patchCount) const
+ {
+ // assume maxTessellationControlPerPatchOutputComponents is 128
+ return patchCount * sizeof(float) * 128;
+ }
+ quint32 patchCountForDrawCall(quint32 vertexOrIndexCount, quint32 instanceCount) const
+ {
+ return ((vertexOrIndexCount + inControlPointCount - 1) / inControlPointCount) * instanceCount;
+ }
+ static int vsCompVariantToIndex(QShader::Variant vertexCompVariant);
+ id<MTLComputePipelineState> vsCompPipeline(QRhiMetal *rhiD, QShader::Variant vertexCompVariant);
+ id<MTLComputePipelineState> tescCompPipeline(QRhiMetal *rhiD);
+ id<MTLRenderPipelineState> teseFragRenderPipeline(QRhiMetal *rhiD, QMetalGraphicsPipeline *pipeline);
+ } tess;
+ void setupVertexInputDescriptor(MTLVertexDescriptor *desc);
+ void setupStageInputDescriptor(MTLStageInputOutputDescriptor *desc);
+
+ // SPIRV-Cross buffer size buffers
+ QMetalBuffer *bufferSizeBuffer = nullptr;
};
struct QMetalComputePipelineData
@@ -283,6 +446,9 @@ struct QMetalComputePipelineData
id<MTLComputePipelineState> ps = nil;
QMetalShader cs;
MTLSize localSize;
+
+ // SPIRV-Cross buffer size buffers
+ QMetalBuffer *bufferSizeBuffer = nullptr;
};
struct QMetalSwapChainData
@@ -290,10 +456,16 @@ struct QMetalSwapChainData
CAMetalLayer *layer = nullptr;
id<CAMetalDrawable> curDrawable = nil;
dispatch_semaphore_t sem[QMTL_FRAMES_IN_FLIGHT];
+ double lastGpuTime[QMTL_FRAMES_IN_FLIGHT];
MTLRenderPassDescriptor *rp = nullptr;
id<MTLTexture> msaaTex[QMTL_FRAMES_IN_FLIGHT];
QRhiTexture::Format rhiColorFormat;
MTLPixelFormat colorFormat;
+#ifdef Q_OS_MACOS
+ bool liveResizeObserverSet = false;
+ QMacNotificationObserver liveResizeStartObserver;
+ QMacNotificationObserver liveResizeEndObserver;
+#endif
};
QRhiMetal::QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *importDevice)
@@ -304,7 +476,7 @@ QRhiMetal::QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *import
importedDevice = importDevice != nullptr;
if (importedDevice) {
- if (d->dev) {
+ if (importDevice->dev) {
d->dev = (id<MTLDevice>) importDevice->dev;
importedCmdQueue = importDevice->cmdQueue != nullptr;
if (importedCmdQueue)
@@ -338,9 +510,44 @@ bool QRhiMetal::probe(QRhiMetalInitParams *params)
return false;
}
+id<MTLCommandBuffer> QRhiMetalData::newCommandBuffer()
+{
+#ifdef QRHI_METAL_COMMAND_BUFFERS_WITH_UNRETAINED_REFERENCES
+ // 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).
+ return [cmdQueue commandBufferWithUnretainedReferences];
+#else
+ return [cmdQueue commandBuffer];
+#endif
+}
+
+bool QRhiMetalData::setupBinaryArchive(NSURL *sourceFileUrl)
+{
+#ifdef QRHI_METAL_DISABLE_BINARY_ARCHIVE
+ return false;
+#endif
+
+ if (@available(macOS 11.0, iOS 14.0, *)) {
+ [binArch release];
+ MTLBinaryArchiveDescriptor *binArchDesc = [MTLBinaryArchiveDescriptor new];
+ binArchDesc.url = sourceFileUrl;
+ NSError *err = nil;
+ binArch = [dev newBinaryArchiveWithDescriptor: binArchDesc error: &err];
+ [binArchDesc release];
+ if (!binArch) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qWarning("newBinaryArchiveWithDescriptor failed: %s", qPrintable(msg));
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
bool QRhiMetal::create(QRhi::Flags flags)
{
- Q_UNUSED(flags);
+ rhiFlags = flags;
if (importedDevice)
[d->dev retain];
@@ -355,10 +562,12 @@ bool QRhiMetal::create(QRhi::Flags flags)
const QString deviceName = QString::fromNSString([d->dev name]);
qCDebug(QRHI_LOG_INFO, "Metal device: %s", qPrintable(deviceName));
driverInfoStruct.deviceName = deviceName.toUtf8();
- driverInfoStruct.deviceId = [d->dev registryID];
-#ifdef Q_OS_IOS
- driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
-#else
+
+ // deviceId and vendorId stay unset for now. Note that registryID is not
+ // suitable as deviceId because it does not seem stable on macOS and can
+ // apparently change when the system is rebooted.
+
+#ifdef Q_OS_MACOS
if (@available(macOS 10.15, *)) {
const MTLDeviceLocation deviceLocation = [d->dev location];
switch (deviceLocation) {
@@ -375,8 +584,14 @@ bool QRhiMetal::create(QRhi::Flags flags)
break;
}
}
+#else
+ driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
#endif
+ const QOperatingSystemVersion ver = QOperatingSystemVersion::current();
+ osMajor = ver.majorVersion();
+ osMinor = ver.minorVersion();
+
if (importedCmdQueue)
[d->cmdQueue retain];
else
@@ -393,12 +608,17 @@ bool QRhiMetal::create(QRhi::Flags flags)
#if defined(Q_OS_MACOS)
caps.maxTextureSize = 16384;
caps.baseVertexAndInstance = true;
+ if (@available(macOS 10.15, *))
+ caps.isAppleGPU = [d->dev supportsFamily:MTLGPUFamilyApple7];
+ caps.maxThreadGroupSize = 1024;
+ caps.multiView = true;
#elif defined(Q_OS_TVOS)
if ([d->dev supportsFeatureSet: MTLFeatureSet(30003)]) // MTLFeatureSet_tvOS_GPUFamily2_v1
caps.maxTextureSize = 16384;
else
caps.maxTextureSize = 8192;
caps.baseVertexAndInstance = false;
+ caps.isAppleGPU = true;
#elif defined(Q_OS_IOS)
// welcome to feature set hell
if ([d->dev supportsFeatureSet: MTLFeatureSet(16)] // MTLFeatureSet_iOS_GPUFamily5_v1
@@ -416,6 +636,13 @@ bool QRhiMetal::create(QRhi::Flags flags)
caps.maxTextureSize = 4096;
caps.baseVertexAndInstance = false;
}
+ caps.isAppleGPU = true;
+ if (@available(iOS 13, *)) {
+ if ([d->dev supportsFamily: MTLGPUFamilyApple4])
+ caps.maxThreadGroupSize = 1024;
+ if ([d->dev supportsFamily: MTLGPUFamilyApple5])
+ caps.multiView = true;
+ }
#endif
caps.supportedSampleCounts = { 1 };
@@ -424,6 +651,9 @@ bool QRhiMetal::create(QRhi::Flags flags)
caps.supportedSampleCounts.append(sampleCount);
}
+ if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
+ d->setupBinaryArchive();
+
nativeHandlesStruct.dev = (MTLDevice *) d->dev;
nativeHandlesStruct.cmdQueue = (MTLCommandQueue *) d->cmdQueue;
@@ -442,6 +672,11 @@ void QRhiMetal::destroy()
[d->captureScope release];
d->captureScope = nil;
+ if (@available(macOS 11.0, iOS 14.0, *)) {
+ [d->binArch release];
+ d->binArch = nil;
+ }
+
[d->cmdQueue release];
if (!importedCmdQueue)
d->cmdQueue = nil;
@@ -456,23 +691,12 @@ QVector<int> QRhiMetal::supportedSampleCounts() const
return caps.supportedSampleCounts;
}
-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)
+QRhiBuffer *QRhiMetal::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
{
return new QMetalBuffer(this, type, usage, size);
}
@@ -515,16 +739,32 @@ bool QRhiMetal::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture
{
Q_UNUSED(flags);
+ bool supportsFamilyMac2 = false; // needed for BC* formats
+ bool supportsFamilyApple3 = false;
+
#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;
+ supportsFamilyMac2 = true;
+ if (caps.isAppleGPU)
+ supportsFamilyApple3 = true;
#else
- if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
- return false;
+ supportsFamilyApple3 = true;
#endif
+ // BC5 is not available for any Apple hardare
+ if (format == QRhiTexture::BC5)
+ return false;
+
+ if (!supportsFamilyApple3) {
+ if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8)
+ return false;
+ if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12)
+ return false;
+ }
+
+ if (!supportsFamilyMac2)
+ if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
+ return false;
+
return true;
}
@@ -538,7 +778,7 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
case QRhi::DebugMarkers:
return true;
case QRhi::Timestamps:
- return false;
+ return true;
case QRhi::Instancing:
return true;
case QRhi::CustomInstanceStepRate:
@@ -582,7 +822,12 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
case QRhi::ReadBackAnyTextureFormat:
return true;
case QRhi::PipelineCacheDataLoadSave:
- return false;
+ {
+ if (@available(macOS 11.0, iOS 14.0, *))
+ return true;
+ else
+ return false;
+ }
case QRhi::ImageDataStride:
return true;
case QRhi::RenderBufferImport:
@@ -594,13 +839,29 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
case QRhi::TextureArrays:
return true;
case QRhi::Tessellation:
- return false;
+ return true;
case QRhi::GeometryShader:
return false;
case QRhi::TextureArrayRange:
return false;
case QRhi::NonFillPolygonMode:
return true;
+ case QRhi::OneDimensionalTextures:
+ return true;
+ case QRhi::OneDimensionalTextureMipmaps:
+ return false;
+ case QRhi::HalfAttributes:
+ return true;
+ case QRhi::RenderToOneDimensionalTexture:
+ return false;
+ case QRhi::ThreeDimensionalTextureMipmaps:
+ return true;
+ case QRhi::MultiView:
+ return caps.multiView;
+ case QRhi::TextureViewFormat:
+ return false;
+ case QRhi::ResolveDepthStencil:
+ return true;
default:
Q_UNREACHABLE();
return false;
@@ -629,11 +890,7 @@ int QRhiMetal::resourceLimit(QRhi::ResourceLimit limit) const
case QRhi::MaxThreadGroupY:
Q_FALLTHROUGH();
case QRhi::MaxThreadGroupZ:
-#if defined(Q_OS_MACOS)
- return 1024;
-#else
- return 512;
-#endif
+ return caps.maxThreadGroupSize;
case QRhi::TextureArraySizeMax:
return 2048;
case QRhi::MaxUniformBufferRange:
@@ -658,9 +915,11 @@ QRhiDriverInfo QRhiMetal::driverInfo() const
return driverInfoStruct;
}
-QRhiMemAllocStats QRhiMetal::graphicsMemoryAllocationStatistics()
+QRhiStats QRhiMetal::statistics()
{
- return {};
+ QRhiStats result;
+ result.totalPipelineCreationTime = totalPipelineCreationTime();
+ return result;
}
bool QRhiMetal::makeThreadLocalNativeContextCurrent()
@@ -682,14 +941,133 @@ bool QRhiMetal::isDeviceLost() const
return false;
}
+struct QMetalPipelineCacheDataHeader
+{
+ quint32 rhiId;
+ quint32 arch;
+ quint32 dataSize;
+ quint32 osMajor;
+ quint32 osMinor;
+ char driver[236];
+};
+
QByteArray QRhiMetal::pipelineCacheData()
{
- return QByteArray();
+ Q_STATIC_ASSERT(sizeof(QMetalPipelineCacheDataHeader) == 256);
+ QByteArray data;
+ if (@available(macOS 11.0, iOS 14.0, *)) {
+ if (!d->binArch || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
+ return data;
+
+ QTemporaryFile tmp;
+ if (!tmp.open()) {
+ qCDebug(QRHI_LOG_INFO, "pipelineCacheData: Failed to create temporary file for Metal");
+ return data;
+ }
+ tmp.close(); // the file exists until the tmp dtor runs
+
+ const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
+ NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
+ NSError *err = nil;
+ if (![d->binArch serializeToURL: url error: &err]) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ // Some of these "errors" are not actual errors. (think of "Nothing to serialize")
+ qCDebug(QRHI_LOG_INFO, "Failed to serialize MTLBinaryArchive: %s", qPrintable(msg));
+ return data;
+ }
+
+ QFile f(fn);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qCDebug(QRHI_LOG_INFO, "pipelineCacheData: Failed to reopen temporary file");
+ return data;
+ }
+ const QByteArray blob = f.readAll();
+ f.close();
+
+ const size_t headerSize = sizeof(QMetalPipelineCacheDataHeader);
+ const quint32 dataSize = quint32(blob.size());
+
+ data.resize(headerSize + dataSize);
+
+ QMetalPipelineCacheDataHeader header = {};
+ header.rhiId = pipelineCacheRhiId();
+ header.arch = quint32(sizeof(void*));
+ header.dataSize = quint32(dataSize);
+ header.osMajor = osMajor;
+ header.osMinor = osMinor;
+ const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
+ if (driverStrLen)
+ memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
+ header.driver[driverStrLen] = '\0';
+
+ memcpy(data.data(), &header, headerSize);
+ memcpy(data.data() + headerSize, blob.constData(), dataSize);
+ }
+ return data;
}
void QRhiMetal::setPipelineCacheData(const QByteArray &data)
{
- Q_UNUSED(data);
+ if (data.isEmpty())
+ return;
+
+ const size_t headerSize = sizeof(QMetalPipelineCacheDataHeader);
+ if (data.size() < qsizetype(headerSize)) {
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (header incomplete)");
+ return;
+ }
+
+ const size_t dataOffset = headerSize;
+ QMetalPipelineCacheDataHeader header;
+ memcpy(&header, data.constData(), headerSize);
+
+ const quint32 rhiId = pipelineCacheRhiId();
+ if (header.rhiId != rhiId) {
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
+ rhiId, header.rhiId);
+ return;
+ }
+
+ const quint32 arch = quint32(sizeof(void*));
+ if (header.arch != arch) {
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
+ arch, header.arch);
+ return;
+ }
+
+ if (header.osMajor != osMajor || header.osMinor != osMinor) {
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: OS version does not match (%u.%u, %u.%u)",
+ osMajor, osMinor, header.osMajor, header.osMinor);
+ return;
+ }
+
+ const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
+ if (strncmp(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Metal device name does not match");
+ return;
+ }
+
+ if (data.size() < qsizetype(dataOffset + header.dataSize)) {
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (data incomplete)");
+ return;
+ }
+
+ if (@available(macOS 11.0, iOS 14.0, *)) {
+ const char *p = data.constData() + dataOffset;
+
+ QTemporaryFile tmp;
+ if (!tmp.open()) {
+ qCDebug(QRHI_LOG_INFO, "pipelineCacheData: Failed to create temporary file for Metal");
+ return;
+ }
+ tmp.write(p, header.dataSize);
+ tmp.close(); // the file exists until the tmp dtor runs
+
+ const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
+ NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
+ if (d->setupBinaryArchive(url))
+ qCDebug(QRHI_LOG_INFO, "Created MTLBinaryArchive with initial data of %u bytes", header.dataSize);
+ }
}
QRhiRenderBuffer *QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
@@ -759,6 +1137,136 @@ static inline int mapBinding(int binding,
return -1;
}
+static inline void bindStageBuffers(QMetalCommandBuffer *cbD,
+ int stage,
+ const QRhiBatchedBindings<id<MTLBuffer>>::Batch &bufferBatch,
+ const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch)
+{
+ switch (stage) {
+ case QMetalShaderResourceBindingsData::VERTEX:
+ [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
+ offsets: offsetBatch.resources.constData()
+ withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
+ break;
+ case QMetalShaderResourceBindingsData::FRAGMENT:
+ [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
+ offsets: offsetBatch.resources.constData()
+ withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
+ break;
+ case QMetalShaderResourceBindingsData::COMPUTE:
+ [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
+ offsets: offsetBatch.resources.constData()
+ withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
+ break;
+ case QMetalShaderResourceBindingsData::TESSCTRL:
+ case QMetalShaderResourceBindingsData::TESSEVAL:
+ // do nothing. These are used later for tessellation
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+static inline void bindStageTextures(QMetalCommandBuffer *cbD,
+ int stage,
+ const QRhiBatchedBindings<id<MTLTexture>>::Batch &textureBatch)
+{
+ switch (stage) {
+ case QMetalShaderResourceBindingsData::VERTEX:
+ [cbD->d->currentRenderPassEncoder setVertexTextures: textureBatch.resources.constData()
+ withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
+ break;
+ case QMetalShaderResourceBindingsData::FRAGMENT:
+ [cbD->d->currentRenderPassEncoder setFragmentTextures: textureBatch.resources.constData()
+ withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
+ break;
+ case QMetalShaderResourceBindingsData::COMPUTE:
+ [cbD->d->currentComputePassEncoder setTextures: textureBatch.resources.constData()
+ withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
+ break;
+ case QMetalShaderResourceBindingsData::TESSCTRL:
+ case QMetalShaderResourceBindingsData::TESSEVAL:
+ // do nothing. These are used later for tessellation
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+static inline void bindStageSamplers(QMetalCommandBuffer *cbD,
+ int encoderStage,
+ const QRhiBatchedBindings<id<MTLSamplerState>>::Batch &samplerBatch)
+{
+ switch (encoderStage) {
+ case QMetalShaderResourceBindingsData::VERTEX:
+ [cbD->d->currentRenderPassEncoder setVertexSamplerStates: samplerBatch.resources.constData()
+ withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
+ break;
+ case QMetalShaderResourceBindingsData::FRAGMENT:
+ [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: samplerBatch.resources.constData()
+ withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
+ break;
+ case QMetalShaderResourceBindingsData::COMPUTE:
+ [cbD->d->currentComputePassEncoder setSamplerStates: samplerBatch.resources.constData()
+ withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
+ break;
+ case QMetalShaderResourceBindingsData::TESSCTRL:
+ case QMetalShaderResourceBindingsData::TESSEVAL:
+ // do nothing. These are used later for tessellation
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+// Helper that is not used during the common vertex+fragment and compute
+// pipelines, but is necessary when tessellation is involved and so the
+// graphics pipeline is under the hood a combination of multiple compute and
+// render pipelines. We need to be able to set the buffers, textures, samplers
+// when a switching between render and compute encoders.
+static inline void rebindShaderResources(QMetalCommandBuffer *cbD, int resourceStage, int encoderStage,
+ const QMetalShaderResourceBindingsData *customBindingState = nullptr)
+{
+ const QMetalShaderResourceBindingsData *bindingData = customBindingState ? customBindingState : &cbD->d->currentShaderResourceBindingState;
+
+ for (int i = 0, ie = bindingData->res[resourceStage].bufferBatches.batches.count(); i != ie; ++i) {
+ const auto &bufferBatch(bindingData->res[resourceStage].bufferBatches.batches[i]);
+ const auto &offsetBatch(bindingData->res[resourceStage].bufferOffsetBatches.batches[i]);
+ bindStageBuffers(cbD, encoderStage, bufferBatch, offsetBatch);
+ }
+
+ for (int i = 0, ie = bindingData->res[resourceStage].textureBatches.batches.count(); i != ie; ++i) {
+ const auto &batch(bindingData->res[resourceStage].textureBatches.batches[i]);
+ bindStageTextures(cbD, encoderStage, batch);
+ }
+
+ for (int i = 0, ie = bindingData->res[resourceStage].samplerBatches.batches.count(); i != ie; ++i) {
+ const auto &batch(bindingData->res[resourceStage].samplerBatches.batches[i]);
+ bindStageSamplers(cbD, encoderStage, batch);
+ }
+}
+
+static inline QRhiShaderResourceBinding::StageFlag toRhiSrbStage(int stage)
+{
+ switch (stage) {
+ case QMetalShaderResourceBindingsData::VERTEX:
+ return QRhiShaderResourceBinding::StageFlag::VertexStage;
+ case QMetalShaderResourceBindingsData::TESSCTRL:
+ return QRhiShaderResourceBinding::StageFlag::TessellationControlStage;
+ case QMetalShaderResourceBindingsData::TESSEVAL:
+ return QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage;
+ case QMetalShaderResourceBindingsData::FRAGMENT:
+ return QRhiShaderResourceBinding::StageFlag::FragmentStage;
+ case QMetalShaderResourceBindingsData::COMPUTE:
+ return QRhiShaderResourceBinding::StageFlag::ComputeStage;
+ }
+
+ Q_UNREACHABLE_RETURN(QRhiShaderResourceBinding::StageFlag::VertexStage);
+}
+
void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD,
QMetalCommandBuffer *cbD,
int dynamicOffsetCount,
@@ -766,38 +1274,16 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
bool offsetOnlyChange,
const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES])
{
- struct Stage {
- struct Buffer {
- int nativeBinding;
- id<MTLBuffer> mtlbuf;
- uint offset;
- };
- struct Texture {
- int nativeBinding;
- id<MTLTexture> mtltex;
- };
- struct Sampler {
- int nativeBinding;
- id<MTLSamplerState> mtlsampler;
- };
- QVarLengthArray<Buffer, 8> buffers;
- QVarLengthArray<Texture, 8> textures;
- QVarLengthArray<Sampler, 8> samplers;
- QRhiBatchedBindings<id<MTLBuffer> > bufferBatches;
- QRhiBatchedBindings<NSUInteger> bufferOffsetBatches;
- QRhiBatchedBindings<id<MTLTexture> > textureBatches;
- QRhiBatchedBindings<id<MTLSamplerState> > samplerBatches;
- } res[SUPPORTED_STAGES];
- enum { VERTEX = 0, FRAGMENT = 1, COMPUTE = 2 };
+ QMetalShaderResourceBindingsData bindingData;
- for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
- const QRhiShaderResourceBinding::Data *b = binding.data();
+ for (const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
+ const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(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 = uint(b->u.ubuf.offset);
+ quint32 offset = b->u.ubuf.offset;
for (int i = 0; i < dynamicOffsetCount; ++i) {
const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
if (dynOfs.first == b->binding) {
@@ -805,20 +1291,13 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
break;
}
}
- if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
- const int nativeBinding = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer);
- if (nativeBinding >= 0)
- res[VERTEX].buffers.append({ nativeBinding, mtlbuf, offset });
- }
- if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
- const int nativeBinding = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer);
- if (nativeBinding >= 0)
- res[FRAGMENT].buffers.append({ nativeBinding, mtlbuf, offset });
- }
- if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
- const int nativeBinding = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer);
- if (nativeBinding >= 0)
- res[COMPUTE].buffers.append({ nativeBinding, mtlbuf, offset });
+
+ for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
+ if (b->stage.testFlag(toRhiSrbStage(stage))) {
+ const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
+ if (nativeBinding >= 0)
+ bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
+ }
}
}
break;
@@ -830,36 +1309,21 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
for (int elem = 0; elem < data->count; ++elem) {
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.texSamplers[elem].tex);
QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.texSamplers[elem].sampler);
- if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
- // Must handle all three cases (combined, separate, separate):
- // first = texture binding, second = sampler binding
- // first = texture binding
- // first = sampler binding (i.e. BindingType::Texture...)
- const int textureBinding = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture);
- const int samplerBinding = texD && samplerD ? mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler)
- : (samplerD ? mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture) : -1);
- if (textureBinding >= 0 && texD)
- res[VERTEX].textures.append({ textureBinding + elem, texD->d->tex });
- if (samplerBinding >= 0)
- res[VERTEX].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
- }
- if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
- const int textureBinding = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture);
- const int samplerBinding = texD && samplerD ? mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler)
- : (samplerD ? mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture) : -1);
- if (textureBinding >= 0 && texD)
- res[FRAGMENT].textures.append({ textureBinding + elem, texD->d->tex });
- if (samplerBinding >= 0)
- res[FRAGMENT].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
- }
- if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
- const int textureBinding = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture);
- const int samplerBinding = texD && samplerD ? mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler)
- : (samplerD ? mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture) : -1);
- if (textureBinding >= 0 && texD)
- res[COMPUTE].textures.append({ textureBinding + elem, texD->d->tex });
- if (samplerBinding >= 0)
- res[COMPUTE].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
+
+ for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
+ if (b->stage.testFlag(toRhiSrbStage(stage))) {
+ // Must handle all three cases (combined, separate, separate):
+ // first = texture binding, second = sampler binding
+ // first = texture binding
+ // first = sampler binding (i.e. BindingType::Texture...)
+ const int textureBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
+ const int samplerBinding = texD && samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Sampler)
+ : (samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture) : -1);
+ if (textureBinding >= 0 && texD)
+ bindingData.res[stage].textures.append({ textureBinding + elem, texD->d->tex });
+ if (samplerBinding >= 0)
+ bindingData.res[stage].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
+ }
}
}
}
@@ -870,20 +1334,13 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
{
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)) {
- const int nativeBinding = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture);
- if (nativeBinding >= 0)
- res[VERTEX].textures.append({ nativeBinding, t });
- }
- if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
- const int nativeBinding = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture);
- if (nativeBinding >= 0)
- res[FRAGMENT].textures.append({ nativeBinding, t });
- }
- if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
- const int nativeBinding = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture);
- if (nativeBinding >= 0)
- res[COMPUTE].textures.append({ nativeBinding, t });
+
+ for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
+ if (b->stage.testFlag(toRhiSrbStage(stage))) {
+ const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
+ if (nativeBinding >= 0)
+ bindingData.res[stage].textures.append({ nativeBinding, t });
+ }
}
}
break;
@@ -893,21 +1350,13 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
{
QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
id<MTLBuffer> mtlbuf = bufD->d->buf[0];
- uint offset = uint(b->u.sbuf.offset);
- if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
- const int nativeBinding = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer);
- if (nativeBinding >= 0)
- res[VERTEX].buffers.append({ nativeBinding, mtlbuf, offset });
- }
- if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
- const int nativeBinding = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer);
- if (nativeBinding >= 0)
- res[FRAGMENT].buffers.append({ nativeBinding, mtlbuf, offset });
- }
- if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
- const int nativeBinding = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer);
- if (nativeBinding >= 0)
- res[COMPUTE].buffers.append({ nativeBinding, mtlbuf, offset });
+ quint32 offset = b->u.sbuf.offset;
+ for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
+ if (b->stage.testFlag(toRhiSrbStage(stage))) {
+ const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
+ if (nativeBinding >= 0)
+ bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
+ }
}
}
break;
@@ -918,9 +1367,10 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
}
for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
- if (cbD->recordingPass != QMetalCommandBuffer::RenderPass && (stage == VERTEX || stage == FRAGMENT))
+ if (cbD->recordingPass != QMetalCommandBuffer::RenderPass && (stage == QMetalShaderResourceBindingsData::VERTEX || stage == QMetalShaderResourceBindingsData::FRAGMENT
+ || stage == QMetalShaderResourceBindingsData::TESSCTRL || stage == QMetalShaderResourceBindingsData::TESSEVAL))
continue;
- if (cbD->recordingPass != QMetalCommandBuffer::ComputePass && stage == COMPUTE)
+ if (cbD->recordingPass != QMetalCommandBuffer::ComputePass && (stage == QMetalShaderResourceBindingsData::COMPUTE))
continue;
// QRhiBatchedBindings works with the native bindings and expects
@@ -928,104 +1378,107 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
// on the QRhi (SPIR-V) binding) is not helpful in this regard, so we
// have to sort here every time.
- std::sort(res[stage].buffers.begin(), res[stage].buffers.end(), [](const Stage::Buffer &a, const Stage::Buffer &b) {
+ std::sort(bindingData.res[stage].buffers.begin(), bindingData.res[stage].buffers.end(), [](const QMetalShaderResourceBindingsData::Stage::Buffer &a, const QMetalShaderResourceBindingsData::Stage::Buffer &b) {
return a.nativeBinding < b.nativeBinding;
});
- for (const Stage::Buffer &buf : qAsConst(res[stage].buffers)) {
- res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
- res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
+ for (const QMetalShaderResourceBindingsData::Stage::Buffer &buf : std::as_const(bindingData.res[stage].buffers)) {
+ bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
+ bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
}
- res[stage].bufferBatches.finish();
- res[stage].bufferOffsetBatches.finish();
-
- for (int i = 0, ie = res[stage].bufferBatches.batches.count(); i != ie; ++i) {
- const auto &bufferBatch(res[stage].bufferBatches.batches[i]);
- const auto &offsetBatch(res[stage].bufferOffsetBatches.batches[i]);
- switch (stage) {
- case VERTEX:
- [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
- offsets: offsetBatch.resources.constData()
- withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
- break;
- case FRAGMENT:
- [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
- offsets: offsetBatch.resources.constData()
- withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
- break;
- case COMPUTE:
- [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
- offsets: offsetBatch.resources.constData()
- withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
- break;
- default:
- Q_UNREACHABLE();
- break;
+ bindingData.res[stage].bufferBatches.finish();
+ bindingData.res[stage].bufferOffsetBatches.finish();
+
+ for (int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
+ const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
+ const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
+ // skip setting Buffer binding if the current state is already correct
+ if (cbD->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches.count() > i
+ && cbD->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches.count() > i
+ && bufferBatch == cbD->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i]
+ && offsetBatch == cbD->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])
+ {
+ continue;
}
+ bindStageBuffers(cbD, stage, bufferBatch, offsetBatch);
}
if (offsetOnlyChange)
continue;
- std::sort(res[stage].textures.begin(), res[stage].textures.end(), [](const Stage::Texture &a, const Stage::Texture &b) {
+ std::sort(bindingData.res[stage].textures.begin(), bindingData.res[stage].textures.end(), [](const QMetalShaderResourceBindingsData::Stage::Texture &a, const QMetalShaderResourceBindingsData::Stage::Texture &b) {
return a.nativeBinding < b.nativeBinding;
});
- std::sort(res[stage].samplers.begin(), res[stage].samplers.end(), [](const Stage::Sampler &a, const Stage::Sampler &b) {
+ std::sort(bindingData.res[stage].samplers.begin(), bindingData.res[stage].samplers.end(), [](const QMetalShaderResourceBindingsData::Stage::Sampler &a, const QMetalShaderResourceBindingsData::Stage::Sampler &b) {
return a.nativeBinding < b.nativeBinding;
});
- for (const Stage::Texture &t : qAsConst(res[stage].textures))
- res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
+ for (const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(bindingData.res[stage].textures))
+ bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
- for (const Stage::Sampler &s : qAsConst(res[stage].samplers))
- res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
+ for (const QMetalShaderResourceBindingsData::Stage::Sampler &s : std::as_const(bindingData.res[stage].samplers))
+ bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
- res[stage].textureBatches.finish();
- res[stage].samplerBatches.finish();
+ bindingData.res[stage].textureBatches.finish();
+ bindingData.res[stage].samplerBatches.finish();
- for (int i = 0, ie = res[stage].textureBatches.batches.count(); i != ie; ++i) {
- const auto &batch(res[stage].textureBatches.batches[i]);
- switch (stage) {
- case VERTEX:
- [cbD->d->currentRenderPassEncoder setVertexTextures: batch.resources.constData()
- withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
- break;
- case FRAGMENT:
- [cbD->d->currentRenderPassEncoder setFragmentTextures: batch.resources.constData()
- withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
- break;
- case COMPUTE:
- [cbD->d->currentComputePassEncoder setTextures: batch.resources.constData()
- withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
- break;
- default:
- Q_UNREACHABLE();
- break;
+ for (int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
+ const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
+ // skip setting Texture binding if the current state is already correct
+ if (cbD->d->currentShaderResourceBindingState.res[stage].textureBatches.batches.count() > i
+ && batch == cbD->d->currentShaderResourceBindingState.res[stage].textureBatches.batches[i])
+ {
+ continue;
}
+ bindStageTextures(cbD, stage, batch);
}
- for (int i = 0, ie = res[stage].samplerBatches.batches.count(); i != ie; ++i) {
- const auto &batch(res[stage].samplerBatches.batches[i]);
- switch (stage) {
- case VERTEX:
- [cbD->d->currentRenderPassEncoder setVertexSamplerStates: batch.resources.constData()
- withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
- break;
- case FRAGMENT:
- [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: batch.resources.constData()
- withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
- break;
- case COMPUTE:
- [cbD->d->currentComputePassEncoder setSamplerStates: batch.resources.constData()
- withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
- break;
- default:
- Q_UNREACHABLE();
- break;
+
+ for (int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
+ const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
+ // skip setting Sampler State if the current state is already correct
+ if (cbD->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches.count() > i
+ && batch == cbD->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches[i])
+ {
+ continue;
}
+ bindStageSamplers(cbD, stage, batch);
}
}
+
+ cbD->d->currentShaderResourceBindingState = bindingData;
+}
+
+void QMetalGraphicsPipeline::makeActiveForCurrentRenderPassEncoder(QMetalCommandBuffer *cbD)
+{
+ [cbD->d->currentRenderPassEncoder setRenderPipelineState: d->ps];
+
+ if (cbD->d->currentDepthStencilState != d->ds) {
+ [cbD->d->currentRenderPassEncoder setDepthStencilState: d->ds];
+ cbD->d->currentDepthStencilState = d->ds;
+ }
+
+ if (cbD->currentCullMode == -1 || d->cullMode != uint(cbD->currentCullMode)) {
+ [cbD->d->currentRenderPassEncoder setCullMode: d->cullMode];
+ cbD->currentCullMode = int(d->cullMode);
+ }
+ if (cbD->currentTriangleFillMode == -1 || d->triangleFillMode != uint(cbD->currentTriangleFillMode)) {
+ [cbD->d->currentRenderPassEncoder setTriangleFillMode: d->triangleFillMode];
+ cbD->currentTriangleFillMode = int(d->triangleFillMode);
+ }
+ if (cbD->currentFrontFaceWinding == -1 || d->winding != uint(cbD->currentFrontFaceWinding)) {
+ [cbD->d->currentRenderPassEncoder setFrontFacingWinding: d->winding];
+ cbD->currentFrontFaceWinding = int(d->winding);
+ }
+ if (!qFuzzyCompare(d->depthBias, cbD->currentDepthBiasValues.first)
+ || !qFuzzyCompare(d->slopeScaledDepthBias, cbD->currentDepthBiasValues.second))
+ {
+ [cbD->d->currentRenderPassEncoder setDepthBias: d->depthBias
+ slopeScale: d->slopeScaledDepthBias
+ clamp: 0.0f];
+ cbD->currentDepthBiasValues = { d->depthBias, d->slopeScaledDepthBias };
+ }
}
void QRhiMetal::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
@@ -1034,33 +1487,24 @@ void QRhiMetal::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline
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;
+ if (cbD->currentGraphicsPipeline == psD && cbD->currentPipelineGeneration == psD->generation)
+ return;
- [cbD->d->currentRenderPassEncoder setRenderPipelineState: psD->d->ps];
- [cbD->d->currentRenderPassEncoder setDepthStencilState: psD->d->ds];
+ cbD->currentGraphicsPipeline = psD;
+ cbD->currentComputePipeline = nullptr;
+ cbD->currentPipelineGeneration = psD->generation;
- if (cbD->currentCullMode == -1 || psD->d->cullMode != uint(cbD->currentCullMode)) {
- [cbD->d->currentRenderPassEncoder setCullMode: psD->d->cullMode];
- cbD->currentCullMode = int(psD->d->cullMode);
- }
- if (cbD->currentTriangleFillMode == -1 || psD->d->triangleFillMode != uint(cbD->currentTriangleFillMode)) {
- [cbD->d->currentRenderPassEncoder setTriangleFillMode: psD->d->triangleFillMode];
- cbD->currentTriangleFillMode = int(psD->d->triangleFillMode);
- }
- if (cbD->currentFrontFaceWinding == -1 || psD->d->winding != uint(cbD->currentFrontFaceWinding)) {
- [cbD->d->currentRenderPassEncoder setFrontFacingWinding: psD->d->winding];
- cbD->currentFrontFaceWinding = int(psD->d->winding);
+ if (!psD->d->tess.enabled && !psD->d->tess.failed) {
+ psD->makeActiveForCurrentRenderPassEncoder(cbD);
+ } else {
+ // mark work buffers that can now be safely reused as reusable
+ for (QMetalBuffer *workBuf : psD->d->extraBufMgr.deviceLocalWorkBuffers) {
+ if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
+ workBuf->lastActiveFrameSlot = -1;
}
- if (!qFuzzyCompare(psD->d->depthBias, cbD->currentDepthBiasValues.first)
- || !qFuzzyCompare(psD->d->slopeScaledDepthBias, cbD->currentDepthBiasValues.second))
- {
- [cbD->d->currentRenderPassEncoder setDepthBias: psD->d->depthBias
- slopeScale: psD->d->slopeScaledDepthBias
- clamp: 0.0f];
- cbD->currentDepthBiasValues = { psD->d->depthBias, psD->d->slopeScaledDepthBias };
+ for (QMetalBuffer *workBuf : psD->d->extraBufMgr.hostVisibleWorkBuffers) {
+ if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
+ workBuf->lastActiveFrameSlot = -1;
}
}
@@ -1073,8 +1517,8 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
{
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);
+ QMetalGraphicsPipeline *gfxPsD = cbD->currentGraphicsPipeline;
+ QMetalComputePipeline *compPsD = cbD->currentComputePipeline;
if (!srb) {
if (gfxPsD)
@@ -1088,9 +1532,15 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
bool hasDynamicOffsetInSrb = false;
bool resNeedsRebind = false;
+ // SPIRV-Cross buffer size buffers
+ // Need to determine storage buffer sizes here as this is the last opportunity for storage
+ // buffer bindings (offset, size) to be specified before draw / dispatch call
+ const bool needsBufferSizeBuffer = (compPsD && compPsD->d->bufferSizeBuffer) || (gfxPsD && gfxPsD->d->bufferSizeBuffer);
+ QMap<QRhiShaderResourceBinding::StageFlag, QMap<int, quint32>> storageBufferSizes;
+
// 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 QRhiShaderResourceBinding::Data *b = srbD->sortedBindings.at(i).data();
+ const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
QMetalShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]);
switch (b->type) {
case QRhiShaderResourceBinding::UniformBuffer:
@@ -1164,6 +1614,17 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
{
QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
+
+ if (needsBufferSizeBuffer) {
+ for (int i = 0; i < 6; ++i) {
+ const QRhiShaderResourceBinding::StageFlag stage =
+ QRhiShaderResourceBinding::StageFlag(1 << i);
+ if (b->stage.testFlag(stage)) {
+ storageBufferSizes[stage][b->binding] = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->size();
+ }
+ }
+ }
+
executeBufferHostWritesForCurrentFrame(bufD);
if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
resNeedsRebind = true;
@@ -1179,26 +1640,141 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
}
}
+ if (needsBufferSizeBuffer) {
+ QMetalBuffer *bufD = nullptr;
+ QVarLengthArray<QPair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders;
+
+ if (compPsD) {
+ bufD = compPsD->d->bufferSizeBuffer;
+ Q_ASSERT(compPsD->d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
+ shaders.append(qMakePair(&compPsD->d->cs, QRhiShaderResourceBinding::StageFlag::ComputeStage));
+ } else {
+ bufD = gfxPsD->d->bufferSizeBuffer;
+ if (gfxPsD->d->tess.enabled) {
+
+ // Assumptions
+ // * We only use one of the compute vertex shader variants in a pipeline at any one time
+ // * The vertex shader variants all have the same storage block bindings
+ // * The vertex shader variants all have the same native resource binding map
+ // * The vertex shader variants all have the same MslBufferSizeBufferBinding requirement
+ // * The vertex shader variants all have the same MslBufferSizeBufferBinding binding
+ // => We only need to use one vertex shader variant to generate the identical shader
+ // resource bindings
+ Q_ASSERT(gfxPsD->d->tess.compVs[0].desc.storageBlocks() == gfxPsD->d->tess.compVs[1].desc.storageBlocks());
+ Q_ASSERT(gfxPsD->d->tess.compVs[0].desc.storageBlocks() == gfxPsD->d->tess.compVs[2].desc.storageBlocks());
+ Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD->d->tess.compVs[1].nativeResourceBindingMap);
+ Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD->d->tess.compVs[2].nativeResourceBindingMap);
+ Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
+ == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
+ Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
+ == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
+ Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
+ == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
+ Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
+ == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
+
+ if (gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
+ shaders.append(qMakePair(&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage));
+
+ if (gfxPsD->d->tess.compTesc.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
+ shaders.append(qMakePair(&gfxPsD->d->tess.compTesc, QRhiShaderResourceBinding::StageFlag::TessellationControlStage));
+
+ if (gfxPsD->d->tess.vertTese.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
+ shaders.append(qMakePair(&gfxPsD->d->tess.vertTese, QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage));
+
+ } else {
+ if (gfxPsD->d->vs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
+ shaders.append(qMakePair(&gfxPsD->d->vs, QRhiShaderResourceBinding::StageFlag::VertexStage));
+ }
+ if (gfxPsD->d->fs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
+ shaders.append(qMakePair(&gfxPsD->d->fs, QRhiShaderResourceBinding::StageFlag::FragmentStage));
+ }
+
+ quint32 offset = 0;
+ for (const QPair<QMetalShader *, QRhiShaderResourceBinding::StageFlag> &shader : shaders) {
+
+ const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
+
+ // if we don't have a srb entry for the buffer size buffer
+ if (!(storageBufferSizes.contains(shader.second) && storageBufferSizes[shader.second].contains(binding))) {
+
+ int maxNativeBinding = 0;
+ for (const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks())
+ maxNativeBinding = qMax(maxNativeBinding, shader.first->nativeResourceBindingMap[block.binding].first);
+
+ const int size = (maxNativeBinding + 1) * sizeof(int);
+
+ Q_ASSERT(offset + size <= bufD->size());
+ srbD->sortedBindings.append(QRhiShaderResourceBinding::bufferLoad(binding, shader.second, bufD, offset, size));
+
+ QMetalShaderResourceBindings::BoundResourceData bd;
+ bd.sbuf.id = bufD->m_id;
+ bd.sbuf.generation = bufD->generation;
+ srbD->boundResourceData.append(bd);
+ }
+
+ // create the buffer size buffer data
+ QVarLengthArray<int, 8> bufferSizeBufferData;
+ Q_ASSERT(storageBufferSizes.contains(shader.second));
+ const QMap<int, quint32> &sizes(storageBufferSizes[shader.second]);
+ for (const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks()) {
+ const int index = shader.first->nativeResourceBindingMap[block.binding].first;
+
+ // if the native binding is -1, the buffer is present but not accessed in the shader
+ if (index < 0)
+ continue;
+
+ if (bufferSizeBufferData.size() <= index)
+ bufferSizeBufferData.resize(index + 1);
+
+ Q_ASSERT(sizes.contains(block.binding));
+ bufferSizeBufferData[index] = sizes[block.binding];
+ }
+
+ QRhiBufferData data;
+ const quint32 size = bufferSizeBufferData.size() * sizeof(int);
+ data.assign(reinterpret_cast<const char *>(bufferSizeBufferData.constData()), size);
+ Q_ASSERT(offset + size <= bufD->size());
+ bufD->d->pendingUpdates[bufD->d->slotted ? currentFrameSlot : 0].append({ offset, data });
+
+ // buffer offsets must be 32byte aligned
+ offset += ((size + 31) / 32) * 32;
+ }
+
+ executeBufferHostWritesForCurrentFrame(bufD);
+ bufD->lastActiveFrameSlot = currentFrameSlot;
+ }
+
// 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 srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srbD) : (cbD->currentComputeSrb != srbD);
const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
// dynamic uniform buffer offsets always trigger a rebind
if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt) {
- const QShader::NativeResourceBindingMap *resBindMaps[SUPPORTED_STAGES] = { nullptr, nullptr, nullptr };
+ const QShader::NativeResourceBindingMap *resBindMaps[SUPPORTED_STAGES] = { nullptr, nullptr, nullptr, nullptr, nullptr };
if (gfxPsD) {
- cbD->currentGraphicsSrb = srb;
+ cbD->currentGraphicsSrb = srbD;
cbD->currentComputeSrb = nullptr;
- resBindMaps[0] = &gfxPsD->d->vs.nativeResourceBindingMap;
- resBindMaps[1] = &gfxPsD->d->fs.nativeResourceBindingMap;
+ if (gfxPsD->d->tess.enabled) {
+ // If tessellating, we don't know which compVs shader to use until the draw call is
+ // made. They should all have the same native resource binding map, so pick one.
+ Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD->d->tess.compVs[1].nativeResourceBindingMap);
+ Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD->d->tess.compVs[2].nativeResourceBindingMap);
+ resBindMaps[QMetalShaderResourceBindingsData::VERTEX] = &gfxPsD->d->tess.compVs[0].nativeResourceBindingMap;
+ resBindMaps[QMetalShaderResourceBindingsData::TESSCTRL] = &gfxPsD->d->tess.compTesc.nativeResourceBindingMap;
+ resBindMaps[QMetalShaderResourceBindingsData::TESSEVAL] = &gfxPsD->d->tess.vertTese.nativeResourceBindingMap;
+ } else {
+ resBindMaps[QMetalShaderResourceBindingsData::VERTEX] = &gfxPsD->d->vs.nativeResourceBindingMap;
+ }
+ resBindMaps[QMetalShaderResourceBindingsData::FRAGMENT] = &gfxPsD->d->fs.nativeResourceBindingMap;
} else {
cbD->currentGraphicsSrb = nullptr;
- cbD->currentComputeSrb = srb;
- resBindMaps[2] = &compPsD->d->cs.nativeResourceBindingMap;
+ cbD->currentComputeSrb = srbD;
+ resBindMaps[QMetalShaderResourceBindingsData::COMPUTE] = &compPsD->d->cs.nativeResourceBindingMap;
}
cbD->currentSrbGeneration = srbD->generation;
cbD->currentResSlot = resSlot;
@@ -1229,13 +1805,13 @@ void QRhiMetal::setVertexInput(QRhiCommandBuffer *cb,
offsets.finish();
// same binding space for vertex and constant buffers - work it around
- QRhiShaderResourceBindings *srb = cbD->currentGraphicsSrb;
+ QMetalShaderResourceBindings *srbD = 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 (!srbD)
+ srbD = QRHI_RES(QMetalShaderResourceBindings, cbD->currentGraphicsPipeline->shaderResourceBindings());
+ const int firstVertexBinding = srbD->maxBinding + 1;
if (firstVertexBinding != cbD->d->currentFirstVertexBinding
|| buffers != cbD->d->currentVertexInputsBuffers
@@ -1259,7 +1835,7 @@ void QRhiMetal::setVertexInput(QRhiCommandBuffer *cb,
QMetalBuffer *ibufD = QRHI_RES(QMetalBuffer, indexBuf);
executeBufferHostWritesForCurrentFrame(ibufD);
ibufD->lastActiveFrameSlot = currentFrameSlot;
- cbD->currentIndexBuffer = indexBuf;
+ cbD->currentIndexBuffer = ibufD;
cbD->currentIndexOffset = indexOffset;
cbD->currentIndexFormat = indexFormat;
} else {
@@ -1275,7 +1851,7 @@ void QRhiMetal::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
// 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))
+ if (!qrhi_toTopLeftRenderTargetRect<UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
return;
MTLViewport vp;
@@ -1288,8 +1864,10 @@ void QRhiMetal::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
[cbD->d->currentRenderPassEncoder setViewport: vp];
- if (!QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
+ if (cbD->currentGraphicsPipeline
+ && !cbD->currentGraphicsPipeline->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
MTLScissorRect s;
+ qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, viewport.viewport(), &x, &y, &w, &h);
s.x = NSUInteger(x);
s.y = NSUInteger(y);
s.width = NSUInteger(w);
@@ -1302,12 +1880,12 @@ 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));
+ Q_ASSERT(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))
+ if (!qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
return;
MTLScissorRect s;
@@ -1336,20 +1914,320 @@ void QRhiMetal::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
[cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
}
+static id<MTLComputeCommandEncoder> tessellationComputeEncoder(QMetalCommandBuffer *cbD)
+{
+ if (cbD->d->currentRenderPassEncoder) {
+ [cbD->d->currentRenderPassEncoder endEncoding];
+ cbD->d->currentRenderPassEncoder = nil;
+ }
+
+ if (!cbD->d->tessellationComputeEncoder)
+ cbD->d->tessellationComputeEncoder = [cbD->d->cb computeCommandEncoder];
+
+ return cbD->d->tessellationComputeEncoder;
+}
+
+static void endTessellationComputeEncoding(QMetalCommandBuffer *cbD)
+{
+ if (cbD->d->tessellationComputeEncoder) {
+ [cbD->d->tessellationComputeEncoder endEncoding];
+ cbD->d->tessellationComputeEncoder = nil;
+ }
+
+ QMetalRenderTargetData * rtD = nullptr;
+
+ switch (cbD->currentTarget->resourceType()) {
+ case QRhiResource::SwapChainRenderTarget:
+ rtD = QRHI_RES(QMetalSwapChainRenderTarget, cbD->currentTarget)->d;
+ break;
+ case QRhiResource::TextureRenderTarget:
+ rtD = QRHI_RES(QMetalTextureRenderTarget, cbD->currentTarget)->d;
+ break;
+ default:
+ break;
+ }
+
+ Q_ASSERT(rtD);
+
+ QVarLengthArray<MTLLoadAction, 4> oldColorLoad;
+ for (uint i = 0; i < uint(rtD->colorAttCount); ++i) {
+ oldColorLoad.append(cbD->d->currentPassRpDesc.colorAttachments[i].loadAction);
+ if (cbD->d->currentPassRpDesc.colorAttachments[i].storeAction != MTLStoreActionDontCare)
+ cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
+ }
+
+ MTLLoadAction oldDepthLoad;
+ MTLLoadAction oldStencilLoad;
+ if (rtD->dsAttCount) {
+ oldDepthLoad = cbD->d->currentPassRpDesc.depthAttachment.loadAction;
+ if (cbD->d->currentPassRpDesc.depthAttachment.storeAction != MTLStoreActionDontCare)
+ cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
+
+ oldStencilLoad = cbD->d->currentPassRpDesc.stencilAttachment.loadAction;
+ if (cbD->d->currentPassRpDesc.stencilAttachment.storeAction != MTLStoreActionDontCare)
+ cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
+ }
+
+ cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
+ cbD->resetPerPassCachedState();
+
+ for (uint i = 0; i < uint(rtD->colorAttCount); ++i) {
+ cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = oldColorLoad[i];
+ }
+
+ if (rtD->dsAttCount) {
+ cbD->d->currentPassRpDesc.depthAttachment.loadAction = oldDepthLoad;
+ cbD->d->currentPassRpDesc.stencilAttachment.loadAction = oldStencilLoad;
+ }
+
+}
+
+void QRhiMetal::tessellatedDraw(const TessDrawArgs &args)
+{
+ QMetalCommandBuffer *cbD = args.cbD;
+ QMetalGraphicsPipeline *graphicsPipeline = cbD->currentGraphicsPipeline;
+ if (graphicsPipeline->d->tess.failed)
+ return;
+
+ const bool indexed = args.type != TessDrawArgs::NonIndexed;
+ const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
+ const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
+
+ QMetalGraphicsPipelineData::Tessellation &tess(graphicsPipeline->d->tess);
+ QMetalGraphicsPipelineData::ExtraBufferManager &extraBufMgr(graphicsPipeline->d->extraBufMgr);
+ const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
+ QMetalBuffer *vertOutBuf = nullptr;
+ QMetalBuffer *tescOutBuf = nullptr;
+ QMetalBuffer *tescPatchOutBuf = nullptr;
+ QMetalBuffer *tescFactorBuf = nullptr;
+ QMetalBuffer *tescParamsBuf = nullptr;
+ id<MTLComputeCommandEncoder> vertTescComputeEncoder = tessellationComputeEncoder(cbD);
+
+ // Step 1: vertex shader (as compute)
+ {
+ id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
+ QShader::Variant shaderVariant = QShader::NonIndexedVertexAsComputeShader;
+ if (args.type == TessDrawArgs::U16Indexed)
+ shaderVariant = QShader::UInt16IndexedVertexAsComputeShader;
+ else if (args.type == TessDrawArgs::U32Indexed)
+ shaderVariant = QShader::UInt32IndexedVertexAsComputeShader;
+ const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
+ id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(this, shaderVariant);
+ [computeEncoder setComputePipelineState: computePipelineState];
+
+ // Make uniform buffers, textures, and samplers (meant for the
+ // vertex stage from the client's point of view) visible in the
+ // "vertex as compute" shader
+ cbD->d->currentComputePassEncoder = computeEncoder;
+ rebindShaderResources(cbD, QMetalShaderResourceBindingsData::VERTEX, QMetalShaderResourceBindingsData::COMPUTE);
+ cbD->d->currentComputePassEncoder = nil;
+
+ const QMap<int, int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
+ const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
+ const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
+
+ if (outputBufferBinding >= 0) {
+ const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
+ vertOutBuf = extraBufMgr.acquireWorkBuffer(this, workBufSize);
+ if (!vertOutBuf)
+ return;
+ [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
+ }
+
+ if (indexBufferBinding >= 0)
+ [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
+
+ for (int i = 0, ie = cbD->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
+ const auto &bufferBatch(cbD->d->currentVertexInputsBuffers.batches[i]);
+ const auto &offsetBatch(cbD->d->currentVertexInputOffsets.batches[i]);
+ [computeEncoder setBuffers: bufferBatch.resources.constData()
+ offsets: offsetBatch.resources.constData()
+ withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
+ }
+
+ if (indexed) {
+ [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
+ args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
+ } else {
+ [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
+ args.draw.vertexCount, args.draw.instanceCount)];
+ }
+
+ [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
+ threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
+ }
+
+ // Step 2: tessellation control shader (as compute)
+ {
+ id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
+ id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(this);
+ [computeEncoder setComputePipelineState: computePipelineState];
+
+ cbD->d->currentComputePassEncoder = computeEncoder;
+ rebindShaderResources(cbD, QMetalShaderResourceBindingsData::TESSCTRL, QMetalShaderResourceBindingsData::COMPUTE);
+ cbD->d->currentComputePassEncoder = nil;
+
+ const QMap<int, int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
+ const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
+ const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
+ const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
+ const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
+ const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
+
+ if (outputBufferBinding >= 0) {
+ const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
+ tescOutBuf = extraBufMgr.acquireWorkBuffer(this, workBufSize);
+ if (!tescOutBuf)
+ return;
+ [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
+ }
+
+ if (patchOutputBufferBinding >= 0) {
+ const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
+ tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(this, workBufSize);
+ if (!tescPatchOutBuf)
+ return;
+ [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
+ }
+
+ if (tessFactorBufferBinding >= 0) {
+ tescFactorBuf = extraBufMgr.acquireWorkBuffer(this, patchCount * sizeof(MTLQuadTessellationFactorsHalf));
+ [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
+ }
+
+ if (paramsBufferBinding >= 0) {
+ struct {
+ quint32 inControlPointCount;
+ quint32 patchCount;
+ } params;
+ tescParamsBuf = extraBufMgr.acquireWorkBuffer(this, sizeof(params), QMetalGraphicsPipelineData::ExtraBufferManager::WorkBufType::HostVisible);
+ if (!tescParamsBuf)
+ return;
+ params.inControlPointCount = tess.inControlPointCount;
+ params.patchCount = patchCount;
+ id<MTLBuffer> paramsBuf = tescParamsBuf->d->buf[0];
+ char *p = reinterpret_cast<char *>([paramsBuf contents]);
+ memcpy(p, &params, sizeof(params));
+ [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
+ }
+
+ if (vertOutBuf && inputBufferBinding >= 0)
+ [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
+
+ int sgSize = int(computePipelineState.threadExecutionWidth);
+ int wgSize = std::lcm(tess.outControlPointCount, sgSize);
+ while (wgSize > caps.maxThreadGroupSize) {
+ sgSize /= 2;
+ wgSize = std::lcm(tess.outControlPointCount, sgSize);
+ }
+ [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
+ threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
+ }
+
+ // Much of the state in the QMetalCommandBuffer is going to be reset
+ // when we get a new render encoder. Save what we need. (cheaper than
+ // starting to walk over the srb again)
+ const QMetalShaderResourceBindingsData resourceBindings = cbD->d->currentShaderResourceBindingState;
+
+ endTessellationComputeEncoding(cbD);
+
+ // Step 3: tessellation evaluation (as vertex) + fragment shader
+ {
+ // No need to call tess.teseFragRenderPipeline because it was done
+ // once and we know the result is stored in the standard place
+ // (graphicsPipeline->d->ps).
+
+ graphicsPipeline->makeActiveForCurrentRenderPassEncoder(cbD);
+ id<MTLRenderCommandEncoder> renderEncoder = cbD->d->currentRenderPassEncoder;
+
+ rebindShaderResources(cbD, QMetalShaderResourceBindingsData::TESSEVAL, QMetalShaderResourceBindingsData::VERTEX, &resourceBindings);
+ rebindShaderResources(cbD, QMetalShaderResourceBindingsData::FRAGMENT, QMetalShaderResourceBindingsData::FRAGMENT, &resourceBindings);
+
+ const QMap<int, int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
+ const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
+ const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
+ const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
+
+ if (outputBufferBinding >= 0 && tescOutBuf)
+ [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
+
+ if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
+ [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
+
+ if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
+ [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
+ [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
+ }
+
+ [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
+ patchStart: 0
+ patchCount: patchCount
+ patchIndexBuffer: nil
+ patchIndexBufferOffset: 0
+ instanceCount: 1
+ baseInstance: 0];
+ }
+}
+
+void QRhiMetal::adjustForMultiViewDraw(quint32 *instanceCount, QRhiCommandBuffer *cb)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ const int multiViewCount = cbD->currentGraphicsPipeline->m_multiViewCount;
+ if (multiViewCount <= 1)
+ return;
+
+ const QMap<int, int> &ebb(cbD->currentGraphicsPipeline->d->vs.nativeShaderInfo.extraBufferBindings);
+ const int viewMaskBufBinding = ebb.value(QShaderPrivate::MslMultiViewMaskBufferBinding, -1);
+ if (viewMaskBufBinding == -1) {
+ qWarning("No extra buffer for multiview in the vertex shader; was it built with --view-count specified?");
+ return;
+ }
+ struct {
+ quint32 viewOffset;
+ quint32 viewCount;
+ } multiViewInfo;
+ multiViewInfo.viewOffset = 0;
+ multiViewInfo.viewCount = quint32(multiViewCount);
+ QMetalBuffer *buf = cbD->currentGraphicsPipeline->d->extraBufMgr.acquireWorkBuffer(this, sizeof(multiViewInfo),
+ QMetalGraphicsPipelineData::ExtraBufferManager::WorkBufType::HostVisible);
+ if (buf) {
+ id<MTLBuffer> mtlbuf = buf->d->buf[0];
+ char *p = reinterpret_cast<char *>([mtlbuf contents]);
+ memcpy(p, &multiViewInfo, sizeof(multiViewInfo));
+ [cbD->d->currentRenderPassEncoder setVertexBuffer: mtlbuf offset: 0 atIndex: viewMaskBufBinding];
+ // The instance count is adjusted for layered rendering. The vertex shader is expected to contain something like:
+ // uint gl_ViewIndex = spvViewMask[0] + (gl_InstanceIndex - gl_BaseInstance) % spvViewMask[1];
+ // where spvViewMask is the buffer with multiViewInfo passed in above.
+ *instanceCount *= multiViewCount;
+ }
+}
+
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);
+ if (cbD->currentGraphicsPipeline->d->tess.enabled) {
+ TessDrawArgs a;
+ a.cbD = cbD;
+ a.type = TessDrawArgs::NonIndexed;
+ a.draw.vertexCount = vertexCount;
+ a.draw.instanceCount = instanceCount;
+ a.draw.firstVertex = firstVertex;
+ a.draw.firstInstance = firstInstance;
+ tessellatedDraw(a);
+ return;
+ }
+
+ adjustForMultiViewDraw(&instanceCount, cb);
+
if (caps.baseVertexAndInstance) {
- [cbD->d->currentRenderPassEncoder drawPrimitives:
- QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline)->d->primitiveType
- vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
+ [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
+ vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
} else {
- [cbD->d->currentRenderPassEncoder drawPrimitives:
- QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline)->d->primitiveType
- vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
+ [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
+ vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
}
}
@@ -1363,25 +2241,41 @@ void QRhiMetal::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
return;
const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
- Q_ASSERT(indexOffset == aligned<quint32>(indexOffset, 4));
+ Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
+
+ QMetalBuffer *ibufD = cbD->currentIndexBuffer;
+ id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
+
+ if (cbD->currentGraphicsPipeline->d->tess.enabled) {
+ TessDrawArgs a;
+ a.cbD = cbD;
+ a.type = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? TessDrawArgs::U16Indexed : TessDrawArgs::U32Indexed;
+ a.drawIndexed.indexCount = indexCount;
+ a.drawIndexed.instanceCount = instanceCount;
+ a.drawIndexed.firstIndex = firstIndex;
+ a.drawIndexed.vertexOffset = vertexOffset;
+ a.drawIndexed.firstInstance = firstInstance;
+ a.drawIndexed.indexBuffer = mtlibuf;
+ tessellatedDraw(a);
+ return;
+ }
- QMetalBuffer *ibufD = QRHI_RES(QMetalBuffer, cbD->currentIndexBuffer);
- id<MTLBuffer> mtlbuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
+ adjustForMultiViewDraw(&instanceCount, cb);
if (caps.baseVertexAndInstance) {
- [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline)->d->primitiveType
+ [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
indexCount: indexCount
indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
- indexBuffer: mtlbuf
+ indexBuffer: mtlibuf
indexBufferOffset: indexOffset
instanceCount: instanceCount
baseVertex: vertexOffset
baseInstance: firstInstance];
} else {
- [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline)->d->primitiveType
+ [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
indexCount: indexCount
indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
- indexBuffer: mtlbuf
+ indexBuffer: mtlibuf
indexBufferOffset: indexOffset
instanceCount: instanceCount];
}
@@ -1438,34 +2332,39 @@ void QRhiMetal::endExternal(QRhiCommandBuffer *cb)
cbD->resetPerPassCachedState();
}
+double QRhiMetal::lastCompletedGpuTime(QRhiCommandBuffer *cb)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ return cbD->d->lastGpuTime;
+}
+
QRhi::FrameOpResult QRhiMetal::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
{
Q_UNUSED(flags);
QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain);
+ currentSwapChain = swapChainD;
+ currentFrameSlot = swapChainD->currentFrameSlot;
- // 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 we are too far ahead, block. This is also what ensures that any
+ // resource used in the previous frame for this slot is now not in use
+ // anymore by the GPU.
+ dispatch_semaphore_wait(swapChainD->d->sem[currentFrameSlot], DISPATCH_TIME_FOREVER);
+
+ // 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).
+ for (QMetalSwapChain *sc : std::as_const(swapchains)) {
if (sc != swapChainD)
- dispatch_semaphore_signal(sem);
+ sc->waitUntilCompleted(currentFrameSlot); // wait+signal
}
- currentSwapChain = swapChainD;
- currentFrameSlot = swapChainD->currentFrameSlot;
- if (swapChainD->ds)
- swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
-
[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];
+ swapChainD->cbWrapper.d->cb = d->newCommandBuffer();
QMetalRenderTargetData::ColorAtt colorAtt;
if (swapChainD->samples > 1) {
@@ -1477,11 +2376,16 @@ QRhi::FrameOpResult QRhiMetal::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF
swapChainD->rtWrapper.d->fb.colorAtt[0] = colorAtt;
swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
+ swapChainD->rtWrapper.d->fb.dsResolveTex = nil;
swapChainD->rtWrapper.d->fb.hasStencil = swapChainD->ds ? true : false;
swapChainD->rtWrapper.d->fb.depthNeedsStore = false;
+ if (swapChainD->ds)
+ swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
+
executeDeferredReleases();
- swapChainD->cbWrapper.resetState();
+ swapChainD->cbWrapper.resetState(swapChainD->d->lastGpuTime[currentFrameSlot]);
+ swapChainD->d->lastGpuTime[currentFrameSlot] = 0;
finishActiveReadbacks();
return QRhi::FrameOpSuccess;
@@ -1492,25 +2396,47 @@ QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain);
Q_ASSERT(currentSwapChain == swapChainD);
+ __block int thisFrameSlot = currentFrameSlot;
+ [swapChainD->cbWrapper.d->cb addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
+ swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
+ dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
+ }];
+
+#ifdef QRHI_METAL_COMMAND_BUFFERS_WITH_UNRETAINED_REFERENCES
+ // When Metal API validation diagnostics is enabled in Xcode the texture is
+ // released before the command buffer is done with it. Manually keep it alive
+ // to work around this.
+ id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
+ [swapChainD->cbWrapper.d->cb addCompletedHandler:^(id<MTLCommandBuffer>) {
+ [drawableTexture release];
+ }];
+#endif
+
const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
- if (needsPresent) {
- auto drawable = swapChainD->d->curDrawable;
- [swapChainD->cbWrapper.d->cb addScheduledHandler:^(id<MTLCommandBuffer>) {
+ const bool presentsWithTransaction = swapChainD->d->layer.presentsWithTransaction;
+ if (!presentsWithTransaction && needsPresent) {
+ // beginFrame-endFrame without a render pass inbetween means there is no drawable.
+ if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable)
+ [swapChainD->cbWrapper.d->cb presentDrawable: drawable];
+ }
+
+ [swapChainD->cbWrapper.d->cb commit];
+
+ if (presentsWithTransaction && needsPresent) {
+ // beginFrame-endFrame without a render pass inbetween means there is no drawable.
+ if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
+ // The layer has presentsWithTransaction set to true to avoid flicker on resizing,
+ // so here it is important to follow what the Metal docs say when it comes to the
+ // issuing the present.
+ [swapChainD->cbWrapper.d->cb waitUntilScheduled];
[drawable present];
- }];
+ }
}
// Must not hold on to the drawable, regardless of needsPresent
[swapChainD->d->curDrawable release];
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];
-
[d->captureScope endScope];
if (needsPresent)
@@ -1526,23 +2452,17 @@ QRhi::FrameOpResult QRhiMetal::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi:
Q_UNUSED(flags);
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);
- }
- }
+
+ for (QMetalSwapChain *sc : std::as_const(swapchains))
+ sc->waitUntilCompleted(currentFrameSlot);
d->ofr.active = true;
*cb = &d->ofr.cbWrapper;
- d->ofr.cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences];
+ d->ofr.cbWrapper.d->cb = d->newCommandBuffer();
executeDeferredReleases();
- d->ofr.cbWrapper.resetState();
+ d->ofr.cbWrapper.resetState(d->ofr.lastGpuTime);
+ d->ofr.lastGpuTime = 0;
finishActiveReadbacks();
return QRhi::FrameOpSuccess;
@@ -1554,10 +2474,13 @@ QRhi::FrameOpResult QRhiMetal::endOffscreenFrame(QRhi::EndFrameFlags flags)
Q_ASSERT(d->ofr.active);
d->ofr.active = false;
- [d->ofr.cbWrapper.d->cb commit];
+ id<MTLCommandBuffer> cb = d->ofr.cbWrapper.d->cb;
+ [cb commit];
// offscreen frames wait for completion, unlike swapchain ones
- [d->ofr.cbWrapper.d->cb waitUntilCompleted];
+ [cb waitUntilCompleted];
+
+ d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
finishActiveReadbacks(true);
@@ -1581,16 +2504,14 @@ QRhi::FrameOpResult QRhiMetal::finish()
}
}
- for (QMetalSwapChain *sc : qAsConst(swapchains)) {
+ for (QMetalSwapChain *sc : std::as_const(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);
+ sc->waitUntilCompleted(i);
}
}
@@ -1600,10 +2521,13 @@ QRhi::FrameOpResult QRhiMetal::finish()
}
if (inFrame) {
- if (d->ofr.active)
- d->ofr.cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences];
- else
- swapChainD->cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences];
+ if (d->ofr.active) {
+ d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
+ d->ofr.cbWrapper.d->cb = d->newCommandBuffer();
+ } else {
+ swapChainD->d->lastGpuTime[currentFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
+ swapChainD->cbWrapper.d->cb = d->newCommandBuffer();
+ }
}
executeDeferredReleases(true);
@@ -1781,6 +2705,15 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
+ id<MTLBlitCommandEncoder> blitEnc = nil;
+ auto ensureBlit = [&blitEnc, cbD, this]() {
+ if (!blitEnc) {
+ blitEnc = [cbD->d->cb blitCommandEncoder];
+ if (debugMarkers)
+ [blitEnc pushDebugGroup: @"Texture upload/copy"];
+ }
+ };
+
for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) {
@@ -1803,25 +2736,33 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf);
executeBufferHostWritesForCurrentFrame(bufD);
const int idx = bufD->d->slotted ? currentFrameSlot : 0;
- char *p = reinterpret_cast<char *>([bufD->d->buf[idx] contents]);
- if (p) {
- u.result->data.resize(u.readSize);
- memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
+ if (bufD->m_type == QRhiBuffer::Dynamic) {
+ char *p = reinterpret_cast<char *>([bufD->d->buf[idx] contents]);
+ if (p) {
+ u.result->data.resize(u.readSize);
+ memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
+ }
+ if (u.result->completed)
+ u.result->completed();
+ } else {
+ QRhiMetalData::BufferReadback readback;
+ readback.activeFrameSlot = idx;
+ readback.buf = bufD->d->buf[idx];
+ readback.offset = u.offset;
+ readback.readSize = u.readSize;
+ readback.result = u.result;
+ d->activeBufferReadbacks.append(readback);
+#ifdef Q_OS_MACOS
+ if (bufD->d->managed) {
+ // On non-Apple Silicon, manually synchronize memory from GPU to CPU
+ ensureBlit();
+ [blitEnc synchronizeResource:readback.buf];
+ }
+#endif
}
- if (u.result->completed)
- u.result->completed();
}
}
- id<MTLBlitCommandEncoder> blitEnc = nil;
- auto ensureBlit = [&blitEnc, cbD, this] {
- if (!blitEnc) {
- blitEnc = [cbD->d->cb blitCommandEncoder];
- if (debugMarkers)
- [blitEnc pushDebugGroup: @"Texture upload/copy"];
- }
- };
-
for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
@@ -1829,7 +2770,7 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
qsizetype stagingSize = 0;
for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
- for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
stagingSize += subresUploadByteSize(subresDesc);
}
}
@@ -1843,7 +2784,7 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
qsizetype curOfs = 0;
for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
- for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
}
}
@@ -1954,17 +2895,17 @@ void QRhiMetal::executeBufferHostWritesForSlot(QMetalBuffer *bufD, int slot)
return;
void *p = [bufD->d->buf[slot] contents];
- int changeBegin = -1;
- int changeEnd = -1;
- for (const QMetalBufferData::BufferUpdate &u : qAsConst(bufD->d->pendingUpdates[slot])) {
+ quint32 changeBegin = UINT32_MAX;
+ quint32 changeEnd = 0;
+ for (const QMetalBufferData::BufferUpdate &u : std::as_const(bufD->d->pendingUpdates[slot])) {
memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
- if (changeBegin == -1 || u.offset < changeBegin)
+ if (u.offset < changeBegin)
changeBegin = u.offset;
- if (changeEnd == -1 || u.offset + u.data.size() > changeEnd)
+ if (u.offset + u.data.size() > changeEnd)
changeEnd = u.offset + u.data.size();
}
#ifdef Q_OS_MACOS
- if (changeBegin >= 0 && bufD->d->managed)
+ if (changeBegin < UINT32_MAX && changeBegin < changeEnd && bufD->d->managed)
[bufD->d->buf[slot] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
#endif
@@ -2032,21 +2973,24 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
rtTex->create();
cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue, rtD->colorAttCount);
- if (rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents)) {
+ if (rtD->fb.preserveColor) {
for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
}
- if (rtD->dsAttCount && rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents)) {
+ if (rtD->dsAttCount && rtD->fb.preserveDs) {
cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
}
for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
it != itEnd; ++it)
{
- if (it->texture())
+ if (it->texture()) {
QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
- else if (it->renderBuffer())
+ if (it->multiViewCount() >= 2)
+ cbD->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
+ } else if (it->renderBuffer()) {
QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
+ }
if (it->resolveTexture())
QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
}
@@ -2054,6 +2998,8 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
if (rtTex->m_desc.depthTexture())
QRHI_RES(QMetalTexture, rtTex->m_desc.depthTexture())->lastActiveFrameSlot = currentFrameSlot;
+ if (rtTex->m_desc.depthResolveTexture())
+ QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
}
break;
default:
@@ -2067,7 +3013,8 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
cbD->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
cbD->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
if (rtD->fb.colorAtt[i].resolveTex) {
- cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = MTLStoreActionMultisampleResolve;
+ cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = rtD->fb.preserveColor ? MTLStoreActionStoreAndMultisampleResolve
+ : MTLStoreActionMultisampleResolve;
cbD->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
cbD->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
cbD->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
@@ -2080,6 +3027,15 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
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;
+ if (rtD->fb.dsResolveTex) {
+ cbD->d->currentPassRpDesc.depthAttachment.storeAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
+ : MTLStoreActionMultisampleResolve;
+ cbD->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
+ if (rtD->fb.hasStencil) {
+ cbD->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
+ cbD->d->currentPassRpDesc.stencilAttachment.storeAction = cbD->d->currentPassRpDesc.depthAttachment.storeAction;
+ }
+ }
}
cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
@@ -2137,9 +3093,9 @@ void QRhiMetal::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *p
Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::ComputePass);
QMetalComputePipeline *psD = QRHI_RES(QMetalComputePipeline, ps);
- if (cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
+ if (cbD->currentComputePipeline != psD || cbD->currentPipelineGeneration != psD->generation) {
cbD->currentGraphicsPipeline = nullptr;
- cbD->currentComputePipeline = ps;
+ cbD->currentComputePipeline = psD;
cbD->currentPipelineGeneration = psD->generation;
[cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
@@ -2204,6 +3160,17 @@ void QRhiMetal::executeDeferredReleases(bool forced)
case QRhiMetalData::DeferredReleaseEntry::StagingBuffer:
[e.stagingBuffer.buffer release];
break;
+ case QRhiMetalData::DeferredReleaseEntry::GraphicsPipeline:
+ [e.graphicsPipeline.pipelineState release];
+ [e.graphicsPipeline.depthStencilState release];
+ [e.graphicsPipeline.tessVertexComputeState[0] release];
+ [e.graphicsPipeline.tessVertexComputeState[1] release];
+ [e.graphicsPipeline.tessVertexComputeState[2] release];
+ [e.graphicsPipeline.tessTessControlComputeState release];
+ break;
+ case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
+ [e.computePipeline.pipelineState release];
+ break;
default:
break;
}
@@ -2229,7 +3196,23 @@ void QRhiMetal::finishActiveReadbacks(bool forced)
if (readback.result->completed)
completedCallbacks.append(readback.result->completed);
- d->activeTextureReadbacks.removeLast();
+ d->activeTextureReadbacks.remove(i);
+ }
+ }
+
+ for (int i = d->activeBufferReadbacks.count() - 1; i >= 0; --i) {
+ const QRhiMetalData::BufferReadback &readback(d->activeBufferReadbacks[i]);
+ if (forced || currentFrameSlot == readback.activeFrameSlot
+ || readback.activeFrameSlot < 0) {
+ readback.result->data.resize(readback.readSize);
+ char *p = reinterpret_cast<char *>([readback.buf contents]);
+ Q_ASSERT(p);
+ memcpy(readback.result->data.data(), p + readback.offset, size_t(readback.readSize));
+
+ if (readback.result->completed)
+ completedCallbacks.append(readback.result->completed);
+
+ d->activeBufferReadbacks.remove(i);
}
}
@@ -2237,7 +3220,7 @@ void QRhiMetal::finishActiveReadbacks(bool forced)
f();
}
-QMetalBuffer::QMetalBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
+QMetalBuffer::QMetalBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
: QRhiBuffer(rhi, type, usage, size),
d(new QMetalBufferData)
{
@@ -2283,13 +3266,15 @@ bool QMetalBuffer::create()
return false;
}
- const uint nonZeroSize = m_size <= 0 ? 256 : uint(m_size);
- const uint roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned<uint>(nonZeroSize, 256) : nonZeroSize;
+ const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
+ const quint32 roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256u) : nonZeroSize;
d->managed = false;
MTLResourceOptions opts = MTLResourceStorageModeShared;
+
+ QRHI_RES_RHI(QRhiMetal);
#ifdef Q_OS_MACOS
- if (m_type != Dynamic) {
+ if (!rhiD->caps.isAppleGPU && m_type != Dynamic) {
opts = MTLResourceStorageModeManaged;
d->managed = true;
}
@@ -2300,8 +3285,10 @@ bool QMetalBuffer::create()
// Static maps to on macOS) is not safe when another frame reading from the
// same buffer is still in flight.
d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer); // except for SSBOs written in the shader
+ // and a special case for internal work buffers
+ if (int(m_usage) == WorkBufPoolUsage)
+ d->slotted = false;
- 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];
@@ -2364,11 +3351,12 @@ void QMetalBuffer::endFullDynamicBufferUpdateForCurrentFrame()
#endif
}
-static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags, const QRhiMetalData *d)
+static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags, const QRhiMetal *d)
{
#ifndef Q_OS_MACOS
Q_UNUSED(d);
#endif
+
const bool srgb = flags.testFlag(QRhiTexture::sRGB);
switch (format) {
case QRhiTexture::RGBA8:
@@ -2410,9 +3398,9 @@ static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QR
case QRhiTexture::D16:
return MTLPixelFormatDepth16Unorm;
case QRhiTexture::D24:
- return [d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
+ return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
case QRhiTexture::D24S8:
- return [d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
+ return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
#else
case QRhiTexture::D16:
return MTLPixelFormatDepth32Float;
@@ -2435,7 +3423,7 @@ static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QR
return MTLPixelFormatBC4_RUnorm;
case QRhiTexture::BC5:
qWarning("QRhiMetal does not support BC5");
- return MTLPixelFormatRGBA8Unorm;
+ return MTLPixelFormatInvalid;
case QRhiTexture::BC6H:
return MTLPixelFormatBC6H_RGBUfloat;
case QRhiTexture::BC7:
@@ -2449,7 +3437,7 @@ static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QR
case QRhiTexture::BC6H:
case QRhiTexture::BC7:
qWarning("QRhiMetal: BCx compression not supported on this platform");
- return MTLPixelFormatRGBA8Unorm;
+ return MTLPixelFormatInvalid;
#endif
#ifndef Q_OS_MACOS
@@ -2490,32 +3478,129 @@ static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QR
return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
#else
case QRhiTexture::ETC2_RGB8:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
+ }
+ qWarning("QRhiMetal: ETC2 compression not supported on this platform");
+ return MTLPixelFormatInvalid;
case QRhiTexture::ETC2_RGB8A1:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
+ }
+ qWarning("QRhiMetal: ETC2 compression not supported on this platform");
+ return MTLPixelFormatInvalid;
case QRhiTexture::ETC2_RGBA8:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
+ }
qWarning("QRhiMetal: ETC2 compression not supported on this platform");
- return MTLPixelFormatRGBA8Unorm;
-
+ return MTLPixelFormatInvalid;
case QRhiTexture::ASTC_4x4:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
+ }
+ qWarning("QRhiMetal: ASTC compression not supported on this platform");
+ return MTLPixelFormatInvalid;
case QRhiTexture::ASTC_5x4:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
+ }
+ qWarning("QRhiMetal: ASTC compression not supported on this platform");
+ return MTLPixelFormatInvalid;
case QRhiTexture::ASTC_5x5:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
+ }
+ qWarning("QRhiMetal: ASTC compression not supported on this platform");
+ return MTLPixelFormatInvalid;
case QRhiTexture::ASTC_6x5:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
+ }
+ qWarning("QRhiMetal: ASTC compression not supported on this platform");
+ return MTLPixelFormatInvalid;
case QRhiTexture::ASTC_6x6:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
+ }
+ qWarning("QRhiMetal: ASTC compression not supported on this platform");
+ return MTLPixelFormatInvalid;
case QRhiTexture::ASTC_8x5:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
+ }
+ qWarning("QRhiMetal: ASTC compression not supported on this platform");
+ return MTLPixelFormatInvalid;
case QRhiTexture::ASTC_8x6:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
+ }
+ qWarning("QRhiMetal: ASTC compression not supported on this platform");
+ return MTLPixelFormatInvalid;
case QRhiTexture::ASTC_8x8:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
+ }
+ qWarning("QRhiMetal: ASTC compression not supported on this platform");
+ return MTLPixelFormatInvalid;
case QRhiTexture::ASTC_10x5:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
+ }
+ qWarning("QRhiMetal: ASTC compression not supported on this platform");
+ return MTLPixelFormatInvalid;
case QRhiTexture::ASTC_10x6:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
+ }
+ qWarning("QRhiMetal: ASTC compression not supported on this platform");
+ return MTLPixelFormatInvalid;
case QRhiTexture::ASTC_10x8:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
+ }
+ qWarning("QRhiMetal: ASTC compression not supported on this platform");
+ return MTLPixelFormatInvalid;
case QRhiTexture::ASTC_10x10:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
+ }
+ qWarning("QRhiMetal: ASTC compression not supported on this platform");
+ return MTLPixelFormatInvalid;
case QRhiTexture::ASTC_12x10:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
+ }
+ qWarning("QRhiMetal: ASTC compression not supported on this platform");
+ return MTLPixelFormatInvalid;
case QRhiTexture::ASTC_12x12:
+ if (d->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *))
+ return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
+ }
qWarning("QRhiMetal: ASTC compression not supported on this platform");
- return MTLPixelFormatRGBA8Unorm;
+ return MTLPixelFormatInvalid;
#endif
default:
Q_UNREACHABLE();
- return MTLPixelFormatRGBA8Unorm;
+ return MTLPixelFormatInvalid;
}
}
@@ -2575,9 +3660,18 @@ bool QMetalRenderBuffer::create()
switch (m_type) {
case DepthStencil:
#ifdef Q_OS_MACOS
- desc.storageMode = MTLStorageModePrivate;
- d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
- ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
+ if (rhiD->caps.isAppleGPU) {
+ if (@available(macOS 11.0, *)) {
+ desc.storageMode = MTLStorageModeMemoryless;
+ d->format = MTLPixelFormatDepth32Float_Stencil8;
+ } else {
+ Q_UNREACHABLE();
+ }
+ } else {
+ desc.storageMode = MTLStorageModePrivate;
+ d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
+ ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
+ }
#else
desc.storageMode = MTLStorageModeMemoryless;
d->format = MTLPixelFormatDepth32Float_Stencil8;
@@ -2587,7 +3681,7 @@ bool QMetalRenderBuffer::create()
case Color:
desc.storageMode = MTLStorageModePrivate;
if (m_backingFormatHint != QRhiTexture::UnknownFormat)
- d->format = toMetalTextureFormat(m_backingFormatHint, {}, rhiD->d);
+ d->format = toMetalTextureFormat(m_backingFormatHint, {}, rhiD);
else
d->format = MTLPixelFormatRGBA8Unorm;
desc.pixelFormat = d->format;
@@ -2669,14 +3763,17 @@ bool QMetalTexture::prepareCreate(QSize *adjustedSize)
if (d->tex)
destroy();
- const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ const bool is1D = m_flags.testFlag(OneDimensional);
+
+ const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
+ : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
QRHI_RES_RHI(QRhiMetal);
- d->format = toMetalTextureFormat(m_format, m_flags, rhiD->d);
+ d->format = toMetalTextureFormat(m_format, m_flags, rhiD);
mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
samples = rhiD->effectiveSampleCount(m_sampleCount);
if (samples > 1) {
@@ -2701,12 +3798,18 @@ bool QMetalTexture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both array and 3D");
return false;
}
- m_depth = qMax(1, m_depth);
+ if (is1D && is3D) {
+ qWarning("Texture cannot be both 1D and 3D");
+ return false;
+ }
+ if (is1D && isCube) {
+ qWarning("Texture cannot be both 1D and cube");
+ return false;
+ }
if (m_depth > 1 && !is3D) {
qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
return false;
}
- m_arraySize = qMax(0, m_arraySize);
if (m_arraySize > 0 && !isArray) {
qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
return false;
@@ -2733,17 +3836,20 @@ bool QMetalTexture::create()
const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
+ const bool is1D = m_flags.testFlag(OneDimensional);
if (isCube) {
desc.textureType = MTLTextureTypeCube;
} else if (is3D) {
desc.textureType = MTLTextureType3D;
+ } else if (is1D) {
+ desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
} else if (isArray) {
#ifdef Q_OS_IOS
- if (samples > 1) {
- // would be available on iOS 14.0+ but cannot test for that with a 13 SDK
- qWarning("Multisample 2D texture array is not supported on iOS");
+ if (@available(iOS 14, *)) {
+ desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
+ } else {
+ desc.textureType = MTLTextureType2DArray;
}
- desc.textureType = MTLTextureType2DArray;
#else
desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
#endif
@@ -2753,12 +3859,12 @@ bool QMetalTexture::create()
desc.pixelFormat = d->format;
desc.width = NSUInteger(size.width());
desc.height = NSUInteger(size.height());
- desc.depth = is3D ? m_depth : 1;
+ desc.depth = is3D ? qMax(1, m_depth) : 1;
desc.mipmapLevelCount = NSUInteger(mipLevelCount);
if (samples > 1)
desc.sampleCount = NSUInteger(samples);
if (isArray)
- desc.arrayLength = NSUInteger(m_arraySize);
+ desc.arrayLength = NSUInteger(qMax(0, m_arraySize));
desc.resourceOptions = MTLResourceStorageModePrivate;
desc.storageMode = MTLStorageModePrivate;
desc.usage = MTLTextureUsageShaderRead;
@@ -2817,7 +3923,8 @@ id<MTLTexture> QMetalTextureData::viewForLevel(int level)
const bool isCube = q->m_flags.testFlag(QRhiTexture::CubeMap);
const bool isArray = q->m_flags.testFlag(QRhiTexture::TextureArray);
id<MTLTexture> view = [tex newTextureViewWithPixelFormat: format textureType: type
- levels: NSMakeRange(NSUInteger(level), 1) slices: NSMakeRange(0, isCube ? 6 : (isArray ? q->m_arraySize : 1))];
+ levels: NSMakeRange(NSUInteger(level), 1)
+ slices: NSMakeRange(0, isCube ? 6 : (isArray ? qMax(0, q->m_arraySize) : 1))];
perLevelViews[level] = view;
return view;
@@ -2962,7 +4069,9 @@ QMetalRenderPassDescriptor::~QMetalRenderPassDescriptor()
void QMetalRenderPassDescriptor::destroy()
{
- // nothing to do here
+ QRHI_RES_RHI(QRhiMetal);
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
bool QMetalRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
@@ -3005,13 +4114,17 @@ void QMetalRenderPassDescriptor::updateSerializedFormat()
QRhiRenderPassDescriptor *QMetalRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
{
- QMetalRenderPassDescriptor *rp = new QMetalRenderPassDescriptor(m_rhi);
- rp->colorAttachmentCount = colorAttachmentCount;
- rp->hasDepthStencil = hasDepthStencil;
- memcpy(rp->colorFormat, colorFormat, sizeof(colorFormat));
- rp->dsFormat = dsFormat;
- rp->updateSerializedFormat();
- return rp;
+ QMetalRenderPassDescriptor *rpD = new QMetalRenderPassDescriptor(m_rhi);
+ rpD->colorAttachmentCount = colorAttachmentCount;
+ rpD->hasDepthStencil = hasDepthStencil;
+ memcpy(rpD->colorFormat, colorFormat, sizeof(colorFormat));
+ rpD->dsFormat = dsFormat;
+
+ rpD->updateSerializedFormat();
+
+ QRHI_RES_RHI(QRhiMetal);
+ rhiD->registerResource(rpD, false);
+ return rpD;
}
QVector<quint32> QMetalRenderPassDescriptor::serializedFormat() const
@@ -3067,12 +4180,14 @@ QMetalTextureRenderTarget::~QMetalTextureRenderTarget()
void QMetalTextureRenderTarget::destroy()
{
- // nothing to do here
+ QRHI_RES_RHI(QRhiMetal);
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
QRhiRenderPassDescriptor *QMetalTextureRenderTarget::newCompatibleRenderPassDescriptor()
{
- const int colorAttachmentCount = m_desc.cendColorAttachments() - m_desc.cbeginColorAttachments();
+ const int colorAttachmentCount = int(m_desc.colorAttachmentCount());
QMetalRenderPassDescriptor *rpD = new QMetalRenderPassDescriptor(m_rhi);
rpD->colorAttachmentCount = colorAttachmentCount;
rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
@@ -3090,14 +4205,16 @@ QRhiRenderPassDescriptor *QMetalTextureRenderTarget::newCompatibleRenderPassDesc
rpD->dsFormat = int(QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
rpD->updateSerializedFormat();
+
+ QRHI_RES_RHI(QRhiMetal);
+ rhiD->registerResource(rpD, false);
return rpD;
}
bool QMetalTextureRenderTarget::create()
{
QRHI_RES_RHI(QRhiMetal);
- const bool hasColorAttachments = m_desc.cbeginColorAttachments() != m_desc.cendColorAttachments();
- Q_ASSERT(hasColorAttachments || m_desc.depthTexture());
+ Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
@@ -3141,8 +4258,9 @@ bool QMetalTextureRenderTarget::create()
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;
+ d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
+ d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
+ d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
if (d->colorAttCount == 0) {
d->pixelSize = depthTexD->pixelSize();
d->sampleCount = depthTexD->samples;
@@ -3152,18 +4270,27 @@ bool QMetalTextureRenderTarget::create()
d->fb.dsTex = depthRbD->d->tex;
d->fb.hasStencil = true;
d->fb.depthNeedsStore = false;
+ d->fb.preserveDs = false;
if (d->colorAttCount == 0) {
d->pixelSize = depthRbD->pixelSize();
d->sampleCount = depthRbD->samples;
}
}
+ if (m_desc.depthResolveTexture()) {
+ QMetalTexture *depthResolveTexD = QRHI_RES(QMetalTexture, m_desc.depthResolveTexture());
+ d->fb.dsResolveTex = depthResolveTexD->d->tex;
+ }
d->dsAttCount = 1;
} else {
d->dsAttCount = 0;
}
+ if (d->colorAttCount > 0)
+ d->fb.preserveColor = m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
+
QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
+ rhiD->registerResource(this, false);
return true;
}
@@ -3199,6 +4326,10 @@ void QMetalShaderResourceBindings::destroy()
{
sortedBindings.clear();
maxBinding = -1;
+
+ QRHI_RES_RHI(QRhiMetal);
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
bool QMetalShaderResourceBindings::create()
@@ -3213,13 +4344,9 @@ bool QMetalShaderResourceBindings::create()
rhiD->updateLayoutDesc(this);
std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
- std::sort(sortedBindings.begin(), sortedBindings.end(),
- [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
- {
- return a.data()->binding < b.data()->binding;
- });
+ std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
if (!sortedBindings.isEmpty())
- maxBinding = sortedBindings.last().data()->binding;
+ maxBinding = QRhiImplementation::shaderResourceBindingData(sortedBindings.last())->binding;
else
maxBinding = -1;
@@ -3229,6 +4356,7 @@ bool QMetalShaderResourceBindings::create()
memset(&bd, 0, sizeof(BoundResourceData));
generation += 1;
+ rhiD->registerResource(this, false);
return true;
}
@@ -3236,13 +4364,8 @@ void QMetalShaderResourceBindings::updateResources(UpdateFlags flags)
{
sortedBindings.clear();
std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
- if (!flags.testFlag(BindingsAreSorted)) {
- std::sort(sortedBindings.begin(), sortedBindings.end(),
- [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
- {
- return a.data()->binding < b.data()->binding;
- });
- }
+ if (!flags.testFlag(BindingsAreSorted))
+ std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
for (BoundResourceData &bd : boundResourceData)
memset(&bd, 0, sizeof(BoundResourceData));
@@ -3254,6 +4377,8 @@ QMetalGraphicsPipeline::QMetalGraphicsPipeline(QRhiImplementation *rhi)
: QRhiGraphicsPipeline(rhi),
d(new QMetalGraphicsPipelineData)
{
+ d->q = this;
+ d->tess.q = d;
}
QMetalGraphicsPipeline::~QMetalGraphicsPipeline()
@@ -3267,18 +4392,45 @@ void QMetalGraphicsPipeline::destroy()
d->vs.destroy();
d->fs.destroy();
- [d->ds release];
- d->ds = nil;
+ d->tess.compVs[0].destroy();
+ d->tess.compVs[1].destroy();
+ d->tess.compVs[2].destroy();
- if (!d->ps)
+ d->tess.compTesc.destroy();
+ d->tess.vertTese.destroy();
+
+ qDeleteAll(d->extraBufMgr.deviceLocalWorkBuffers);
+ d->extraBufMgr.deviceLocalWorkBuffers.clear();
+ qDeleteAll(d->extraBufMgr.hostVisibleWorkBuffers);
+ d->extraBufMgr.hostVisibleWorkBuffers.clear();
+
+ delete d->bufferSizeBuffer;
+ d->bufferSizeBuffer = nullptr;
+
+ if (!d->ps && !d->ds
+ && !d->tess.vertexComputeState[0] && !d->tess.vertexComputeState[1] && !d->tess.vertexComputeState[2]
+ && !d->tess.tessControlComputeState)
+ {
return;
+ }
- [d->ps release];
+ QRhiMetalData::DeferredReleaseEntry e;
+ e.type = QRhiMetalData::DeferredReleaseEntry::GraphicsPipeline;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+ e.graphicsPipeline.pipelineState = d->ps;
+ e.graphicsPipeline.depthStencilState = d->ds;
+ e.graphicsPipeline.tessVertexComputeState = d->tess.vertexComputeState;
+ e.graphicsPipeline.tessTessControlComputeState = d->tess.tessControlComputeState;
d->ps = nil;
+ d->ds = nil;
+ d->tess.vertexComputeState = {};
+ d->tess.tessControlComputeState = nil;
QRHI_RES_RHI(QRhiMetal);
- if (rhiD)
+ if (rhiD) {
+ rhiD->d->releaseQueue.append(e);
rhiD->unregisterResource(this);
+ }
}
static inline MTLVertexFormat toMetalAttributeFormat(QRhiVertexInputAttribute::Format format)
@@ -3314,6 +4466,30 @@ static inline MTLVertexFormat toMetalAttributeFormat(QRhiVertexInputAttribute::F
return MTLVertexFormatInt2;
case QRhiVertexInputAttribute::SInt:
return MTLVertexFormatInt;
+ case QRhiVertexInputAttribute::Half4:
+ return MTLVertexFormatHalf4;
+ case QRhiVertexInputAttribute::Half3:
+ return MTLVertexFormatHalf3;
+ case QRhiVertexInputAttribute::Half2:
+ return MTLVertexFormatHalf2;
+ case QRhiVertexInputAttribute::Half:
+ return MTLVertexFormatHalf;
+ case QRhiVertexInputAttribute::UShort4:
+ return MTLVertexFormatUShort4;
+ case QRhiVertexInputAttribute::UShort3:
+ return MTLVertexFormatUShort3;
+ case QRhiVertexInputAttribute::UShort2:
+ return MTLVertexFormatUShort2;
+ case QRhiVertexInputAttribute::UShort:
+ return MTLVertexFormatUShort;
+ case QRhiVertexInputAttribute::SShort4:
+ return MTLVertexFormatShort4;
+ case QRhiVertexInputAttribute::SShort3:
+ return MTLVertexFormatShort3;
+ case QRhiVertexInputAttribute::SShort2:
+ return MTLVertexFormatShort2;
+ case QRhiVertexInputAttribute::SShort:
+ return MTLVertexFormatShort;
default:
Q_UNREACHABLE();
return MTLVertexFormatFloat4;
@@ -3469,6 +4645,24 @@ static inline MTLPrimitiveType toMetalPrimitiveType(QRhiGraphicsPipeline::Topolo
}
}
+static inline MTLPrimitiveTopologyClass toMetalPrimitiveTopologyClass(QRhiGraphicsPipeline::Topology t)
+{
+ switch (t) {
+ case QRhiGraphicsPipeline::Triangles:
+ case QRhiGraphicsPipeline::TriangleStrip:
+ case QRhiGraphicsPipeline::TriangleFan:
+ return MTLPrimitiveTopologyClassTriangle;
+ case QRhiGraphicsPipeline::Lines:
+ case QRhiGraphicsPipeline::LineStrip:
+ return MTLPrimitiveTopologyClassLine;
+ case QRhiGraphicsPipeline::Points:
+ return MTLPrimitiveTopologyClassPoint;
+ default:
+ Q_UNREACHABLE();
+ return MTLPrimitiveTopologyClassTriangle;
+ }
+}
+
static inline MTLCullMode toMetalCullMode(QRhiGraphicsPipeline::CullMode c)
{
switch (c) {
@@ -3497,15 +4691,67 @@ static inline MTLTriangleFillMode toMetalTriangleFillMode(QRhiGraphicsPipeline::
}
}
+static inline MTLWinding toMetalTessellationWindingOrder(QShaderDescription::TessellationWindingOrder w)
+{
+ switch (w) {
+ case QShaderDescription::CwTessellationWindingOrder:
+ return MTLWindingClockwise;
+ case QShaderDescription::CcwTessellationWindingOrder:
+ return MTLWindingCounterClockwise;
+ default:
+ // this is reachable, consider a tess.eval. shader not declaring it, the value is then Unknown
+ return MTLWindingCounterClockwise;
+ }
+}
+
+static inline MTLTessellationPartitionMode toMetalTessellationPartitionMode(QShaderDescription::TessellationPartitioning p)
+{
+ switch (p) {
+ case QShaderDescription::EqualTessellationPartitioning:
+ return MTLTessellationPartitionModePow2;
+ case QShaderDescription::FractionalEvenTessellationPartitioning:
+ return MTLTessellationPartitionModeFractionalEven;
+ case QShaderDescription::FractionalOddTessellationPartitioning:
+ return MTLTessellationPartitionModeFractionalOdd;
+ default:
+ // this is reachable, consider a tess.eval. shader not declaring it, the value is then Unknown
+ return MTLTessellationPartitionModePow2;
+ }
+}
+
+static inline MTLLanguageVersion toMetalLanguageVersion(const QShaderVersion &version)
+{
+ int v = version.version();
+ return MTLLanguageVersion(((v / 10) << 16) + (v % 10));
+}
+
id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
{
- QShaderKey key = { QShader::MetalLibShader, 20, shaderVariant };
- QShaderCode mtllib = shader.shader(key);
- if (mtllib.shader().isEmpty()) {
- key.setSourceVersion(12);
- mtllib = shader.shader(key);
+ QVarLengthArray<int, 8> versions;
+ if (@available(macOS 13, iOS 16, *))
+ versions << 30;
+ if (@available(macOS 12, iOS 15, *))
+ versions << 24;
+ if (@available(macOS 11, iOS 14, *))
+ versions << 23;
+ if (@available(macOS 10.15, iOS 13, *))
+ versions << 22;
+ if (@available(macOS 10.14, iOS 12, *))
+ versions << 21;
+ versions << 20 << 12;
+
+ const QList<QShaderKey> shaders = shader.availableShaders();
+
+ QShaderKey key;
+
+ for (const int &version : versions) {
+ key = { QShader::Source::MetalLibShader, version, shaderVariant };
+ if (shaders.contains(key))
+ break;
}
+
+ QShaderCode mtllib = shader.shader(key);
if (!mtllib.shader().isEmpty()) {
dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
size_t(mtllib.shader().size()),
@@ -3524,12 +4770,13 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
}
}
- key = { QShader::MslShader, 20, shaderVariant };
- QShaderCode mslSource = shader.shader(key);
- if (mslSource.shader().isEmpty()) {
- key.setSourceVersion(12);
- mslSource = shader.shader(key);
+ for (const int &version : versions) {
+ key = { QShader::Source::MslShader, version, shaderVariant };
+ if (shaders.contains(key))
+ break;
}
+
+ QShaderCode mslSource = shader.shader(key);
if (mslSource.shader().isEmpty()) {
qWarning() << "No MSL 2.0 or 1.2 code found in baked shader" << shader;
return nil;
@@ -3537,7 +4784,7 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
- opts.languageVersion = key.sourceVersion() == 20 ? MTLLanguageVersion2_0 : MTLLanguageVersion1_2;
+ opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
NSError *err = nil;
id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
[opts release];
@@ -3559,55 +4806,192 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
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;
+ return [lib newFunctionWithName:[NSString stringWithUTF8String:entryPoint.constData()]];
}
-bool QMetalGraphicsPipeline::create()
+void QMetalGraphicsPipeline::setupAttachmentsInMetalRenderPassDescriptor(void *metalRpDesc, QMetalRenderPassDescriptor *rpD)
{
- if (d->ps)
- destroy();
+ MTLRenderPipelineDescriptor *rpDesc = reinterpret_cast<MTLRenderPipelineDescriptor *>(metalRpDesc);
+
+ 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 (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
+ const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[int(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 defined(Q_OS_MACOS)
+ if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
+#else
+ if (fmt != MTLPixelFormatDepth32Float)
+#endif
+ rpDesc.stencilAttachmentPixelFormat = fmt;
+ }
QRHI_RES_RHI(QRhiMetal);
- if (!rhiD->sanityCheckGraphicsPipeline(this))
- return false;
+ rpDesc.sampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
+}
+
+void QMetalGraphicsPipeline::setupMetalDepthStencilDescriptor(void *metalDsDesc)
+{
+ MTLDepthStencilDescriptor *dsDesc = reinterpret_cast<MTLDepthStencilDescriptor *>(metalDsDesc);
+ 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;
+ }
+}
+
+void QMetalGraphicsPipeline::mapStates()
+{
+ d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
+ d->cullMode = toMetalCullMode(m_cullMode);
+ d->triangleFillMode = toMetalTriangleFillMode(m_polygonMode);
+ d->depthBias = float(m_depthBias);
+ d->slopeScaledDepthBias = m_slopeScaledDepthBias;
+}
+
+void QMetalGraphicsPipelineData::setupVertexInputDescriptor(MTLVertexDescriptor *desc)
+{
// same binding space for vertex and constant buffers - work it around
- const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, m_shaderResourceBindings)->maxBinding + 1;
+ // should be in native resource binding not SPIR-V, but this will work anyway
+ const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, q->shaderResourceBindings())->maxBinding + 1;
- MTLVertexDescriptor *inputLayout = [MTLVertexDescriptor vertexDescriptor];
- for (auto it = m_vertexInputLayout.cbeginAttributes(), itEnd = m_vertexInputLayout.cendAttributes();
+ QRhiVertexInputLayout vertexInputLayout = q->vertexInputLayout();
+ for (auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
it != itEnd; ++it)
{
const uint loc = uint(it->location());
- inputLayout.attributes[loc].format = toMetalAttributeFormat(it->format());
- inputLayout.attributes[loc].offset = NSUInteger(it->offset());
- inputLayout.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
+ desc.attributes[loc].format = decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
+ desc.attributes[loc].offset = NSUInteger(it->offset());
+ desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
}
int bindingIndex = 0;
- for (auto it = m_vertexInputLayout.cbeginBindings(), itEnd = m_vertexInputLayout.cendBindings();
+ const NSUInteger viewCount = qMax<NSUInteger>(1, q->multiViewCount());
+ for (auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
it != itEnd; ++it, ++bindingIndex)
{
const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
- inputLayout.layouts[layoutIdx].stepFunction =
- it->classification() == QRhiVertexInputBinding::PerInstance
- ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
- inputLayout.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
- inputLayout.layouts[layoutIdx].stride = it->stride();
+ desc.layouts[layoutIdx].stepFunction =
+ it->classification() == QRhiVertexInputBinding::PerInstance
+ ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
+ desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
+ if (desc.layouts[layoutIdx].stepFunction == MTLVertexStepFunctionPerInstance)
+ desc.layouts[layoutIdx].stepRate *= viewCount;
+ desc.layouts[layoutIdx].stride = it->stride();
}
+}
- MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
+void QMetalGraphicsPipelineData::setupStageInputDescriptor(MTLStageInputOutputDescriptor *desc)
+{
+ // same binding space for vertex and constant buffers - work it around
+ // should be in native resource binding not SPIR-V, but this will work anyway
+ const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, q->shaderResourceBindings())->maxBinding + 1;
+
+ QRhiVertexInputLayout vertexInputLayout = q->vertexInputLayout();
+ for (auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
+ it != itEnd; ++it)
+ {
+ const uint loc = uint(it->location());
+ desc.attributes[loc].format = decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
+ desc.attributes[loc].offset = NSUInteger(it->offset());
+ desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
+ }
+ int bindingIndex = 0;
+ for (auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
+ it != itEnd; ++it, ++bindingIndex)
+ {
+ const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
+ if (desc.indexBufferIndex) {
+ desc.layouts[layoutIdx].stepFunction =
+ it->classification() == QRhiVertexInputBinding::PerInstance
+ ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridXIndexed;
+ } else {
+ desc.layouts[layoutIdx].stepFunction =
+ it->classification() == QRhiVertexInputBinding::PerInstance
+ ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridX;
+ }
+ desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
+ desc.layouts[layoutIdx].stride = it->stride();
+ }
+}
+
+void QRhiMetalData::trySeedingRenderPipelineFromBinaryArchive(MTLRenderPipelineDescriptor *rpDesc)
+{
+ if (@available(macOS 11.0, iOS 14.0, *)) {
+ if (binArch) {
+ NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
+ rpDesc.binaryArchives = binArchArray;
+ }
+ }
+}
+
+void QRhiMetalData::addRenderPipelineToBinaryArchive(MTLRenderPipelineDescriptor *rpDesc)
+{
+ if (@available(macOS 11.0, iOS 14.0, *)) {
+ if (binArch) {
+ NSError *err = nil;
+ if (![binArch addRenderPipelineFunctionsWithDescriptor: rpDesc error: &err]) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qWarning("Failed to collect render pipeline functions to binary archive: %s", qPrintable(msg));
+ }
+ }
+ }
+}
- rpDesc.vertexDescriptor = inputLayout;
+bool QMetalGraphicsPipeline::createVertexFragmentPipeline()
+{
+ QRHI_RES_RHI(QRhiMetal);
- // mutability cannot be determined (slotted buffers could be set as
+ MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
+ d->setupVertexInputDescriptor(vertexDesc);
+
+ MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
+ rpDesc.vertexDescriptor = vertexDesc;
+
+ // 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
+ // buffers not just the resource binding layout), so leave
+ // rpDesc.vertex/fragmentBuffers at the defaults.
- for (const QRhiShaderStage &shaderStage : qAsConst(m_shaderStages)) {
+ for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
auto cacheIt = rhiD->d->shaderCache.constFind(shaderStage);
if (cacheIt != rhiD->d->shaderCache.constEnd()) {
switch (shaderStage.type()) {
@@ -3654,6 +5038,8 @@ bool QMetalGraphicsPipeline::create()
d->vs.lib = lib;
d->vs.func = func;
d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
+ d->vs.desc = shader.description();
+ d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
rhiD->d->shaderCache.insert(shaderStage, d->vs);
[d->vs.lib retain];
[d->vs.func retain];
@@ -3663,6 +5049,8 @@ bool QMetalGraphicsPipeline::create()
d->fs.lib = lib;
d->fs.func = func;
d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
+ d->fs.desc = shader.description();
+ d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
rhiD->d->shaderCache.insert(shaderStage, d->fs);
[d->fs.lib retain];
[d->fs.func retain];
@@ -3677,86 +5065,838 @@ bool QMetalGraphicsPipeline::create()
}
QMetalRenderPassDescriptor *rpD = QRHI_RES(QMetalRenderPassDescriptor, m_renderPassDesc);
+ setupAttachmentsInMetalRenderPassDescriptor(rpDesc, rpD);
- 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;
+ if (m_multiViewCount >= 2)
+ rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
- Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
- || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
+ rhiD->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
- for (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
- const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[int(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 (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
+ rhiD->d->addRenderPipelineToBinaryArchive(rpDesc);
+
+ NSError *err = nil;
+ d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
+ [rpDesc release];
+ if (!d->ps) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qWarning("Failed to create render pipeline state: %s", qPrintable(msg));
+ return false;
+ }
+
+ MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
+ setupMetalDepthStencilDescriptor(dsDesc);
+ d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
+ [dsDesc release];
+
+ d->primitiveType = toMetalPrimitiveType(m_topology);
+ mapStates();
+
+ return true;
+}
+
+int QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(QShader::Variant vertexCompVariant)
+{
+ switch (vertexCompVariant) {
+ case QShader::NonIndexedVertexAsComputeShader:
+ return 0;
+ case QShader::UInt32IndexedVertexAsComputeShader:
+ return 1;
+ case QShader::UInt16IndexedVertexAsComputeShader:
+ return 2;
+ default:
+ break;
+ }
+ return -1;
+}
+
+id<MTLComputePipelineState> QMetalGraphicsPipelineData::Tessellation::vsCompPipeline(QRhiMetal *rhiD, QShader::Variant vertexCompVariant)
+{
+ const int varIndex = vsCompVariantToIndex(vertexCompVariant);
+ if (varIndex >= 0 && vertexComputeState[varIndex])
+ return vertexComputeState[varIndex];
+
+ id<MTLFunction> func = nil;
+ if (varIndex >= 0)
+ func = compVs[varIndex].func;
+
+ if (!func) {
+ qWarning("No compute function found for vertex shader translated for tessellation, this should not happen");
+ return nil;
+ }
+
+ const QMap<int, int> &ebb(compVs[varIndex].nativeShaderInfo.extraBufferBindings);
+ const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
+
+ MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor new];
+ cpDesc.computeFunction = func;
+ cpDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
+ cpDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor];
+ if (indexBufferBinding >= 0) {
+ if (vertexCompVariant == QShader::UInt32IndexedVertexAsComputeShader) {
+ cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32;
+ cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
+ } else if (vertexCompVariant == QShader::UInt16IndexedVertexAsComputeShader) {
+ cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
+ cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
}
}
+ q->setupStageInputDescriptor(cpDesc.stageInputDescriptor);
- 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;
-#ifdef Q_OS_MACOS
- if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
-#else
- if (fmt != MTLPixelFormatDepth32Float)
-#endif
- rpDesc.stencilAttachmentPixelFormat = fmt;
+ rhiD->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
+
+ if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
+ rhiD->d->addComputePipelineToBinaryArchive(cpDesc);
+
+ NSError *err = nil;
+ id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
+ options: MTLPipelineOptionNone
+ reflection: nil
+ error: &err];
+ [cpDesc release];
+ if (!ps) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qWarning("Failed to create compute pipeline state: %s", qPrintable(msg));
+ } else {
+ vertexComputeState[varIndex] = ps;
}
+ // not retained, the only owner is vertexComputeState and so the QRhiGraphicsPipeline
+ return ps;
+}
- rpDesc.sampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
+id<MTLComputePipelineState> QMetalGraphicsPipelineData::Tessellation::tescCompPipeline(QRhiMetal *rhiD)
+{
+ if (tessControlComputeState)
+ return tessControlComputeState;
+
+ MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor new];
+ cpDesc.computeFunction = compTesc.func;
+
+ rhiD->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
+
+ if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
+ rhiD->d->addComputePipelineToBinaryArchive(cpDesc);
NSError *err = nil;
- d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
- if (!d->ps) {
+ id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
+ options: MTLPipelineOptionNone
+ reflection: nil
+ error: &err];
+ [cpDesc release];
+ if (!ps) {
const QString msg = QString::fromNSString(err.localizedDescription);
- qWarning("Failed to create render pipeline state: %s", qPrintable(msg));
- [rpDesc release];
- return false;
+ qWarning("Failed to create compute pipeline state: %s", qPrintable(msg));
+ } else {
+ tessControlComputeState = ps;
}
+ // not retained, the only owner is tessControlComputeState and so the QRhiGraphicsPipeline
+ return ps;
+}
+
+static inline bool indexTaken(quint32 index, quint64 indices)
+{
+ return (indices >> index) & 0x1;
+}
+
+static inline void takeIndex(quint32 index, quint64 &indices)
+{
+ indices |= 1 << index;
+}
+
+static inline int nextAttributeIndex(quint64 indices)
+{
+ // Maximum number of vertex attributes per vertex descriptor. There does
+ // not appear to be a way to query this from the implementation.
+ // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf indicates
+ // that all GPU families have a value of 31.
+ static const int maxVertexAttributes = 31;
+
+ for (int index = 0; index < maxVertexAttributes; ++index) {
+ if (!indexTaken(index, indices))
+ return index;
+ }
+
+ Q_UNREACHABLE_RETURN(-1);
+}
+
+static inline int aligned(quint32 offset, quint32 alignment)
+{
+ return ((offset + alignment - 1) / alignment) * alignment;
+}
+
+template<typename T>
+static void addUnusedVertexAttribute(const T &variable, QRhiMetal *rhiD, quint32 &offset, quint32 &vertexAlignment)
+{
+
+ int elements = 1;
+ for (const int dim : variable.arrayDims)
+ elements *= dim;
+
+ if (variable.type == QShaderDescription::VariableType::Struct) {
+ for (int element = 0; element < elements; ++element) {
+ for (const auto &member : variable.structMembers) {
+ addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
+ }
+ }
+ } else {
+ const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
+ const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
+
+ // MSL specification 3.0 says alignment = size for non packed scalars and vectors
+ const quint32 alignment = size;
+ vertexAlignment = std::max(vertexAlignment, alignment);
+
+ for (int element = 0; element < elements; ++element) {
+ // adjust alignment
+ offset = aligned(offset, alignment);
+ offset += size;
+ }
+ }
+}
+
+template<typename T>
+static void addVertexAttribute(const T &variable, int binding, QRhiMetal *rhiD, int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
+{
+
+ int elements = 1;
+ for (const int dim : variable.arrayDims)
+ elements *= dim;
+
+ if (variable.type == QShaderDescription::VariableType::Struct) {
+ for (int element = 0; element < elements; ++element) {
+ for (const auto &member : variable.structMembers) {
+ addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
+ }
+ }
+ } else {
+ const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
+ const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
+
+ // MSL specification 3.0 says alignment = size for non packed scalars and vectors
+ const quint32 alignment = size;
+ vertexAlignment = std::max(vertexAlignment, alignment);
+
+ for (int element = 0; element < elements; ++element) {
+ Q_ASSERT(!indexTaken(index, indices));
+
+ // adjust alignment
+ offset = aligned(offset, alignment);
+
+ attributes[index].bufferIndex = binding;
+ attributes[index].format = toMetalAttributeFormat(format);
+ attributes[index].offset = offset;
+
+ takeIndex(index, indices);
+ index++;
+ if (indexTaken(index, indices))
+ index = nextAttributeIndex(indices);
+
+ offset += size;
+ }
+ }
+}
+
+static inline bool matches(const QList<QShaderDescription::BlockVariable> &a, const QList<QShaderDescription::BlockVariable> &b)
+{
+ if (a.size() == b.size()) {
+ bool match = true;
+ for (int i = 0; i < a.size() && match; ++i) {
+ match &= a[i].type == b[i].type
+ && a[i].arrayDims == b[i].arrayDims
+ && matches(a[i].structMembers, b[i].structMembers);
+ }
+ return match;
+ }
+
+ return false;
+}
+
+static inline bool matches(const QShaderDescription::InOutVariable &a, const QShaderDescription::InOutVariable &b)
+{
+ return a.location == b.location
+ && a.type == b.type
+ && a.perPatch == b.perPatch
+ && matches(a.structMembers, b.structMembers);
+}
+
+//
+// Create the tessellation evaluation render pipeline state
+//
+// The tesc runs as a compute shader in a compute pipeline and writes per patch and per patch
+// control point data into separate storage buffers. The tese runs as a vertex shader in a render
+// pipeline. Our task is to generate a render pipeline descriptor for the tese that pulls vertices
+// from these buffers.
+//
+// As the buffers we are pulling vertices from are written by a compute pipeline, they follow the
+// MSL alignment conventions which we must take into account when generating our
+// MTLVertexDescriptor. We must include the user defined tese input attributes, and any builtins
+// that were used.
+//
+// SPIRV-Cross generates the MSL tese shader code with input attribute indices that reflect the
+// specified GLSL locations. Interface blocks are flattened with each member having an incremented
+// attribute index. SPIRV-Cross reports an error on compilation if there are clashes in the index
+// address space.
+//
+// After the user specified attributes are processed, SPIRV-Cross places the in-use builtins at the
+// next available (lowest value) attribute index. Tese builtins are processed in the following
+// order:
+//
+// in gl_PerVertex
+// {
+// vec4 gl_Position;
+// float gl_PointSize;
+// float gl_ClipDistance[];
+// };
+//
+// patch in float gl_TessLevelOuter[4];
+// patch in float gl_TessLevelInner[2];
+//
+// Enumerations in QShaderDescription::BuiltinType are defined in this order.
+//
+// For quads, SPIRV-Cross places MTLQuadTessellationFactorsHalf per patch in the tessellation
+// factor buffer. For triangles it uses MTLTriangleTessellationFactorsHalf.
+//
+// It should be noted that SPIRV-Cross handles the following builtin inputs internally, with no
+// host side support required.
+//
+// in vec3 gl_TessCoord;
+// in int gl_PatchVerticesIn;
+// in int gl_PrimitiveID;
+//
+id<MTLRenderPipelineState> QMetalGraphicsPipelineData::Tessellation::teseFragRenderPipeline(QRhiMetal *rhiD, QMetalGraphicsPipeline *pipeline)
+{
+ if (pipeline->d->ps)
+ return pipeline->d->ps;
+
+ MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
+ MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
+
+ // tesc output buffers
+ const QMap<int, int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
+ const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
+ const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
+ const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
+ quint32 offsetInTescOutput = 0;
+ quint32 offsetInTescPatchOutput = 0;
+ quint32 offsetInTessFactorBuffer = 0;
+ quint32 tescOutputAlignment = 0;
+ quint32 tescPatchOutputAlignment = 0;
+ quint32 tessFactorAlignment = 0;
+ QSet<int> usedBuffers;
+
+ // tesc output variables in ascending location order
+ QMap<int, QShaderDescription::InOutVariable> tescOutVars;
+ for (const auto &tescOutVar : compTesc.desc.outputVariables())
+ tescOutVars[tescOutVar.location] = tescOutVar;
+
+ // tese input variables in ascending location order
+ QMap<int, QShaderDescription::InOutVariable> teseInVars;
+ for (const auto &teseInVar : vertTese.desc.inputVariables())
+ teseInVars[teseInVar.location] = teseInVar;
+
+ // bit mask tracking usage of vertex attribute indices
+ quint64 indices = 0;
+
+ for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
+
+ int index = tescOutVar.location;
+ int binding = -1;
+ quint32 *offset = nullptr;
+ quint32 *alignment = nullptr;
+
+ if (tescOutVar.perPatch) {
+ binding = tescPatchOutputBufferBinding;
+ offset = &offsetInTescPatchOutput;
+ alignment = &tescPatchOutputAlignment;
+ } else {
+ tescOutVar.arrayDims.removeLast();
+ binding = tescOutputBufferBinding;
+ offset = &offsetInTescOutput;
+ alignment = &tescOutputAlignment;
+ }
+
+ if (teseInVars.contains(index)) {
+
+ if (!matches(teseInVars[index], tescOutVar)) {
+ qWarning() << "mismatched tessellation control output -> tesssellation evaluation input at location" << index;
+ qWarning() << " tesc out:" << tescOutVar;
+ qWarning() << " tese in:" << teseInVars[index];
+ }
+
+ if (binding != -1) {
+ addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
+ usedBuffers << binding;
+ } else {
+ qWarning() << "baked tessellation control shader missing output buffer binding information";
+ addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
+ }
+
+ } else {
+ qWarning() << "missing tessellation evaluation input for tessellation control output:" << tescOutVar;
+ addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
+ }
+
+ teseInVars.remove(tescOutVar.location);
+ }
+
+ for (const QShaderDescription::InOutVariable &teseInVar : teseInVars)
+ qWarning() << "missing tessellation control output for tessellation evaluation input:" << teseInVar;
+
+ // tesc output builtins in ascending location order
+ QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
+ for (const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
+ tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
+
+ // tese input builtins in ascending location order
+ QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
+ for (const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
+ teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
+
+ const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
+ bool tessLevelAdded = false;
+
+ for (const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
+
+ QShaderDescription::InOutVariable variable;
+ int binding = -1;
+ quint32 *offset = nullptr;
+ quint32 *alignment = nullptr;
+
+ switch (builtin.type) {
+ case QShaderDescription::BuiltinType::PositionBuiltin:
+ variable.type = QShaderDescription::VariableType::Vec4;
+ binding = tescOutputBufferBinding;
+ offset = &offsetInTescOutput;
+ alignment = &tescOutputAlignment;
+ break;
+ case QShaderDescription::BuiltinType::PointSizeBuiltin:
+ variable.type = QShaderDescription::VariableType::Float;
+ binding = tescOutputBufferBinding;
+ offset = &offsetInTescOutput;
+ alignment = &tescOutputAlignment;
+ break;
+ case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
+ variable.type = QShaderDescription::VariableType::Float;
+ variable.arrayDims = builtin.arrayDims;
+ binding = tescOutputBufferBinding;
+ offset = &offsetInTescOutput;
+ alignment = &tescOutputAlignment;
+ break;
+ case QShaderDescription::BuiltinType::TessLevelOuterBuiltin:
+ variable.type = QShaderDescription::VariableType::Half4;
+ binding = tessFactorBufferBinding;
+ offset = &offsetInTessFactorBuffer;
+ tessLevelAdded = trianglesMode;
+ alignment = &tessFactorAlignment;
+ break;
+ case QShaderDescription::BuiltinType::TessLevelInnerBuiltin:
+ if (trianglesMode) {
+ if (!tessLevelAdded) {
+ variable.type = QShaderDescription::VariableType::Half4;
+ binding = tessFactorBufferBinding;
+ offsetInTessFactorBuffer = 0;
+ offset = &offsetInTessFactorBuffer;
+ alignment = &tessFactorAlignment;
+ tessLevelAdded = true;
+ } else {
+ teseInBuiltins.remove(builtin.type);
+ continue;
+ }
+ } else {
+ variable.type = QShaderDescription::VariableType::Half2;
+ binding = tessFactorBufferBinding;
+ offsetInTessFactorBuffer = 8;
+ offset = &offsetInTessFactorBuffer;
+ alignment = &tessFactorAlignment;
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ if (teseInBuiltins.contains(builtin.type)) {
+ if (binding != -1) {
+ int index = nextAttributeIndex(indices);
+ addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
+ usedBuffers << binding;
+ } else {
+ qWarning() << "baked tessellation control shader missing output buffer binding information";
+ addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
+ }
+ } else {
+ addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
+ }
+
+ teseInBuiltins.remove(builtin.type);
+ }
+
+ for (const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
+ switch (builtin.type) {
+ case QShaderDescription::BuiltinType::PositionBuiltin:
+ case QShaderDescription::BuiltinType::PointSizeBuiltin:
+ case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
+ qWarning() << "missing tessellation control output for tessellation evaluation builtin input:" << builtin;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (usedBuffers.contains(tescOutputBufferBinding)) {
+ vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
+ vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
+ }
+
+ if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
+ vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
+ vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
+ }
+
+ if (usedBuffers.contains(tessFactorBufferBinding)) {
+ vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
+ vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ? sizeof(MTLTriangleTessellationFactorsHalf) : sizeof(MTLQuadTessellationFactorsHalf);
+ }
+
+ rpDesc.vertexDescriptor = vertexDesc;
+ rpDesc.vertexFunction = vertTese.func;
+ rpDesc.fragmentFunction = pipeline->d->fs.func;
+
+ // The portable, cross-API approach is to use CCW, the results are then
+ // identical (assuming the applied clipSpaceCorrMatrix) for all the 3D
+ // APIs. The tess.eval. GLSL shader is thus expected to specify ccw. If it
+ // doesn't, things may not work as expected.
+ rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
+
+ rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
+
+ QMetalRenderPassDescriptor *rpD = QRHI_RES(QMetalRenderPassDescriptor, pipeline->renderPassDescriptor());
+ pipeline->setupAttachmentsInMetalRenderPassDescriptor(rpDesc, rpD);
+
+ rhiD->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
+
+ if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
+ rhiD->d->addRenderPipelineToBinaryArchive(rpDesc);
+
+ NSError *err = nil;
+ id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
[rpDesc release];
+ if (!ps) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qWarning("Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
+ } else {
+ // ps is stored in the QMetalGraphicsPipelineData so the end result in this
+ // regard is no different from what createVertexFragmentPipeline does
+ pipeline->d->ps = ps;
+ }
+ return ps;
+}
- 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;
+QMetalBuffer *QMetalGraphicsPipelineData::ExtraBufferManager::acquireWorkBuffer(QRhiMetal *rhiD, quint32 size, WorkBufType type)
+{
+ QVector<QMetalBuffer *> *workBuffers = type == WorkBufType::DeviceLocal ? &deviceLocalWorkBuffers : &hostVisibleWorkBuffers;
- 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;
+ // Check if something is reusable as-is.
+ for (QMetalBuffer *workBuf : *workBuffers) {
+ if (workBuf && workBuf->lastActiveFrameSlot == -1 && workBuf->size() >= size) {
+ workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
+ return workBuf;
+ }
+ }
+
+ // Once the pool is above a certain threshold, see if there is something
+ // unused (but too small) and recreate that our size.
+ if (workBuffers->count() > QMTL_FRAMES_IN_FLIGHT * 8) {
+ for (QMetalBuffer *workBuf : *workBuffers) {
+ if (workBuf && workBuf->lastActiveFrameSlot == -1) {
+ workBuf->setSize(size);
+ if (workBuf->create()) {
+ workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
+ return workBuf;
+ }
+ }
+ }
}
+ // Add a new buffer to the pool.
+ QMetalBuffer *buf;
+ if (type == WorkBufType::DeviceLocal) {
+ // for GPU->GPU data (non-slotted, not necessarily host writable)
+ buf = new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
+ } else {
+ // for CPU->GPU (non-slotted, host writable/coherent)
+ buf = new QMetalBuffer(rhiD, QRhiBuffer::Dynamic, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
+ }
+ if (buf->create()) {
+ buf->lastActiveFrameSlot = rhiD->currentFrameSlot;
+ workBuffers->append(buf);
+ return buf;
+ }
+
+ qWarning("Failed to acquire work buffer of size %u", size);
+ return nullptr;
+}
+
+bool QMetalGraphicsPipeline::createTessellationPipelines(const QShader &tessVert, const QShader &tesc, const QShader &tese, const QShader &tessFrag)
+{
+ QRHI_RES_RHI(QRhiMetal);
+ QString error;
+ QByteArray entryPoint;
+ QShaderKey activeKey;
+
+ const QShaderDescription tescDesc = tesc.description();
+ const QShaderDescription teseDesc = tese.description();
+ d->tess.inControlPointCount = uint(m_patchControlPointCount);
+ d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
+ if (!d->tess.outControlPointCount)
+ d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
+
+ if (!d->tess.outControlPointCount) {
+ qWarning("Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
+ d->tess.enabled = false;
+ d->tess.failed = true;
+ return false;
+ }
+
+ if (m_multiViewCount >= 2)
+ qWarning("Multiview is not supported with tessellation");
+
+ // Now the vertex shader is a compute shader.
+ // It should have three dedicated *VertexAsComputeShader variants.
+ // What the requested variant was (Standard or Batchable) plays no role here.
+ // (the Qt Quick scenegraph does not use tessellation with its materials)
+ // Create all three versions.
+
+ bool variantsPresent[3] = {};
+ const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
+ for (const QShaderKey &k : tessVertKeys) {
+ switch (k.sourceVariant()) {
+ case QShader::NonIndexedVertexAsComputeShader:
+ variantsPresent[0] = true;
+ break;
+ case QShader::UInt32IndexedVertexAsComputeShader:
+ variantsPresent[1] = true;
+ break;
+ case QShader::UInt16IndexedVertexAsComputeShader:
+ variantsPresent[2] = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
+ qWarning("Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
+ "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
+ "Try passing --msltess to qsb.");
+ d->tess.enabled = false;
+ d->tess.failed = true;
+ return false;
+ }
+
+ int varIndex = 0; // Will map NonIndexed as 0, UInt32 as 1, UInt16 as 2. Do not change this ordering.
+ for (QShader::Variant variant : {
+ QShader::NonIndexedVertexAsComputeShader,
+ QShader::UInt32IndexedVertexAsComputeShader,
+ QShader::UInt16IndexedVertexAsComputeShader })
+ {
+ id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant, &error, &entryPoint, &activeKey);
+ if (!lib) {
+ qWarning("MSL shader compilation failed for vertex-as-compute shader %d: %s", int(variant), qPrintable(error));
+ d->tess.enabled = false;
+ d->tess.failed = true;
+ 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];
+ d->tess.enabled = false;
+ d->tess.failed = true;
+ return false;
+ }
+ QMetalShader &compVs(d->tess.compVs[varIndex]);
+ compVs.lib = lib;
+ compVs.func = func;
+ compVs.desc = tessVert.description();
+ compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
+ compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
+
+ // pre-create all three MTLComputePipelineStates
+ if (!d->tess.vsCompPipeline(rhiD, variant)) {
+ qWarning("Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)", int(variant));
+ d->tess.enabled = false;
+ d->tess.failed = true;
+ return false;
+ }
+
+ ++varIndex;
+ }
+
+ // Pipeline #2 is a compute that runs the tessellation control (compute) shader
+ id<MTLLibrary> tessControlLib = rhiD->d->createMetalLib(tesc, QShader::StandardShader, &error, &entryPoint, &activeKey);
+ if (!tessControlLib) {
+ qWarning("MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
+ d->tess.enabled = false;
+ d->tess.failed = true;
+ return false;
+ }
+ id<MTLFunction> tessControlFunc = rhiD->d->createMSLShaderFunction(tessControlLib, entryPoint);
+ if (!tessControlFunc) {
+ qWarning("MSL function for entry point %s not found", entryPoint.constData());
+ [tessControlLib release];
+ d->tess.enabled = false;
+ d->tess.failed = true;
+ return false;
+ }
+ d->tess.compTesc.lib = tessControlLib;
+ d->tess.compTesc.func = tessControlFunc;
+ d->tess.compTesc.desc = tesc.description();
+ d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
+ d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
+ if (!d->tess.tescCompPipeline(rhiD)) {
+ qWarning("Failed to pre-generate compute pipeline for tessellation control shader");
+ d->tess.enabled = false;
+ d->tess.failed = true;
+ return false;
+ }
+
+ // Pipeline #3 is a render pipeline with the tessellation evaluation (vertex) + the fragment shader
+ id<MTLLibrary> tessEvalLib = rhiD->d->createMetalLib(tese, QShader::StandardShader, &error, &entryPoint, &activeKey);
+ if (!tessEvalLib) {
+ qWarning("MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
+ d->tess.enabled = false;
+ d->tess.failed = true;
+ return false;
+ }
+ id<MTLFunction> tessEvalFunc = rhiD->d->createMSLShaderFunction(tessEvalLib, entryPoint);
+ if (!tessEvalFunc) {
+ qWarning("MSL function for entry point %s not found", entryPoint.constData());
+ [tessEvalLib release];
+ d->tess.enabled = false;
+ d->tess.failed = true;
+ return false;
+ }
+ d->tess.vertTese.lib = tessEvalLib;
+ d->tess.vertTese.func = tessEvalFunc;
+ d->tess.vertTese.desc = tese.description();
+ d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
+ d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
+
+ id<MTLLibrary> fragLib = rhiD->d->createMetalLib(tessFrag, QShader::StandardShader, &error, &entryPoint, &activeKey);
+ if (!fragLib) {
+ qWarning("MSL shader compilation failed for fragment shader: %s", qPrintable(error));
+ d->tess.enabled = false;
+ d->tess.failed = true;
+ return false;
+ }
+ id<MTLFunction> fragFunc = rhiD->d->createMSLShaderFunction(fragLib, entryPoint);
+ if (!fragFunc) {
+ qWarning("MSL function for entry point %s not found", entryPoint.constData());
+ [fragLib release];
+ d->tess.enabled = false;
+ d->tess.failed = true;
+ return false;
+ }
+ d->fs.lib = fragLib;
+ d->fs.func = fragFunc;
+ d->fs.desc = tessFrag.description();
+ d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
+ d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
+
+ if (!d->tess.teseFragRenderPipeline(rhiD, this)) {
+ qWarning("Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
+ d->tess.enabled = false;
+ d->tess.failed = true;
+ return false;
+ }
+
+ MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
+ setupMetalDepthStencilDescriptor(dsDesc);
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);
- d->triangleFillMode = toMetalTriangleFillMode(m_polygonMode);
- d->depthBias = float(m_depthBias);
- d->slopeScaledDepthBias = m_slopeScaledDepthBias;
+ // no primitiveType
+ mapStates();
+
+ return true;
+}
+
+bool QMetalGraphicsPipeline::create()
+{
+ destroy(); // no early test, always invoke and leave it to destroy to decide what to clean up
+ QRHI_RES_RHI(QRhiMetal);
+ rhiD->pipelineCreationStart();
+ if (!rhiD->sanityCheckGraphicsPipeline(this))
+ return false;
+
+ // See if tessellation is involved. Things will be very different, if so.
+ QShader tessVert;
+ QShader tesc;
+ QShader tese;
+ QShader tessFrag;
+ for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
+ switch (shaderStage.type()) {
+ case QRhiShaderStage::Vertex:
+ tessVert = shaderStage.shader();
+ break;
+ case QRhiShaderStage::TessellationControl:
+ tesc = shaderStage.shader();
+ break;
+ case QRhiShaderStage::TessellationEvaluation:
+ tese = shaderStage.shader();
+ break;
+ case QRhiShaderStage::Fragment:
+ tessFrag = shaderStage.shader();
+ break;
+ default:
+ break;
+ }
+ }
+ d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
+ d->tess.failed = false;
+
+ bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
+ if (!ok)
+ return false;
+
+ // SPIRV-Cross buffer size buffers
+ int buffers = 0;
+ QVarLengthArray<QMetalShader *, 6> shaders;
+ if (d->tess.enabled) {
+ shaders.append(&d->tess.compVs[0]);
+ shaders.append(&d->tess.compVs[1]);
+ shaders.append(&d->tess.compVs[2]);
+ shaders.append(&d->tess.compTesc);
+ shaders.append(&d->tess.vertTese);
+ } else {
+ shaders.append(&d->vs);
+ }
+ shaders.append(&d->fs);
+
+ for (QMetalShader *shader : shaders) {
+ if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
+ const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
+ shader->nativeResourceBindingMap[binding] = qMakePair(binding, -1);
+ int maxNativeBinding = 0;
+ for (const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
+ maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
+
+ // we use one buffer to hold data for all graphics shader stages, each with a different offset.
+ // buffer offsets must be 32byte aligned - adjust buffer count accordingly
+ buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
+ }
+ }
+
+ if (buffers) {
+ if (!d->bufferSizeBuffer)
+ d->bufferSizeBuffer = new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers * sizeof(int));
+
+ d->bufferSizeBuffer->setSize(buffers * sizeof(int));
+ d->bufferSizeBuffer->create();
+ }
+
+ rhiD->pipelineCreationEnd();
lastActiveFrameSlot = -1;
generation += 1;
rhiD->registerResource(this);
@@ -3782,12 +5922,43 @@ void QMetalComputePipeline::destroy()
if (!d->ps)
return;
- [d->ps release];
+ delete d->bufferSizeBuffer;
+ d->bufferSizeBuffer = nullptr;
+
+ QRhiMetalData::DeferredReleaseEntry e;
+ e.type = QRhiMetalData::DeferredReleaseEntry::ComputePipeline;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+ e.computePipeline.pipelineState = d->ps;
d->ps = nil;
QRHI_RES_RHI(QRhiMetal);
- if (rhiD)
+ if (rhiD) {
+ rhiD->d->releaseQueue.append(e);
rhiD->unregisterResource(this);
+ }
+}
+
+void QRhiMetalData::trySeedingComputePipelineFromBinaryArchive(MTLComputePipelineDescriptor *cpDesc)
+{
+ if (@available(macOS 11.0, iOS 14.0, *)) {
+ if (binArch) {
+ NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
+ cpDesc.binaryArchives = binArchArray;
+ }
+ }
+}
+
+void QRhiMetalData::addComputePipelineToBinaryArchive(MTLComputePipelineDescriptor *cpDesc)
+{
+ if (@available(macOS 11.0, iOS 14.0, *)) {
+ if (binArch) {
+ NSError *err = nil;
+ if (![binArch addComputePipelineFunctionsWithDescriptor: cpDesc error: &err]) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qWarning("Failed to collect compute pipeline functions to binary archive: %s", qPrintable(msg));
+ }
+ }
+ }
}
bool QMetalComputePipeline::create()
@@ -3796,6 +5967,7 @@ bool QMetalComputePipeline::create()
destroy();
QRHI_RES_RHI(QRhiMetal);
+ rhiD->pipelineCreationStart();
auto cacheIt = rhiD->d->shaderCache.constFind(m_shaderStage);
if (cacheIt != rhiD->d->shaderCache.constEnd()) {
@@ -3821,6 +5993,14 @@ bool QMetalComputePipeline::create()
d->cs.func = func;
d->cs.localSize = shader.description().computeShaderLocalSize();
d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
+ d->cs.desc = shader.description();
+ d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
+
+ // SPIRV-Cross buffer size buffers
+ if (d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
+ const int binding = d->cs.nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
+ d->cs.nativeResourceBindingMap[binding] = qMakePair(binding, -1);
+ }
if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
for (QMetalShader &s : rhiD->d->shaderCache)
@@ -3835,14 +6015,42 @@ bool QMetalComputePipeline::create()
d->localSize = MTLSizeMake(d->cs.localSize[0], d->cs.localSize[1], d->cs.localSize[2]);
+ MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor new];
+ cpDesc.computeFunction = d->cs.func;
+
+ rhiD->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
+
+ if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
+ rhiD->d->addComputePipelineToBinaryArchive(cpDesc);
+
NSError *err = nil;
- d->ps = [rhiD->d->dev newComputePipelineStateWithFunction: d->cs.func error: &err];
+ d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
+ options: MTLPipelineOptionNone
+ reflection: nil
+ error: &err];
+ [cpDesc release];
if (!d->ps) {
const QString msg = QString::fromNSString(err.localizedDescription);
- qWarning("Failed to create render pipeline state: %s", qPrintable(msg));
+ qWarning("Failed to create compute pipeline state: %s", qPrintable(msg));
return false;
}
+ // SPIRV-Cross buffer size buffers
+ if (d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
+ int buffers = 0;
+ for (const QShaderDescription::StorageBlock &block : d->cs.desc.storageBlocks())
+ buffers = qMax(buffers, d->cs.nativeResourceBindingMap[block.binding].first);
+
+ buffers += 1;
+
+ if (!d->bufferSizeBuffer)
+ d->bufferSizeBuffer = new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers * sizeof(int));
+
+ d->bufferSizeBuffer->setSize(buffers * sizeof(int));
+ d->bufferSizeBuffer->create();
+ }
+
+ rhiD->pipelineCreationEnd();
lastActiveFrameSlot = -1;
generation += 1;
rhiD->registerResource(this);
@@ -3874,10 +6082,12 @@ const QRhiNativeHandles *QMetalCommandBuffer::nativeHandles()
return &nativeHandlesStruct;
}
-void QMetalCommandBuffer::resetState()
+void QMetalCommandBuffer::resetState(double lastGpuTime)
{
+ d->lastGpuTime = lastGpuTime;
d->currentRenderPassEncoder = nil;
d->currentComputePassEncoder = nil;
+ d->tessellationComputeEncoder = nil;
d->currentPassRpDesc = nil;
resetPerPassState();
}
@@ -3906,6 +6116,8 @@ void QMetalCommandBuffer::resetPerPassCachedState()
currentFrontFaceWinding = -1;
currentDepthBiasValues = { 0.0f, 0.0f };
+ d->currentShaderResourceBindingState = {};
+ d->currentDepthStencilState = nil;
d->currentFirstVertexBinding = -1;
d->currentVertexInputsBuffers.clear();
d->currentVertexInputOffsets.clear();
@@ -3937,8 +6149,7 @@ void QMetalSwapChain::destroy()
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]);
+ waitUntilCompleted(i);
dispatch_release(d->sem[i]);
d->sem[i] = nullptr;
@@ -3950,7 +6161,14 @@ void QMetalSwapChain::destroy()
d->msaaTex[i] = nil;
}
+#ifdef Q_OS_MACOS
+ d->liveResizeStartObserver.remove();
+ d->liveResizeEndObserver.remove();
+ d->liveResizeObserverSet = false;
+#endif
+
d->layer = nullptr;
+ m_proxyData = {};
[d->curDrawable release];
d->curDrawable = nil;
@@ -3972,6 +6190,9 @@ QRhiRenderTarget *QMetalSwapChain::currentFrameRenderTarget()
return &rtWrapper;
}
+// view.layer should ideally be called on the main thread, otherwise the UI
+// Thread Checker in Xcode drops a warning. Hence trying to proxy it through
+// QRhiSwapChainProxyData instead of just calling this function directly.
static inline CAMetalLayer *layerForWindow(QWindow *window)
{
Q_ASSERT(window);
@@ -3984,30 +6205,51 @@ static inline CAMetalLayer *layerForWindow(QWindow *window)
return static_cast<CAMetalLayer *>(view.layer);
}
+// If someone calls this, it is hopefully from the main thread, and they will
+// then set the returned data on the QRhiSwapChain, so it won't need to query
+// the layer on its own later on.
+QRhiSwapChainProxyData QRhiMetal::updateSwapChainProxyData(QWindow *window)
+{
+ QRhiSwapChainProxyData d;
+ d.reserved[0] = layerForWindow(window);
+ return d;
+}
+
QSize QMetalSwapChain::surfacePixelSize()
{
Q_ASSERT(m_window);
CAMetalLayer *layer = d->layer;
if (!layer)
- layer = layerForWindow(m_window);
+ layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
- CGSize layerSize = layer.bounds.size;
- layerSize.width *= layer.contentsScale;
- layerSize.height *= layer.contentsScale;
- return QSizeF::fromCGSize(layerSize).toSize();
+ Q_ASSERT(layer);
+ int height = (int)layer.bounds.size.height;
+ int width = (int)layer.bounds.size.width;
+ width *= layer.contentsScale;
+ height *= layer.contentsScale;
+ return QSize(width, height);
}
bool QMetalSwapChain::isFormatSupported(Format f)
{
-#ifdef Q_OS_MACOS
- if (@available(macOS 10.12, /*iOS 10.0,*/ *))
- return f == SDR || f == HDRExtendedSrgbLinear;
-#endif
+ if (f == HDRExtendedSrgbLinear) {
+ if (@available(macOS 10.11, iOS 16.0, *))
+ return hdrInfo().limits.colorComponentValue.maxPotentialColorComponentValue > 1.0f;
+ else
+ return false;
+ } else if (f == HDRExtendedDisplayP3Linear) {
+ if (@available(macOS 11.0, iOS 14.0, *))
+ return hdrInfo().limits.colorComponentValue.maxPotentialColorComponentValue > 1.0f;
+ else
+ return false;
+ }
return f == SDR;
}
QRhiRenderPassDescriptor *QMetalSwapChain::newCompatibleRenderPassDescriptor()
{
+ QRHI_RES_RHI(QRhiMetal);
+
chooseFormats(); // ensure colorFormat and similar are filled out
QMetalRenderPassDescriptor *rpD = new QMetalRenderPassDescriptor(m_rhi);
@@ -4018,7 +6260,6 @@ QRhiRenderPassDescriptor *QMetalSwapChain::newCompatibleRenderPassDescriptor()
#ifdef Q_OS_MACOS
// m_depthStencil may not be built yet so cannot rely on computed fields in it
- QRHI_RES_RHI(QRhiMetal);
rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
#else
@@ -4026,6 +6267,8 @@ QRhiRenderPassDescriptor *QMetalSwapChain::newCompatibleRenderPassDescriptor()
#endif
rpD->updateSerializedFormat();
+
+ rhiD->registerResource(rpD, false);
return rpD;
}
@@ -4034,17 +6277,26 @@ void QMetalSwapChain::chooseFormats()
QRHI_RES_RHI(QRhiMetal);
samples = rhiD->effectiveSampleCount(m_sampleCount);
// pick a format that is allowed for CAMetalLayer.pixelFormat
- if (m_format == HDRExtendedSrgbLinear) {
- if (@available(macOS 10.12, /*iOS 10.0,*/ *)) {
- d->colorFormat = MTLPixelFormatRGBA16Float;
- d->rhiColorFormat = QRhiTexture::RGBA16F;
- return;
- }
+ if (m_format == HDRExtendedSrgbLinear || m_format == HDRExtendedDisplayP3Linear) {
+ d->colorFormat = MTLPixelFormatRGBA16Float;
+ d->rhiColorFormat = QRhiTexture::RGBA16F;
+ return;
}
d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
d->rhiColorFormat = QRhiTexture::BGRA8;
}
+void QMetalSwapChain::waitUntilCompleted(int slot)
+{
+ // 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 = d->sem[slot];
+ dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
+ dispatch_semaphore_signal(sem);
+}
+
bool QMetalSwapChain::createOrResize()
{
Q_ASSERT(m_window);
@@ -4066,20 +6318,24 @@ bool QMetalSwapChain::createOrResize()
return false;
}
- d->layer = layerForWindow(window);
+ d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
Q_ASSERT(d->layer);
chooseFormats();
if (d->colorFormat != d->layer.pixelFormat)
d->layer.pixelFormat = d->colorFormat;
-#ifdef Q_OS_MACOS
- if (@available(macOS 10.12, /*iOS 10.0,*/ *)) { // Can't enable this on iOS until wantsExtendedDynamicRangeContent is available
- if (m_format == HDRExtendedSrgbLinear) {
+
+ if (m_format == HDRExtendedSrgbLinear) {
+ if (@available(macOS 10.11, iOS 16.0, *)) {
d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
d->layer.wantsExtendedDynamicRangeContent = YES;
}
+ } else if (m_format == HDRExtendedDisplayP3Linear) {
+ if (@available(macOS 11.0, iOS 16.0, *)) {
+ d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
+ d->layer.wantsExtendedDynamicRangeContent = YES;
+ }
}
-#endif
if (m_flags.testFlag(UsedAsTransferSource))
d->layer.framebufferOnly = NO;
@@ -4104,9 +6360,12 @@ bool QMetalSwapChain::createOrResize()
// Now set the layer's drawableSize which will stay set to the same value
// until the next createOrResize(), thus ensuring atomicity with regards to
// the drawable size in frames.
- CGSize layerSize = d->layer.bounds.size;
- layerSize.width *= d->layer.contentsScale;
- layerSize.height *= d->layer.contentsScale;
+ int width = (int)d->layer.bounds.size.width;
+ int height = (int)d->layer.bounds.size.height;
+ CGSize layerSize = CGSizeMake(width, height);
+ const float scaleFactor = d->layer.contentsScale;
+ layerSize.width *= scaleFactor;
+ layerSize.height *= scaleFactor;
d->layer.drawableSize = layerSize;
m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
@@ -4114,10 +6373,39 @@ bool QMetalSwapChain::createOrResize()
[d->layer setDevice: rhiD->d->dev];
+#ifdef Q_OS_MACOS
+ // Can only use presentsWithTransaction (to get smooth resizing) when
+ // presenting from the main (gui) thread. We predict that based on the
+ // thread this function is called on since if the QRhiSwapChain is
+ // initialied on a given thread then that's almost certainly the thread on
+ // which the QRhi renders and presents.
+ const bool canUsePresentsWithTransaction = NSThread.isMainThread;
+
+ // Have an env.var. just in case it turns out presentsWithTransaction is
+ // not desired in some specific case.
+ static bool allowPresentsWithTransaction = !qEnvironmentVariableIntValue("QT_MTL_NO_TRANSACTION");
+
+ if (allowPresentsWithTransaction && canUsePresentsWithTransaction && !d->liveResizeObserverSet) {
+ d->liveResizeObserverSet = true;
+ NSView *view = reinterpret_cast<NSView *>(window->winId());
+ NSWindow *window = view.window;
+ if (window) {
+ qCDebug(QRHI_LOG_INFO, "will set presentsWithTransaction during live resize");
+ d->liveResizeStartObserver = QMacNotificationObserver(window, NSWindowWillStartLiveResizeNotification, [this] {
+ d->layer.presentsWithTransaction = true;
+ });
+ d->liveResizeEndObserver = QMacNotificationObserver(window, NSWindowDidEndLiveResizeNotification, [this] {
+ d->layer.presentsWithTransaction = false;
+ });
+ }
+ }
+#endif
+
[d->curDrawable release];
d->curDrawable = nil;
for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ d->lastGpuTime[i] = 0;
if (!d->sem[i])
d->sem[i] = dispatch_semaphore_create(QMTL_FRAMES_IN_FLIGHT - 1);
}
@@ -4145,12 +6433,13 @@ bool QMetalSwapChain::createOrResize()
rtWrapper.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
rtWrapper.d->pixelSize = pixelSize;
- rtWrapper.d->dpr = float(window->devicePixelRatio());
+ rtWrapper.d->dpr = scaleFactor;
rtWrapper.d->sampleCount = samples;
rtWrapper.d->colorAttCount = 1;
rtWrapper.d->dsAttCount = ds ? 1 : 0;
- qCDebug(QRHI_LOG_INFO, "got CAMetalLayer, size %dx%d", pixelSize.width(), pixelSize.height());
+ qCDebug(QRHI_LOG_INFO, "got CAMetalLayer, pixel size %dx%d (scale %.2f)",
+ pixelSize.width(), pixelSize.height(), scaleFactor);
if (samples > 1) {
MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
@@ -4179,21 +6468,28 @@ QRhiSwapChainHdrInfo QMetalSwapChain::hdrInfo()
{
QRhiSwapChainHdrInfo info;
info.limitsType = QRhiSwapChainHdrInfo::ColorComponentValue;
- if (m_format == SDR) {
- info.limits.colorComponentValue.maxColorComponentValue = 1;
- return info;
- }
+ info.limits.colorComponentValue.maxColorComponentValue = 1;
+ info.limits.colorComponentValue.maxPotentialColorComponentValue = 1;
+ info.luminanceBehavior = QRhiSwapChainHdrInfo::DisplayReferred; // 1.0 = SDR white
+ info.sdrWhiteLevel = 200; // typical value, but dummy (don't know the real one); won't matter due to being display-referred
-#ifdef Q_OS_MACOS
- info.isHardCodedDefaults = false;
- NSView *view = reinterpret_cast<NSView *>(window->winId());
- info.limits.colorComponentValue.maxColorComponentValue = view.window.screen.maximumExtendedDynamicRangeColorComponentValue;
-#else
- // ### Fixme: Maybe retrieve the brightness from the screen and if we're not at full brightness we might be able to do more.
- // For now, assume 2, in line with iPhone 12 specs that claim 625 nits max brightness and 1200 nits max HDR brightness.
- info.isHardCodedDefaults = true;
- info.limits.colorComponentValue.maxColorComponentValue = 2;
+ if (m_window) {
+ // Must use m_window, not window, given this may be called before createOrResize().
+#if defined(Q_OS_MACOS)
+ NSView *view = reinterpret_cast<NSView *>(m_window->winId());
+ NSScreen *screen = view.window.screen;
+ info.limits.colorComponentValue.maxColorComponentValue = screen.maximumExtendedDynamicRangeColorComponentValue;
+ info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
+#elif defined(Q_OS_IOS)
+ if (@available(iOS 16.0, *)) {
+ UIView *view = reinterpret_cast<UIView *>(m_window->winId());
+ UIScreen *screen = view.window.windowScene.screen;
+ info.limits.colorComponentValue.maxColorComponentValue = view.window.windowScene.screen.currentEDRHeadroom;
+ info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.potentialEDRHeadroom;
+ }
#endif
+ }
+
return info;
}
diff --git a/src/gui/rhi/qrhimetal_p.h b/src/gui/rhi/qrhimetal_p.h
index 016bf749e2..f539148b2c 100644
--- a/src/gui/rhi/qrhimetal_p.h
+++ b/src/gui/rhi/qrhimetal_p.h
@@ -1,8 +1,8 @@
-// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QRHIMETAL_H
-#define QRHIMETAL_H
+#ifndef QRHIMETAL_P_H
+#define QRHIMETAL_P_H
//
// W A R N I N G
@@ -15,29 +15,494 @@
// We mean it.
//
-#include <private/qrhi_p.h>
-
-Q_FORWARD_DECLARE_OBJC_CLASS(MTLDevice);
-Q_FORWARD_DECLARE_OBJC_CLASS(MTLCommandQueue);
-Q_FORWARD_DECLARE_OBJC_CLASS(MTLCommandBuffer);
-Q_FORWARD_DECLARE_OBJC_CLASS(MTLRenderCommandEncoder);
+#include "qrhi_p.h"
+#include <QWindow>
QT_BEGIN_NAMESPACE
-struct Q_GUI_EXPORT QRhiMetalInitParams : public QRhiInitParams
+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, quint32 size);
+ ~QMetalBuffer();
+ void destroy() override;
+ bool create() override;
+ QRhiBuffer::NativeBuffer nativeBuffer() override;
+ char *beginFullDynamicBufferUpdateForCurrentFrame() override;
+ void endFullDynamicBufferUpdateForCurrentFrame() override;
+
+ QMetalBufferData *d;
+ uint generation = 0;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiMetal;
+ friend struct QMetalShaderResourceBindings;
+
+ static constexpr int WorkBufPoolUsage = 1 << 8;
+ static_assert(WorkBufPoolUsage > QRhiBuffer::StorageBuffer);
+};
+
+struct QMetalRenderBufferData;
+
+struct QMetalRenderBuffer : public QRhiRenderBuffer
+{
+ QMetalRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags,
+ QRhiTexture::Format backingFormatHint);
+ ~QMetalRenderBuffer();
+ void destroy() override;
+ bool create() 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 depth,
+ int arraySize, int sampleCount, Flags flags);
+ ~QMetalTexture();
+ void destroy() override;
+ bool create() override;
+ bool createFrom(NativeTexture src) override;
+ NativeTexture nativeTexture() override;
+
+ bool prepareCreate(QSize *adjustedSize = nullptr);
+
+ QMetalTextureData *d;
+ 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, AddressMode w);
+ ~QMetalSampler();
+ void destroy() override;
+ bool create() override;
+
+ QMetalSamplerData *d;
+ uint generation = 0;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiMetal;
+ friend struct QMetalShaderResourceBindings;
+};
+
+struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor
{
+ QMetalRenderPassDescriptor(QRhiImplementation *rhi);
+ ~QMetalRenderPassDescriptor();
+ void destroy() override;
+ bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
+ QVector<quint32> serializedFormat() const override;
+
+ void updateSerializedFormat();
+
+ // 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;
+ QVector<quint32> serializedFormatData;
+};
+
+struct QMetalRenderTargetData;
+
+struct QMetalSwapChainRenderTarget : public QRhiSwapChainRenderTarget
+{
+ QMetalSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain);
+ ~QMetalSwapChainRenderTarget();
+ void destroy() 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 destroy() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool create() override;
+
+ QMetalRenderTargetData *d;
+ friend class QRhiMetal;
};
-struct Q_GUI_EXPORT QRhiMetalNativeHandles : public QRhiNativeHandles
+struct QMetalShaderResourceBindings : public QRhiShaderResourceBindings
{
- MTLDevice *dev = nullptr;
- MTLCommandQueue *cmdQueue = nullptr;
+ QMetalShaderResourceBindings(QRhiImplementation *rhi);
+ ~QMetalShaderResourceBindings();
+ void destroy() override;
+ bool create() override;
+ void updateResources(UpdateFlags flags) override;
+
+ QVarLengthArray<QRhiShaderResourceBinding, 8> sortedBindings;
+ int maxBinding = -1;
+
+ struct BoundUniformBufferData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundSampledTextureData {
+ int count;
+ struct {
+ quint64 texId;
+ uint texGeneration;
+ quint64 samplerId;
+ uint samplerGeneration;
+ } d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE];
+ };
+ struct BoundStorageImageData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundStorageBufferData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundResourceData {
+ union {
+ BoundUniformBufferData ubuf;
+ BoundSampledTextureData stex;
+ BoundStorageImageData simage;
+ BoundStorageBufferData sbuf;
+ };
+ };
+ QVarLengthArray<BoundResourceData, 8> boundResourceData;
+
+ uint generation = 0;
+ friend class QRhiMetal;
};
-struct Q_GUI_EXPORT QRhiMetalCommandBufferNativeHandles : public QRhiNativeHandles
+struct QMetalGraphicsPipelineData;
+struct QMetalCommandBuffer;
+
+struct QMetalGraphicsPipeline : public QRhiGraphicsPipeline
{
- MTLCommandBuffer *commandBuffer = nullptr;
- MTLRenderCommandEncoder *encoder = nullptr;
+ QMetalGraphicsPipeline(QRhiImplementation *rhi);
+ ~QMetalGraphicsPipeline();
+ void destroy() override;
+ bool create() override;
+
+ void makeActiveForCurrentRenderPassEncoder(QMetalCommandBuffer *cbD);
+ void setupAttachmentsInMetalRenderPassDescriptor(void *metalRpDesc, QMetalRenderPassDescriptor *rpD);
+ void setupMetalDepthStencilDescriptor(void *metalDsDesc);
+ void mapStates();
+ bool createVertexFragmentPipeline();
+ bool createTessellationPipelines(const QShader &tessVert, const QShader &tesc, const QShader &tese, const QShader &tessFrag);
+
+ QMetalGraphicsPipelineData *d;
+ uint generation = 0;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiMetal;
+};
+
+struct QMetalComputePipelineData;
+
+struct QMetalComputePipeline : public QRhiComputePipeline
+{
+ QMetalComputePipeline(QRhiImplementation *rhi);
+ ~QMetalComputePipeline();
+ void destroy() override;
+ bool create() 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 destroy() override;
+
+ QMetalCommandBufferData *d = nullptr;
+ QRhiMetalCommandBufferNativeHandles nativeHandlesStruct;
+
+ enum PassType {
+ NoPass,
+ RenderPass,
+ ComputePass
+ };
+
+ // per-pass (render or compute command encoder) persistent state
+ PassType recordingPass;
+ QRhiRenderTarget *currentTarget;
+
+ // per-pass (render or compute command encoder) volatile (cached) state
+ QMetalGraphicsPipeline *currentGraphicsPipeline;
+ QMetalComputePipeline *currentComputePipeline;
+ uint currentPipelineGeneration;
+ QMetalShaderResourceBindings *currentGraphicsSrb;
+ QMetalShaderResourceBindings *currentComputeSrb;
+ uint currentSrbGeneration;
+ int currentResSlot;
+ QMetalBuffer *currentIndexBuffer;
+ quint32 currentIndexOffset;
+ QRhiCommandBuffer::IndexFormat currentIndexFormat;
+ int currentCullMode;
+ int currentTriangleFillMode;
+ int currentFrontFaceWinding;
+ QPair<float, float> currentDepthBiasValues;
+
+ const QRhiNativeHandles *nativeHandles();
+ void resetState(double lastGpuTime = 0);
+ void resetPerPassState();
+ void resetPerPassCachedState();
+};
+
+struct QMetalSwapChainData;
+
+struct QMetalSwapChain : public QRhiSwapChain
+{
+ QMetalSwapChain(QRhiImplementation *rhi);
+ ~QMetalSwapChain();
+ void destroy() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+ QSize surfacePixelSize() override;
+ bool isFormatSupported(Format f) override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+
+ bool createOrResize() override;
+
+ virtual QRhiSwapChainHdrInfo hdrInfo() override;
+
+ void chooseFormats();
+ void waitUntilCompleted(int slot);
+
+ QWindow *window = nullptr;
+ QSize pixelSize;
+ int currentFrameSlot = 0; // 0..QMTL_FRAMES_IN_FLIGHT-1
+ int frameCount = 0;
+ int samples = 1;
+ QMetalSwapChainRenderTarget rtWrapper;
+ QMetalCommandBuffer cbWrapper;
+ QMetalRenderBuffer *ds = nullptr;
+ QMetalSwapChainData *d = nullptr;
+};
+
+struct QRhiMetalData;
+
+class QRhiMetal : public QRhiImplementation
+{
+public:
+ QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *importDevice = nullptr);
+ ~QRhiMetal();
+
+ static bool probe(QRhiMetalInitParams *params);
+ static QRhiSwapChainProxyData updateSwapChainProxyData(QWindow *window);
+
+ 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,
+ quint32 size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags,
+ QRhiTexture::Format backingFormatHint) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int depth,
+ int arraySize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
+ QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u,
+ QRhiSampler::AddressMode v,
+ QRhiSampler::AddressMode w) 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, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) 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,
+ QRhiCommandBuffer::BeginPassFlags flags) 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,
+ QRhiCommandBuffer::BeginPassFlags flags) 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;
+ double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
+
+ QList<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;
+ QRhiDriverInfo driverInfo() const override;
+ QRhiStats statistics() override;
+ bool makeThreadLocalNativeContextCurrent() override;
+ void releaseCachedResources() override;
+ bool isDeviceLost() const override;
+
+ QByteArray pipelineCacheData() override;
+ void setPipelineCacheData(const QByteArray &data) 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 executeBufferHostWritesForSlot(QMetalBuffer *bufD, int slot);
+ void executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD);
+ static const int SUPPORTED_STAGES = 5;
+ void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD,
+ QMetalCommandBuffer *cbD,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
+ bool offsetOnlyChange,
+ const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES]);
+ struct TessDrawArgs {
+ QMetalCommandBuffer *cbD;
+ enum {
+ NonIndexed,
+ U16Indexed,
+ U32Indexed
+ } type;
+ struct NonIndexedArgs {
+ quint32 vertexCount;
+ quint32 instanceCount;
+ quint32 firstVertex;
+ quint32 firstInstance;
+ };
+ struct IndexedArgs {
+ quint32 indexCount;
+ quint32 instanceCount;
+ quint32 firstIndex;
+ qint32 vertexOffset;
+ quint32 firstInstance;
+ void *indexBuffer;
+ };
+ union {
+ NonIndexedArgs draw;
+ IndexedArgs drawIndexed;
+ };
+ };
+ void tessellatedDraw(const TessDrawArgs &args);
+ void adjustForMultiViewDraw(quint32 *instanceCount, QRhiCommandBuffer *cb);
+
+ QRhi::Flags rhiFlags;
+ bool importedDevice = false;
+ bool importedCmdQueue = false;
+ QMetalSwapChain *currentSwapChain = nullptr;
+ QSet<QMetalSwapChain *> swapchains;
+ QRhiMetalNativeHandles nativeHandlesStruct;
+ QRhiDriverInfo driverInfoStruct;
+ quint32 osMajor = 0;
+ quint32 osMinor = 0;
+
+ struct {
+ int maxTextureSize = 4096;
+ bool baseVertexAndInstance = true;
+ QVector<int> supportedSampleCounts;
+ bool isAppleGPU = false;
+ int maxThreadGroupSize = 512;
+ bool multiView = false;
+ } caps;
+
+ QRhiMetalData *d = nullptr;
};
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h
deleted file mode 100644
index ef9b9c93ca..0000000000
--- a/src/gui/rhi/qrhimetal_p_p.h
+++ /dev/null
@@ -1,464 +0,0 @@
-// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#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 destroy() override;
- bool create() override;
- QRhiBuffer::NativeBuffer nativeBuffer() override;
- char *beginFullDynamicBufferUpdateForCurrentFrame() override;
- void endFullDynamicBufferUpdateForCurrentFrame() 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,
- QRhiTexture::Format backingFormatHint);
- ~QMetalRenderBuffer();
- void destroy() override;
- bool create() 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 depth,
- int arraySize, int sampleCount, Flags flags);
- ~QMetalTexture();
- void destroy() override;
- bool create() override;
- bool createFrom(NativeTexture src) override;
- NativeTexture nativeTexture() override;
-
- bool prepareCreate(QSize *adjustedSize = nullptr);
-
- QMetalTextureData *d;
- 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, AddressMode w);
- ~QMetalSampler();
- void destroy() override;
- bool create() override;
-
- QMetalSamplerData *d;
- uint generation = 0;
- int lastActiveFrameSlot = -1;
- friend class QRhiMetal;
- friend struct QMetalShaderResourceBindings;
-};
-
-struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor
-{
- QMetalRenderPassDescriptor(QRhiImplementation *rhi);
- ~QMetalRenderPassDescriptor();
- void destroy() override;
- bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
- QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
- QVector<quint32> serializedFormat() const override;
-
- void updateSerializedFormat();
-
- // 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;
- QVector<quint32> serializedFormatData;
-};
-
-struct QMetalRenderTargetData;
-
-struct QMetalSwapChainRenderTarget : public QRhiSwapChainRenderTarget
-{
- QMetalSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain);
- ~QMetalSwapChainRenderTarget();
- void destroy() 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 destroy() override;
-
- QSize pixelSize() const override;
- float devicePixelRatio() const override;
- int sampleCount() const override;
-
- QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
- bool create() override;
-
- QMetalRenderTargetData *d;
- friend class QRhiMetal;
-};
-
-struct QMetalShaderResourceBindings : public QRhiShaderResourceBindings
-{
- QMetalShaderResourceBindings(QRhiImplementation *rhi);
- ~QMetalShaderResourceBindings();
- void destroy() override;
- bool create() override;
- void updateResources(UpdateFlags flags) override;
-
- QVarLengthArray<QRhiShaderResourceBinding, 8> sortedBindings;
- int maxBinding = -1;
-
- struct BoundUniformBufferData {
- quint64 id;
- uint generation;
- };
- struct BoundSampledTextureData {
- int count;
- struct {
- quint64 texId;
- uint texGeneration;
- quint64 samplerId;
- uint samplerGeneration;
- } d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE];
- };
- struct BoundStorageImageData {
- quint64 id;
- uint generation;
- };
- struct BoundStorageBufferData {
- quint64 id;
- uint generation;
- };
- struct BoundResourceData {
- union {
- BoundUniformBufferData ubuf;
- BoundSampledTextureData stex;
- BoundStorageImageData simage;
- BoundStorageBufferData sbuf;
- };
- };
- QVarLengthArray<BoundResourceData, 8> boundResourceData;
-
- uint generation = 0;
- friend class QRhiMetal;
-};
-
-struct QMetalGraphicsPipelineData;
-
-struct QMetalGraphicsPipeline : public QRhiGraphicsPipeline
-{
- QMetalGraphicsPipeline(QRhiImplementation *rhi);
- ~QMetalGraphicsPipeline();
- void destroy() override;
- bool create() override;
-
- QMetalGraphicsPipelineData *d;
- uint generation = 0;
- int lastActiveFrameSlot = -1;
- friend class QRhiMetal;
-};
-
-struct QMetalComputePipelineData;
-
-struct QMetalComputePipeline : public QRhiComputePipeline
-{
- QMetalComputePipeline(QRhiImplementation *rhi);
- ~QMetalComputePipeline();
- void destroy() override;
- bool create() 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 destroy() override;
-
- QMetalCommandBufferData *d = nullptr;
- QRhiMetalCommandBufferNativeHandles nativeHandlesStruct;
-
- enum PassType {
- NoPass,
- RenderPass,
- ComputePass
- };
-
- // per-pass (render or compute command encoder) persistent state
- PassType recordingPass;
- QRhiRenderTarget *currentTarget;
-
- // per-pass (render or compute command encoder) volatile (cached) state
- QRhiGraphicsPipeline *currentGraphicsPipeline;
- QRhiComputePipeline *currentComputePipeline;
- uint currentPipelineGeneration;
- QRhiShaderResourceBindings *currentGraphicsSrb;
- QRhiShaderResourceBindings *currentComputeSrb;
- uint currentSrbGeneration;
- int currentResSlot;
- QRhiBuffer *currentIndexBuffer;
- quint32 currentIndexOffset;
- QRhiCommandBuffer::IndexFormat currentIndexFormat;
- int currentCullMode;
- int currentTriangleFillMode;
- int currentFrontFaceWinding;
- QPair<float, float> currentDepthBiasValues;
-
- const QRhiNativeHandles *nativeHandles();
- void resetState();
- void resetPerPassState();
- void resetPerPassCachedState();
-};
-
-struct QMetalSwapChainData;
-
-struct QMetalSwapChain : public QRhiSwapChain
-{
- QMetalSwapChain(QRhiImplementation *rhi);
- ~QMetalSwapChain();
- void destroy() override;
-
- QRhiCommandBuffer *currentFrameCommandBuffer() override;
- QRhiRenderTarget *currentFrameRenderTarget() override;
- QSize surfacePixelSize() override;
- bool isFormatSupported(Format f) override;
-
- QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
-
- bool createOrResize() override;
-
- virtual QRhiSwapChainHdrInfo hdrInfo() override;
-
- void chooseFormats();
-
- QWindow *window = nullptr;
- QSize pixelSize;
- int currentFrameSlot = 0; // 0..QMTL_FRAMES_IN_FLIGHT-1
- int frameCount = 0;
- int samples = 1;
- QMetalSwapChainRenderTarget rtWrapper;
- QMetalCommandBuffer cbWrapper;
- QMetalRenderBuffer *ds = nullptr;
- QMetalSwapChainData *d = nullptr;
-};
-
-struct QRhiMetalData;
-
-class QRhiMetal : public QRhiImplementation
-{
-public:
- QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *importDevice = nullptr);
- ~QRhiMetal();
-
- static bool probe(QRhiMetalInitParams *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,
- QRhiTexture::Format backingFormatHint) override;
- QRhiTexture *createTexture(QRhiTexture::Format format,
- const QSize &pixelSize,
- int depth,
- int arraySize,
- int sampleCount,
- QRhiTexture::Flags flags) override;
- QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
- QRhiSampler::Filter minFilter,
- QRhiSampler::Filter mipmapMode,
- QRhiSampler:: AddressMode u,
- QRhiSampler::AddressMode v,
- QRhiSampler::AddressMode w) 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, QRhi::BeginFrameFlags flags) override;
- QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) 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,
- QRhiCommandBuffer::BeginPassFlags flags) 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,
- QRhiCommandBuffer::BeginPassFlags flags) 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;
-
- QList<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;
- QRhiDriverInfo driverInfo() const override;
- QRhiMemAllocStats graphicsMemoryAllocationStatistics() override;
- bool makeThreadLocalNativeContextCurrent() override;
- void releaseCachedResources() override;
- bool isDeviceLost() const override;
-
- QByteArray pipelineCacheData() override;
- void setPipelineCacheData(const QByteArray &data) 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 executeBufferHostWritesForSlot(QMetalBuffer *bufD, int slot);
- void executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD);
- static const int SUPPORTED_STAGES = 3;
- void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD,
- QMetalCommandBuffer *cbD,
- int dynamicOffsetCount,
- const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
- bool offsetOnlyChange,
- const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES]);
- int effectiveSampleCount(int sampleCount) const;
-
- bool importedDevice = false;
- bool importedCmdQueue = false;
- QMetalSwapChain *currentSwapChain = nullptr;
- QSet<QMetalSwapChain *> swapchains;
- QRhiMetalNativeHandles nativeHandlesStruct;
- QRhiDriverInfo driverInfoStruct;
-
- struct {
- int maxTextureSize = 4096;
- bool baseVertexAndInstance = true;
- QVector<int> supportedSampleCounts;
- } caps;
-
- QRhiMetalData *d = nullptr;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp
index 2a3412a69c..566b922c1b 100644
--- a/src/gui/rhi/qrhinull.cpp
+++ b/src/gui/rhi/qrhinull.cpp
@@ -1,7 +1,7 @@
-// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qrhinull_p_p.h"
+#include "qrhinull_p.h"
#include <qmath.h>
#include <QPainter>
@@ -9,10 +9,13 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiNullInitParams
- \internal
\inmodule QtGui
+ \since 6.6
\brief Null backend specific initialization parameters.
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
A Null QRhi needs no special parameters for initialization.
\badcode
@@ -28,9 +31,12 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiNullNativeHandles
- \internal
\inmodule QtGui
+ \since 6.6
\brief Empty.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
QRhiNull::QRhiNull(QRhiNullInitParams *params)
@@ -59,7 +65,7 @@ QRhiSwapChain *QRhiNull::createSwapChain()
return new QNullSwapChain(this);
}
-QRhiBuffer *QRhiNull::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
+QRhiBuffer *QRhiNull::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
{
return new QNullBuffer(this, type, usage, size);
}
@@ -135,8 +141,7 @@ int QRhiNull::resourceLimit(QRhi::ResourceLimit limit) const
return 32;
}
- Q_UNREACHABLE();
- return 0;
+ Q_UNREACHABLE_RETURN(0);
}
const QRhiNativeHandles *QRhiNull::nativeHandles()
@@ -151,7 +156,7 @@ QRhiDriverInfo QRhiNull::driverInfo() const
return info;
}
-QRhiMemAllocStats QRhiNull::graphicsMemoryAllocationStatistics()
+QRhiStats QRhiNull::statistics()
{
return {};
}
@@ -345,6 +350,12 @@ void QRhiNull::endExternal(QRhiCommandBuffer *cb)
Q_UNUSED(cb);
}
+double QRhiNull::lastCompletedGpuTime(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+ return 0;
+}
+
QRhi::FrameOpResult QRhiNull::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
{
Q_UNUSED(flags);
@@ -382,9 +393,9 @@ QRhi::FrameOpResult QRhiNull::finish()
void QRhiNull::simulateTextureUpload(const QRhiResourceUpdateBatchPrivate::TextureOp &u)
{
QNullTexture *texD = QRHI_RES(QNullTexture, u.dst);
- for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
+ for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
- for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level])) {
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level])) {
if (!subresDesc.image().isNull()) {
const QImage src = subresDesc.image();
QPainter painter(&texD->image[layer][level]);
@@ -457,7 +468,7 @@ void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *re
QNullBuffer *bufD = QRHI_RES(QNullBuffer, u.buf);
memcpy(bufD->data + u.offset, u.data.constData(), size_t(u.data.size()));
} else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) {
- QRhiBufferReadbackResult *result = u.result;
+ QRhiReadbackResult *result = u.result;
result->data.resize(u.readSize);
QNullBuffer *bufD = QRHI_RES(QNullBuffer, u.buf);
memcpy(result->data.data(), bufD->data + u.offset, size_t(u.readSize));
@@ -550,7 +561,7 @@ void QRhiNull::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *re
resourceUpdate(cb, resourceUpdates);
}
-QNullBuffer::QNullBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
+QNullBuffer::QNullBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
: QRhiBuffer(rhi, type, usage, size)
{
}
@@ -662,11 +673,14 @@ bool QNullTexture::create()
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
- QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
- m_depth = qMax(1, m_depth);
+ const bool is1D = m_flags.testFlags(OneDimensional);
+ QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
+ : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
const int mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
- m_arraySize = qMax(0, m_arraySize);
- const int layerCount = is3D ? m_depth : (isCube ? 6 : (isArray ? m_arraySize : 1));
+ const int layerCount = is3D ? qMax(1, m_depth)
+ : (isCube ? 6
+ : (isArray ? qMax(0, m_arraySize)
+ : 1));
if (m_format == RGBA8) {
image.resize(layerCount);
@@ -715,10 +729,15 @@ QNullSampler::~QNullSampler()
void QNullSampler::destroy()
{
+ QRHI_RES_RHI(QRhiNull);
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
bool QNullSampler::create()
{
+ QRHI_RES_RHI(QRhiNull);
+ rhiD->registerResource(this);
return true;
}
@@ -734,6 +753,9 @@ QNullRenderPassDescriptor::~QNullRenderPassDescriptor()
void QNullRenderPassDescriptor::destroy()
{
+ QRHI_RES_RHI(QRhiNull);
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
bool QNullRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
@@ -744,7 +766,10 @@ bool QNullRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *oth
QRhiRenderPassDescriptor *QNullRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
{
- return new QNullRenderPassDescriptor(m_rhi);
+ QNullRenderPassDescriptor *rpD = new QNullRenderPassDescriptor(m_rhi);
+ QRHI_RES_RHI(QRhiNull);
+ rhiD->registerResource(rpD, false);
+ return rpD;
}
QVector<quint32> QNullRenderPassDescriptor::serializedFormat() const
@@ -797,11 +822,17 @@ QNullTextureRenderTarget::~QNullTextureRenderTarget()
void QNullTextureRenderTarget::destroy()
{
+ QRHI_RES_RHI(QRhiNull);
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
QRhiRenderPassDescriptor *QNullTextureRenderTarget::newCompatibleRenderPassDescriptor()
{
- return new QNullRenderPassDescriptor(m_rhi);
+ QNullRenderPassDescriptor *rpD = new QNullRenderPassDescriptor(m_rhi);
+ QRHI_RES_RHI(QRhiNull);
+ rhiD->registerResource(rpD, false);
+ return rpD;
}
bool QNullTextureRenderTarget::create()
@@ -819,6 +850,7 @@ bool QNullTextureRenderTarget::create()
d.pixelSize = m_desc.depthTexture()->pixelSize();
}
QRhiRenderTargetAttachmentTracker::updateResIdList<QNullTexture, QNullRenderBuffer>(m_desc, &d.currentResIdList);
+ rhiD->registerResource(this);
return true;
}
@@ -852,6 +884,9 @@ QNullShaderResourceBindings::~QNullShaderResourceBindings()
void QNullShaderResourceBindings::destroy()
{
+ QRHI_RES_RHI(QRhiNull);
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
bool QNullShaderResourceBindings::create()
@@ -862,6 +897,7 @@ bool QNullShaderResourceBindings::create()
rhiD->updateLayoutDesc(this);
+ rhiD->registerResource(this, false);
return true;
}
@@ -882,6 +918,9 @@ QNullGraphicsPipeline::~QNullGraphicsPipeline()
void QNullGraphicsPipeline::destroy()
{
+ QRHI_RES_RHI(QRhiNull);
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
bool QNullGraphicsPipeline::create()
@@ -890,6 +929,7 @@ bool QNullGraphicsPipeline::create()
if (!rhiD->sanityCheckGraphicsPipeline(this))
return false;
+ rhiD->registerResource(this);
return true;
}
@@ -905,10 +945,15 @@ QNullComputePipeline::~QNullComputePipeline()
void QNullComputePipeline::destroy()
{
+ QRHI_RES_RHI(QRhiNull);
+ if (rhiD)
+ rhiD->unregisterResource(this);
}
bool QNullComputePipeline::create()
{
+ QRHI_RES_RHI(QRhiNull);
+ rhiD->registerResource(this);
return true;
}
@@ -968,7 +1013,10 @@ bool QNullSwapChain::isFormatSupported(Format f)
QRhiRenderPassDescriptor *QNullSwapChain::newCompatibleRenderPassDescriptor()
{
- return new QNullRenderPassDescriptor(m_rhi);
+ QNullRenderPassDescriptor *rpD = new QNullRenderPassDescriptor(m_rhi);
+ QRHI_RES_RHI(QRhiNull);
+ rhiD->registerResource(rpD, false);
+ return rpD;
}
bool QNullSwapChain::createOrResize()
diff --git a/src/gui/rhi/qrhinull_p.h b/src/gui/rhi/qrhinull_p.h
index 061b6eba4a..fc266b4f38 100644
--- a/src/gui/rhi/qrhinull_p.h
+++ b/src/gui/rhi/qrhinull_p.h
@@ -1,8 +1,8 @@
-// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QRHINULL_H
-#define QRHINULL_H
+#ifndef QRHINULL_P_H
+#define QRHINULL_P_H
//
// W A R N I N G
@@ -15,16 +15,279 @@
// We mean it.
//
-#include <private/qrhi_p.h>
+#include "qrhi_p.h"
QT_BEGIN_NAMESPACE
-struct Q_GUI_EXPORT QRhiNullInitParams : public QRhiInitParams
+struct QNullBuffer : public QRhiBuffer
{
+ QNullBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size);
+ ~QNullBuffer();
+ void destroy() override;
+ bool create() override;
+ char *beginFullDynamicBufferUpdateForCurrentFrame() override;
+
+ char *data = nullptr;
+};
+
+struct QNullRenderBuffer : public QRhiRenderBuffer
+{
+ QNullRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags,
+ QRhiTexture::Format backingFormatHint);
+ ~QNullRenderBuffer();
+ void destroy() override;
+ bool create() override;
+ QRhiTexture::Format backingFormat() const override;
+
+ bool valid = false;
+ uint generation = 0;
+};
+
+struct QNullTexture : public QRhiTexture
+{
+ QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
+ int arraySize, int sampleCount, Flags flags);
+ ~QNullTexture();
+ void destroy() override;
+ bool create() override;
+ bool createFrom(NativeTexture src) override;
+
+ bool valid = false;
+ QVarLengthArray<std::array<QImage, QRhi::MAX_MIP_LEVELS>, 6> image;
+ uint generation = 0;
+};
+
+struct QNullSampler : public QRhiSampler
+{
+ QNullSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v, AddressMode w);
+ ~QNullSampler();
+ void destroy() override;
+ bool create() override;
+};
+
+struct QNullRenderPassDescriptor : public QRhiRenderPassDescriptor
+{
+ QNullRenderPassDescriptor(QRhiImplementation *rhi);
+ ~QNullRenderPassDescriptor();
+ void destroy() override;
+ bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
+ QVector<quint32> serializedFormat() const override;
+};
+
+struct QNullRenderTargetData
+{
+ QNullRenderTargetData(QRhiImplementation *) { }
+
+ QNullRenderPassDescriptor *rp = nullptr;
+ QSize pixelSize;
+ float dpr = 1;
+ QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList;
+};
+
+struct QNullSwapChainRenderTarget : public QRhiSwapChainRenderTarget
+{
+ QNullSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain);
+ ~QNullSwapChainRenderTarget();
+ void destroy() 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 destroy() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool create() override;
+
+ QNullRenderTargetData d;
+};
+
+struct QNullShaderResourceBindings : public QRhiShaderResourceBindings
+{
+ QNullShaderResourceBindings(QRhiImplementation *rhi);
+ ~QNullShaderResourceBindings();
+ void destroy() override;
+ bool create() override;
+ void updateResources(UpdateFlags flags) override;
+};
+
+struct QNullGraphicsPipeline : public QRhiGraphicsPipeline
+{
+ QNullGraphicsPipeline(QRhiImplementation *rhi);
+ ~QNullGraphicsPipeline();
+ void destroy() override;
+ bool create() override;
+};
+
+struct QNullComputePipeline : public QRhiComputePipeline
+{
+ QNullComputePipeline(QRhiImplementation *rhi);
+ ~QNullComputePipeline();
+ void destroy() override;
+ bool create() override;
};
-struct Q_GUI_EXPORT QRhiNullNativeHandles : public QRhiNativeHandles
+struct QNullCommandBuffer : public QRhiCommandBuffer
{
+ QNullCommandBuffer(QRhiImplementation *rhi);
+ ~QNullCommandBuffer();
+ void destroy() override;
+};
+
+struct QNullSwapChain : public QRhiSwapChain
+{
+ QNullSwapChain(QRhiImplementation *rhi);
+ ~QNullSwapChain();
+ void destroy() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+
+ QSize surfacePixelSize() override;
+ bool isFormatSupported(Format f) override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool createOrResize() override;
+
+ QWindow *window = nullptr;
+ QNullSwapChainRenderTarget 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,
+ quint32 size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags,
+ QRhiTexture::Format backingFormatHint) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int depth,
+ int arraySize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
+ QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u,
+ QRhiSampler::AddressMode v,
+ QRhiSampler::AddressMode w) 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, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) 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,
+ QRhiCommandBuffer::BeginPassFlags flags) 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,
+ QRhiCommandBuffer::BeginPassFlags flags) 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;
+ double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
+
+ QList<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;
+ QRhiDriverInfo driverInfo() const override;
+ QRhiStats statistics() override;
+ bool makeThreadLocalNativeContextCurrent() override;
+ void releaseCachedResources() override;
+ bool isDeviceLost() const override;
+
+ QByteArray pipelineCacheData() override;
+ void setPipelineCacheData(const QByteArray &data) override;
+
+ void simulateTextureUpload(const QRhiResourceUpdateBatchPrivate::TextureOp &u);
+ void simulateTextureCopy(const QRhiResourceUpdateBatchPrivate::TextureOp &u);
+ void simulateTextureGenMips(const QRhiResourceUpdateBatchPrivate::TextureOp &u);
+
+ QRhiNullNativeHandles nativeHandlesStruct;
+ QRhiSwapChain *currentSwapChain = nullptr;
+ QNullCommandBuffer offscreenCommandBuffer;
};
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h
deleted file mode 100644
index 7c2703cada..0000000000
--- a/src/gui/rhi/qrhinull_p_p.h
+++ /dev/null
@@ -1,295 +0,0 @@
-// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#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 destroy() override;
- bool create() override;
- char *beginFullDynamicBufferUpdateForCurrentFrame() override;
-
- char *data = nullptr;
-};
-
-struct QNullRenderBuffer : public QRhiRenderBuffer
-{
- QNullRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
- int sampleCount, QRhiRenderBuffer::Flags flags,
- QRhiTexture::Format backingFormatHint);
- ~QNullRenderBuffer();
- void destroy() override;
- bool create() override;
- QRhiTexture::Format backingFormat() const override;
-
- bool valid = false;
- uint generation = 0;
-};
-
-struct QNullTexture : public QRhiTexture
-{
- QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
- int arraySize, int sampleCount, Flags flags);
- ~QNullTexture();
- void destroy() override;
- bool create() override;
- bool createFrom(NativeTexture src) override;
-
- bool valid = false;
- QVarLengthArray<std::array<QImage, QRhi::MAX_MIP_LEVELS>, 6> image;
- uint generation = 0;
-};
-
-struct QNullSampler : public QRhiSampler
-{
- QNullSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
- AddressMode u, AddressMode v, AddressMode w);
- ~QNullSampler();
- void destroy() override;
- bool create() override;
-};
-
-struct QNullRenderPassDescriptor : public QRhiRenderPassDescriptor
-{
- QNullRenderPassDescriptor(QRhiImplementation *rhi);
- ~QNullRenderPassDescriptor();
- void destroy() override;
- bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
- QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
- QVector<quint32> serializedFormat() const override;
-};
-
-struct QNullRenderTargetData
-{
- QNullRenderTargetData(QRhiImplementation *) { }
-
- QNullRenderPassDescriptor *rp = nullptr;
- QSize pixelSize;
- float dpr = 1;
- QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList;
-};
-
-struct QNullSwapChainRenderTarget : public QRhiSwapChainRenderTarget
-{
- QNullSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain);
- ~QNullSwapChainRenderTarget();
- void destroy() 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 destroy() override;
-
- QSize pixelSize() const override;
- float devicePixelRatio() const override;
- int sampleCount() const override;
-
- QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
- bool create() override;
-
- QNullRenderTargetData d;
-};
-
-struct QNullShaderResourceBindings : public QRhiShaderResourceBindings
-{
- QNullShaderResourceBindings(QRhiImplementation *rhi);
- ~QNullShaderResourceBindings();
- void destroy() override;
- bool create() override;
- void updateResources(UpdateFlags flags) override;
-};
-
-struct QNullGraphicsPipeline : public QRhiGraphicsPipeline
-{
- QNullGraphicsPipeline(QRhiImplementation *rhi);
- ~QNullGraphicsPipeline();
- void destroy() override;
- bool create() override;
-};
-
-struct QNullComputePipeline : public QRhiComputePipeline
-{
- QNullComputePipeline(QRhiImplementation *rhi);
- ~QNullComputePipeline();
- void destroy() override;
- bool create() override;
-};
-
-struct QNullCommandBuffer : public QRhiCommandBuffer
-{
- QNullCommandBuffer(QRhiImplementation *rhi);
- ~QNullCommandBuffer();
- void destroy() override;
-};
-
-struct QNullSwapChain : public QRhiSwapChain
-{
- QNullSwapChain(QRhiImplementation *rhi);
- ~QNullSwapChain();
- void destroy() override;
-
- QRhiCommandBuffer *currentFrameCommandBuffer() override;
- QRhiRenderTarget *currentFrameRenderTarget() override;
-
- QSize surfacePixelSize() override;
- bool isFormatSupported(Format f) override;
-
- QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
- bool createOrResize() override;
-
- QWindow *window = nullptr;
- QNullSwapChainRenderTarget 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,
- QRhiTexture::Format backingFormatHint) override;
- QRhiTexture *createTexture(QRhiTexture::Format format,
- const QSize &pixelSize,
- int depth,
- int arraySize,
- int sampleCount,
- QRhiTexture::Flags flags) override;
- QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
- QRhiSampler::Filter minFilter,
- QRhiSampler::Filter mipmapMode,
- QRhiSampler:: AddressMode u,
- QRhiSampler::AddressMode v,
- QRhiSampler::AddressMode w) 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, QRhi::BeginFrameFlags flags) override;
- QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) 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,
- QRhiCommandBuffer::BeginPassFlags flags) 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,
- QRhiCommandBuffer::BeginPassFlags flags) 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;
-
- QList<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;
- QRhiDriverInfo driverInfo() const override;
- QRhiMemAllocStats graphicsMemoryAllocationStatistics() override;
- bool makeThreadLocalNativeContextCurrent() override;
- void releaseCachedResources() override;
- bool isDeviceLost() const override;
-
- QByteArray pipelineCacheData() override;
- void setPipelineCacheData(const QByteArray &data) override;
-
- void simulateTextureUpload(const QRhiResourceUpdateBatchPrivate::TextureOp &u);
- void simulateTextureCopy(const QRhiResourceUpdateBatchPrivate::TextureOp &u);
- void simulateTextureGenMips(const QRhiResourceUpdateBatchPrivate::TextureOp &u);
-
- QRhiNullNativeHandles nativeHandlesStruct;
- QRhiSwapChain *currentSwapChain = nullptr;
- QNullCommandBuffer offscreenCommandBuffer;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index 26fc3e05ed..f0b51146cc 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -1,10 +1,11 @@
-// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qrhivulkan_p_p.h"
-#include "qrhivulkanext_p.h"
+#include "qrhivulkan_p.h"
+#include <qpa/qplatformvulkaninstance.h>
#define VMA_IMPLEMENTATION
+#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_RECORDING_ENABLED 0
#define VMA_DEDICATED_ALLOCATION 0
@@ -22,6 +23,7 @@ QT_WARNING_POP
#include <qmath.h>
#include <QVulkanFunctions>
#include <QtGui/qwindow.h>
+#include <optional>
QT_BEGIN_NAMESPACE
@@ -57,10 +59,13 @@ QT_BEGIN_NAMESPACE
/*!
\class QRhiVulkanInitParams
- \internal
\inmodule QtGui
+ \since 6.6
\brief Vulkan specific initialization parameters.
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+
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:
@@ -163,34 +168,121 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \variable QRhiVulkanInitParams::inst
+
+ The QVulkanInstance that has already been successfully
+ \l{QVulkanInstance::create()}{created}, required.
+*/
+
+/*!
+ \variable QRhiVulkanInitParams::window
+
+ Optional, but recommended when targeting a QWindow.
+*/
+
+/*!
+ \variable QRhiVulkanInitParams::deviceExtensions
+
+ Optional, empty by default. The list of Vulkan device extensions to enable.
+ Unsupported extensions are ignored gracefully.
+*/
+
+/*!
\class QRhiVulkanNativeHandles
- \internal
\inmodule QtGui
+ \since 6.6
\brief Collects device, queue, and other Vulkan objects that are used by the QRhi.
\note Ownership of the Vulkan objects is never transferred.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
+ \variable QRhiVulkanNativeHandles::physDev
+
+ When different from \nullptr, specifies the Vulkan physical device to use.
+*/
+
+/*!
+ \variable QRhiVulkanNativeHandles::dev
+
+ When wanting to import not just a physical device, but also use an already
+ existing VkDevice, set this and the graphics queue index and family index.
+*/
+
+/*!
+ \variable QRhiVulkanNativeHandles::gfxQueueFamilyIdx
+
+ Graphics queue family index.
+*/
+
+/*!
+ \variable QRhiVulkanNativeHandles::gfxQueueIdx
+
+ Graphics queue index.
+*/
+
+/*!
+ \variable QRhiVulkanNativeHandles::vmemAllocator
+
+ Relevant only when importing an existing memory allocator object,
+ leave it set to \nullptr otherwise.
+*/
+
+/*!
+ \variable QRhiVulkanNativeHandles::gfxQueue
+
+ Output only, not used by QRhi::create(), only set by the
+ QRhi::nativeHandles() accessor. The graphics VkQueue used by the QRhi.
+*/
+
+/*!
+ \variable QRhiVulkanNativeHandles::inst
+
+ Output only, not used by QRhi::create(), only set by the
+ QRhi::nativeHandles() accessor. The QVulkanInstance used by the QRhi.
+*/
+
+/*!
\class QRhiVulkanCommandBufferNativeHandles
- \internal
\inmodule QtGui
+ \since 6.6
\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.
+ \l{QRhi::endOffscreenFrame()}{endOffscreenFrame()} pair.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
/*!
+ \variable QRhiVulkanCommandBufferNativeHandles::commandBuffer
+
+ The VkCommandBuffer object.
+*/
+
+/*!
\class QRhiVulkanRenderPassNativeHandles
- \internal
\inmodule QtGui
+ \since 6.6
\brief Holds the Vulkan render pass object backing a QRhiRenderPassDescriptor.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
*/
+/*!
+ \variable QRhiVulkanRenderPassNativeHandles::renderPass
+
+ The VkRenderPass object.
+*/
+
template <class Int>
inline Int aligned(Int v, Int byteAlign)
{
@@ -199,84 +291,14 @@ inline Int aligned(Int v, Int byteAlign)
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)
+static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetInstanceProcAddr(VkInstance, const char *pName)
{
- globalVulkanInstance->deviceFunctions(device)->vkFreeMemory(device, memory, pAllocator);
+ return globalVulkanInstance->getInstanceProcAddr(pName);
}
-VkResult VKAPI_PTR wrap_vkMapMemory(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData)
+static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetDeviceProcAddr(VkDevice device, const char *pName)
{
- 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);
+ return globalVulkanInstance->functions()->vkGetDeviceProcAddr(device, pName);
}
static inline VmaAllocation toVmaAllocation(QVkAlloc a)
@@ -289,6 +311,13 @@ static inline VmaAllocator toVmaAllocator(QVkAllocator a)
return reinterpret_cast<VmaAllocator>(a);
}
+/*!
+ \return the list of instance extensions that are expected to be enabled on
+ the QVulkanInstance that is used for the Vulkan-based QRhi.
+
+ The returned list can be safely passed to QVulkanInstance::setExtensions()
+ as-is, because unsupported extensions are filtered out automatically.
+ */
QByteArrayList QRhiVulkanInitParams::preferredInstanceExtensions()
{
return {
@@ -296,12 +325,18 @@ QByteArrayList QRhiVulkanInitParams::preferredInstanceExtensions()
};
}
+/*!
+ \return the list of device extensions that are expected to be enabled on the
+ \c VkDevice when creating a Vulkan-based QRhi with an externally created
+ \c VkDevice object.
+ */
QByteArrayList QRhiVulkanInitParams::preferredExtensionsForImportedDevice()
{
return {
QByteArrayLiteral("VK_KHR_swapchain"),
- QByteArrayLiteral("VK_EXT_debug_marker"),
- QByteArrayLiteral("VK_EXT_vertex_attribute_divisor")
+ QByteArrayLiteral("VK_EXT_vertex_attribute_divisor"),
+ QByteArrayLiteral("VK_KHR_create_renderpass2"),
+ QByteArrayLiteral("VK_KHR_depth_stencil_resolve")
};
}
@@ -328,20 +363,19 @@ QRhiVulkan::QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *im
}
}
-static bool qvk_debug_filter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object,
- size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage)
+static bool qvk_debug_filter(QVulkanInstance::DebugMessageSeverityFlags severity,
+ QVulkanInstance::DebugMessageTypeFlags type,
+ const void *callbackData)
{
- Q_UNUSED(flags);
- Q_UNUSED(objectType);
- Q_UNUSED(object);
- Q_UNUSED(location);
- Q_UNUSED(messageCode);
- Q_UNUSED(pLayerPrefix);
+ Q_UNUSED(severity);
+ Q_UNUSED(type);
+#ifdef VK_EXT_debug_utils
+ const VkDebugUtilsMessengerCallbackDataEXT *d = static_cast<const VkDebugUtilsMessengerCallbackDataEXT *>(callbackData);
// 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"))
+ if (strstr(d->pMessage, "Mapping an image with layout")
+ && strstr(d->pMessage, "can result in undefined behavior if this memory is used by the device"))
{
return true;
}
@@ -352,9 +386,11 @@ static bool qvk_debug_filter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTyp
// then move on to another pool. If there is a real error, a qWarning
// message is shown by allocateDescriptorSet(), so the validation warning
// does not have any value and is just noise.
- if (strstr(pMessage, "VUID-VkDescriptorSetAllocateInfo-descriptorPool-00307"))
+ if (strstr(d->pMessage, "VUID-VkDescriptorSetAllocateInfo-descriptorPool-00307"))
return true;
-
+#else
+ Q_UNUSED(callbackData);
+#endif
return false;
}
@@ -384,13 +420,19 @@ bool QRhiVulkan::create(QRhi::Flags flags)
return false;
}
- globalVulkanInstance = inst; // assume this will not change during the lifetime of the entire application
+ rhiFlags = flags;
+ qCDebug(QRHI_LOG_INFO, "Initializing QRhi Vulkan backend %p with flags %d", this, int(rhiFlags));
+ globalVulkanInstance = inst; // used for function resolving in vkmemalloc callbacks
f = inst->functions();
+ if (QRHI_LOG_INFO().isEnabled(QtDebugMsg)) {
+ qCDebug(QRHI_LOG_INFO, "Enabled instance extensions:");
+ for (const char *ext : inst->extensions())
+ qCDebug(QRHI_LOG_INFO, " %s", ext);
+ }
- caps.vulkan11OrHigher = inst->apiVersion() >= QVersionNumber(1, 1);
-
- rhiFlags = flags;
+ caps = {};
+ caps.debugUtils = inst->extensions().contains(QByteArrayLiteral("VK_EXT_debug_utils"));
QList<VkQueueFamilyProperties> queueFamilyProps;
auto queryQueueFamilyProps = [this, &queueFamilyProps] {
@@ -471,50 +513,125 @@ bool QRhiVulkan::create(QRhi::Flags flags)
physDevProperties.deviceType);
}
+ caps.apiVersion = inst->apiVersion();
+
+ // Check the physical device API version against the instance API version,
+ // they do not have to match, which means whatever version was set in the
+ // QVulkanInstance may not be legally used with a given device if the
+ // physical device has a lower version.
+ const QVersionNumber physDevApiVersion(VK_VERSION_MAJOR(physDevProperties.apiVersion),
+ VK_VERSION_MINOR(physDevProperties.apiVersion)); // patch version left out intentionally
+ if (physDevApiVersion < caps.apiVersion) {
+ qCDebug(QRHI_LOG_INFO) << "Instance has api version" << caps.apiVersion
+ << "whereas the chosen physical device has" << physDevApiVersion
+ << "- restricting to the latter";
+ caps.apiVersion = physDevApiVersion;
+ }
+
driverInfoStruct.deviceName = QByteArray(physDevProperties.deviceName);
driverInfoStruct.deviceId = physDevProperties.deviceID;
driverInfoStruct.vendorId = physDevProperties.vendorID;
driverInfoStruct.deviceType = toRhiDeviceType(physDevProperties.deviceType);
- f->vkGetPhysicalDeviceFeatures(physDev, &physDevFeatures);
+ bool featuresQueried = false;
+#ifdef VK_VERSION_1_1
+ VkPhysicalDeviceFeatures2 physDevFeaturesChainable = {};
+ physDevFeaturesChainable.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+#endif
+
+ // Vulkan >=1.2 headers at build time, >=1.2 implementation at run time
+#ifdef VK_VERSION_1_2
+ if (!featuresQueried) {
+ // Vulkan11Features, Vulkan12Features, etc. are only in Vulkan 1.2 and newer.
+ if (caps.apiVersion >= QVersionNumber(1, 2)) {
+ physDevFeatures11IfApi12OrNewer = {};
+ physDevFeatures11IfApi12OrNewer.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
+ physDevFeatures12 = {};
+ physDevFeatures12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
+#ifdef VK_VERSION_1_3
+ physDevFeatures13 = {};
+ physDevFeatures13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
+#endif
+ physDevFeaturesChainable.pNext = &physDevFeatures11IfApi12OrNewer;
+ physDevFeatures11IfApi12OrNewer.pNext = &physDevFeatures12;
+#ifdef VK_VERSION_1_3
+ if (caps.apiVersion >= QVersionNumber(1, 3))
+ physDevFeatures12.pNext = &physDevFeatures13;
+#endif
+ f->vkGetPhysicalDeviceFeatures2(physDev, &physDevFeaturesChainable);
+ memcpy(&physDevFeatures, &physDevFeaturesChainable.features, sizeof(VkPhysicalDeviceFeatures));
+ featuresQueried = true;
+ }
+ }
+#endif // VK_VERSION_1_2
+
+ // Vulkan >=1.1 headers at build time, 1.1 implementation at run time
+#ifdef VK_VERSION_1_1
+ if (!featuresQueried) {
+ // Vulkan versioning nightmares: if the runtime API version is 1.1,
+ // there is no Vulkan11Features (introduced in 1.2+, the headers might
+ // have the types and structs, but the Vulkan implementation version at
+ // run time is what matters). But there are individual feature structs.
+ // For multiview, it is important to get this right since at the time of
+ // writing Quest 3 Android is a Vulkan 1.1 implementation at run time on
+ // the headset.
+ if (caps.apiVersion == QVersionNumber(1, 1)) {
+ multiviewFeaturesIfApi11 = {};
+ multiviewFeaturesIfApi11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
+ physDevFeaturesChainable.pNext = &multiviewFeaturesIfApi11;
+ f->vkGetPhysicalDeviceFeatures2(physDev, &physDevFeaturesChainable);
+ memcpy(&physDevFeatures, &physDevFeaturesChainable.features, sizeof(VkPhysicalDeviceFeatures));
+ featuresQueried = true;
+ }
+ }
+#endif
+
+ if (!featuresQueried) {
+ // If the API version at run time is 1.0 (or we are building with
+ // ancient 1.0 headers), then do the Vulkan 1.0 query.
+ f->vkGetPhysicalDeviceFeatures(physDev, &physDevFeatures);
+ featuresQueried = true;
+ }
// Choose queue and create device, unless the device was specified in importParams.
if (!importedDevice) {
// 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;
+ std::optional<uint32_t> gfxQueueFamilyIdxOpt;
+ std::optional<uint32_t> computelessGfxQueueCandidateIdxOpt;
queryQueueFamilyProps();
- for (int i = 0; i < queueFamilyProps.count(); ++i) {
- qCDebug(QRHI_LOG_INFO, "queue family %d: flags=0x%x count=%d",
+ const uint32_t queueFamilyCount = uint32_t(queueFamilyProps.size());
+ for (uint32_t i = 0; i < queueFamilyCount; ++i) {
+ qCDebug(QRHI_LOG_INFO, "queue family %u: flags=0x%x count=%u",
i, queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount);
- if (gfxQueueFamilyIdx == -1
+ if (!gfxQueueFamilyIdxOpt.has_value()
&& (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
- && (!maybeWindow || inst->supportsPresent(physDev, uint32_t(i), maybeWindow)))
+ && (!maybeWindow || inst->supportsPresent(physDev, i, maybeWindow)))
{
if (queueFamilyProps[i].queueFlags & VK_QUEUE_COMPUTE_BIT)
- gfxQueueFamilyIdx = i;
- else if (computelessGfxQueueCandidateIdx == -1)
- computelessGfxQueueCandidateIdx = i;
+ gfxQueueFamilyIdxOpt = i;
+ else if (!computelessGfxQueueCandidateIdxOpt.has_value())
+ computelessGfxQueueCandidateIdxOpt = i;
}
}
- if (gfxQueueFamilyIdx == -1) {
- if (computelessGfxQueueCandidateIdx != -1) {
- gfxQueueFamilyIdx = computelessGfxQueueCandidateIdx;
+ if (gfxQueueFamilyIdxOpt.has_value()) {
+ gfxQueueFamilyIdx = gfxQueueFamilyIdxOpt.value();
+ } else {
+ if (computelessGfxQueueCandidateIdxOpt.has_value()) {
+ gfxQueueFamilyIdx = computelessGfxQueueCandidateIdxOpt.value();
} else {
qWarning("No graphics (or no graphics+present) queue family found");
return false;
}
}
- VkDeviceQueueCreateInfo queueInfo[2];
+ VkDeviceQueueCreateInfo queueInfo = {};
const float prio[] = { 0 };
- memset(queueInfo, 0, sizeof(queueInfo));
- queueInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
- queueInfo[0].queueFamilyIndex = uint32_t(gfxQueueFamilyIdx);
- queueInfo[0].queueCount = 1;
- queueInfo[0].pQueuePriorities = prio;
+ queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ queueInfo.queueFamilyIndex = gfxQueueFamilyIdx;
+ queueInfo.queueCount = 1;
+ queueInfo.pQueuePriorities = prio;
QList<const char *> devLayers;
if (inst->layers().contains("VK_LAYER_KHRONOS_validation"))
@@ -526,44 +643,69 @@ bool QRhiVulkan::create(QRhi::Flags flags)
if (devExtCount) {
QList<VkExtensionProperties> extProps(devExtCount);
f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, extProps.data());
- for (const VkExtensionProperties &p : qAsConst(extProps))
+ for (const VkExtensionProperties &p : std::as_const(extProps))
devExts.append({ p.extensionName, p.specVersion });
}
- qCDebug(QRHI_LOG_INFO, "%d device extensions available", int(devExts.count()));
+ qCDebug(QRHI_LOG_INFO, "%d device extensions available", int(devExts.size()));
QList<const char *> requestedDevExts;
requestedDevExts.append("VK_KHR_swapchain");
- caps.debugMarkers = false;
- if (devExts.contains(VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
- requestedDevExts.append(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
- caps.debugMarkers = true;
+ const bool hasPhysDevProp2 = inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"));
+
+ if (devExts.contains(QByteArrayLiteral("VK_KHR_portability_subset"))) {
+ if (hasPhysDevProp2) {
+ requestedDevExts.append("VK_KHR_portability_subset");
+ } else {
+ qWarning("VK_KHR_portability_subset should be enabled on the device "
+ "but the instance does not have VK_KHR_get_physical_device_properties2 enabled. "
+ "Expect problems.");
+ }
}
- caps.vertexAttribDivisor = false;
+#ifdef VK_EXT_vertex_attribute_divisor
if (devExts.contains(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
- if (inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"))) {
+ if (hasPhysDevProp2) {
requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
caps.vertexAttribDivisor = true;
}
}
+#endif
+
+#ifdef VK_KHR_create_renderpass2
+ if (devExts.contains(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)) {
+ requestedDevExts.append(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
+ caps.renderPass2KHR = true;
+ }
+#endif
+
+#ifdef VK_KHR_depth_stencil_resolve
+ if (devExts.contains(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME)) {
+ requestedDevExts.append(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
+ caps.depthStencilResolveKHR = true;
+ }
+#endif
for (const QByteArray &ext : requestedDeviceExtensions) {
- if (!ext.isEmpty()) {
- if (devExts.contains(ext))
+ if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
+ if (devExts.contains(ext)) {
requestedDevExts.append(ext.constData());
- else
- qWarning("Device extension %s is not supported", ext.constData());
+ } else {
+ qWarning("Device extension %s requested in QRhiVulkanInitParams is not supported",
+ ext.constData());
+ }
}
}
QByteArrayList envExtList = qgetenv("QT_VULKAN_DEVICE_EXTENSIONS").split(';');
for (const QByteArray &ext : envExtList) {
if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
- if (devExts.contains(ext))
+ if (devExts.contains(ext)) {
requestedDevExts.append(ext.constData());
- else
- qWarning("Device extension %s is not supported", ext.constData());
+ } else {
+ qWarning("Device extension %s requested in QT_VULKAN_DEVICE_EXTENSIONS is not supported",
+ ext.constData());
+ }
}
}
@@ -573,35 +715,50 @@ bool QRhiVulkan::create(QRhi::Flags flags)
qCDebug(QRHI_LOG_INFO, " %s", ext);
}
- VkDeviceCreateInfo devInfo;
- memset(&devInfo, 0, sizeof(devInfo));
+ VkDeviceCreateInfo devInfo = {};
devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
devInfo.queueCreateInfoCount = 1;
- devInfo.pQueueCreateInfos = queueInfo;
- devInfo.enabledLayerCount = uint32_t(devLayers.count());
+ devInfo.pQueueCreateInfos = &queueInfo;
+ devInfo.enabledLayerCount = uint32_t(devLayers.size());
devInfo.ppEnabledLayerNames = devLayers.constData();
- devInfo.enabledExtensionCount = uint32_t(requestedDevExts.count());
+ devInfo.enabledExtensionCount = uint32_t(requestedDevExts.size());
devInfo.ppEnabledExtensionNames = requestedDevExts.constData();
- VkPhysicalDeviceFeatures features;
- memset(&features, 0, sizeof(features));
- if (physDevFeatures.wideLines)
- features.wideLines = VK_TRUE;
- if (physDevFeatures.largePoints)
- features.largePoints = VK_TRUE;
- if (physDevFeatures.tessellationShader)
- features.tessellationShader = VK_TRUE;
- if (physDevFeatures.textureCompressionETC2)
- features.textureCompressionETC2 = VK_TRUE;
- if (physDevFeatures.textureCompressionASTC_LDR)
- features.textureCompressionASTC_LDR = VK_TRUE;
- if (physDevFeatures.textureCompressionBC)
- features.textureCompressionBC = VK_TRUE;
- if (physDevFeatures.geometryShader)
- features.geometryShader = VK_TRUE;
- if (physDevFeatures.fillModeNonSolid)
- features.fillModeNonSolid = VK_TRUE;
- devInfo.pEnabledFeatures = &features;
+ // Enable all features that are reported as supported, except
+ // robustness because that potentially affects performance.
+ //
+ // Enabling all features mainly serves third-party renderers that may
+ // use the VkDevice created here. For the record, the backend here
+ // optionally relies on the following features, meaning just for our
+ // (QRhi/Quick/Quick 3D) purposes it would be sufficient to
+ // enable-if-supported only the following:
+ //
+ // wideLines, largePoints, fillModeNonSolid,
+ // tessellationShader, geometryShader
+ // textureCompressionETC2, textureCompressionASTC_LDR, textureCompressionBC
+
+#ifdef VK_VERSION_1_1
+ physDevFeaturesChainable.features.robustBufferAccess = VK_FALSE;
+#endif
+#ifdef VK_VERSION_1_3
+ physDevFeatures13.robustImageAccess = VK_FALSE;
+#endif
+
+#ifdef VK_VERSION_1_1
+ if (caps.apiVersion >= QVersionNumber(1, 1)) {
+ // For a >=1.2 implementation at run time, this will enable all
+ // (1.0-1.3) features reported as supported, except the ones we turn
+ // off explicitly above. For a 1.1 implementation at run time, this
+ // only enables the 1.0 and multiview features reported as
+ // supported. We will not be bothering with the Vulkan 1.1
+ // individual feature struct nonsense.
+ devInfo.pNext = &physDevFeaturesChainable;
+ } else
+#endif
+ {
+ physDevFeatures.robustBufferAccess = VK_FALSE;
+ devInfo.pEnabledFeatures = &physDevFeatures;
+ }
VkResult err = f->vkCreateDevice(physDev, &devInfo, nullptr, &dev);
if (err != VK_SUCCESS) {
@@ -610,6 +767,13 @@ bool QRhiVulkan::create(QRhi::Flags flags)
}
} else {
qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev);
+
+ // Here we have no way to tell if the extensions got enabled or not.
+ // Pretend it's all there and supported. If getProcAddress fails, we'll
+ // handle that gracefully.
+ caps.vertexAttribDivisor = true;
+ caps.renderPass2KHR = true;
+ caps.depthStencilResolveKHR = true;
}
vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
@@ -618,20 +782,12 @@ bool QRhiVulkan::create(QRhi::Flags flags)
inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR"));
vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(
inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfacePresentModesKHR"));
- if (!vkGetPhysicalDeviceSurfaceCapabilitiesKHR
- || !vkGetPhysicalDeviceSurfaceFormatsKHR
- || !vkGetPhysicalDeviceSurfacePresentModesKHR)
- {
- qWarning("Physical device surface queries not available");
- return false;
- }
df = inst->deviceFunctions(dev);
- VkCommandPoolCreateInfo poolInfo;
- memset(&poolInfo, 0, sizeof(poolInfo));
+ VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
- poolInfo.queueFamilyIndex = uint32_t(gfxQueueFamilyIdx);
+ poolInfo.queueFamilyIndex = gfxQueueFamilyIdx;
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
VkResult err = df->vkCreateCommandPool(dev, &poolInfo, nullptr, &cmdPool[i]);
if (err != VK_SUCCESS) {
@@ -640,13 +796,10 @@ bool QRhiVulkan::create(QRhi::Flags flags)
}
}
- if (gfxQueueFamilyIdx < 0) {
- // this is when importParams is faulty and did not specify the queue family index
- qWarning("No queue family index provided");
- return false;
- }
+ qCDebug(QRHI_LOG_INFO, "Using queue family index %u and queue index %u",
+ gfxQueueFamilyIdx, gfxQueueIdx);
- df->vkGetDeviceQueue(dev, uint32_t(gfxQueueFamilyIdx), gfxQueueIdx, &gfxQueue);
+ df->vkGetDeviceQueue(dev, gfxQueueFamilyIdx, gfxQueueIdx, &gfxQueue);
if (queueFamilyProps.isEmpty())
queryQueueFamilyProps();
@@ -661,40 +814,51 @@ bool QRhiVulkan::create(QRhi::Flags flags)
caps.wideLines = physDevFeatures.wideLines;
- caps.texture3DSliceAs2D = caps.vulkan11OrHigher;
+ caps.texture3DSliceAs2D = caps.apiVersion >= QVersionNumber(1, 1);
caps.tessellation = physDevFeatures.tessellationShader;
caps.geometryShader = physDevFeatures.geometryShader;
caps.nonFillPolygonMode = physDevFeatures.fillModeNonSolid;
+#ifdef VK_VERSION_1_2
+ if (caps.apiVersion >= QVersionNumber(1, 2))
+ caps.multiView = physDevFeatures11IfApi12OrNewer.multiview;
+#endif
+
+#ifdef VK_VERSION_1_1
+ if (caps.apiVersion == QVersionNumber(1, 1))
+ caps.multiView = multiviewFeaturesIfApi11.multiview;
+#endif
+
+ // With Vulkan 1.2 renderpass2 and depth_stencil_resolve are core, but we
+ // have to support the case of 1.1 + extensions, in particular for the Quest
+ // 3 (Android, Vulkan 1.1 at the time of writing). Therefore, always rely on
+ // the KHR extension for now.
+#ifdef VK_KHR_create_renderpass2
+ if (caps.renderPass2KHR) {
+ vkCreateRenderPass2KHR = reinterpret_cast<PFN_vkCreateRenderPass2KHR>(f->vkGetDeviceProcAddr(dev, "vkCreateRenderPass2KHR"));
+ if (!vkCreateRenderPass2KHR) // handle it gracefully, the caps flag may be incorrect when using an imported VkDevice
+ caps.renderPass2KHR = false;
+ }
+#endif
+
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));
+ VmaVulkanFunctions funcs = {};
+ funcs.vkGetInstanceProcAddr = wrap_vkGetInstanceProcAddr;
+ funcs.vkGetDeviceProcAddr = wrap_vkGetDeviceProcAddr;
+
+ VmaAllocatorCreateInfo allocatorInfo = {};
// A QRhi is supposed to be used from one single thread only. Disable
// the allocator's own mutexes. This gives a performance boost.
allocatorInfo.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
allocatorInfo.physicalDevice = physDev;
allocatorInfo.device = dev;
- allocatorInfo.pVulkanFunctions = &afuncs;
+ allocatorInfo.pVulkanFunctions = &funcs;
+ allocatorInfo.instance = inst->vkInstance();
+ allocatorInfo.vulkanApiVersion = VK_MAKE_VERSION(caps.apiVersion.majorVersion(),
+ caps.apiVersion.minorVersion(),
+ caps.apiVersion.microVersion());
VmaAllocator vmaallocator;
VkResult err = vmaCreateAllocator(&allocatorInfo, &vmaallocator);
if (err != VK_SUCCESS) {
@@ -713,8 +877,7 @@ bool QRhiVulkan::create(QRhi::Flags flags)
else
qWarning("Failed to create initial descriptor pool: %d", err);
- VkQueryPoolCreateInfo timestampQueryPoolInfo;
- memset(&timestampQueryPoolInfo, 0, sizeof(timestampQueryPoolInfo));
+ VkQueryPoolCreateInfo timestampQueryPoolInfo = {};
timestampQueryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
timestampQueryPoolInfo.queryType = VK_QUERY_TYPE_TIMESTAMP;
timestampQueryPoolInfo.queryCount = QVK_MAX_ACTIVE_TIMESTAMP_PAIRS * 2;
@@ -726,12 +889,14 @@ bool QRhiVulkan::create(QRhi::Flags flags)
timestampQueryPoolMap.resize(QVK_MAX_ACTIVE_TIMESTAMP_PAIRS); // 1 bit per pair
timestampQueryPoolMap.fill(false);
- if (caps.debugMarkers) {
- 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"));
+#ifdef VK_EXT_debug_utils
+ if (caps.debugUtils) {
+ vkSetDebugUtilsObjectNameEXT = reinterpret_cast<PFN_vkSetDebugUtilsObjectNameEXT>(f->vkGetDeviceProcAddr(dev, "vkSetDebugUtilsObjectNameEXT"));
+ vkCmdBeginDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdBeginDebugUtilsLabelEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdBeginDebugUtilsLabelEXT"));
+ vkCmdEndDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdEndDebugUtilsLabelEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdEndDebugUtilsLabelEXT"));
+ vkCmdInsertDebugUtilsLabelEXT = reinterpret_cast<PFN_vkCmdInsertDebugUtilsLabelEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdInsertDebugUtilsLabelEXT"));
}
+#endif
deviceLost = false;
@@ -741,6 +906,7 @@ bool QRhiVulkan::create(QRhi::Flags flags)
nativeHandlesStruct.gfxQueueIdx = gfxQueueIdx;
nativeHandlesStruct.gfxQueue = gfxQueue;
nativeHandlesStruct.vmemAllocator = allocator;
+ nativeHandlesStruct.inst = inst;
return true;
}
@@ -809,8 +975,7 @@ VkResult QRhiVulkan::createDescriptorPool(VkDescriptorPool *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));
+ VkDescriptorPoolCreateInfo 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
@@ -832,7 +997,7 @@ bool QRhiVulkan::allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, V
return r;
};
- int lastPoolIdx = descriptorPools.count() - 1;
+ int lastPoolIdx = descriptorPools.size() - 1;
for (int i = lastPoolIdx; i >= 0; --i) {
if (descriptorPools[i].refCount == 0) {
df->vkResetDescriptorPool(dev, descriptorPools[i].pool, 0);
@@ -852,7 +1017,7 @@ bool QRhiVulkan::allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, V
VkResult poolErr = createDescriptorPool(&newPool);
if (poolErr == VK_SUCCESS) {
descriptorPools.append(newPool);
- lastPoolIdx = descriptorPools.count() - 1;
+ lastPoolIdx = descriptorPools.size() - 1;
VkResult err = tryAllocate(lastPoolIdx);
if (err != VK_SUCCESS) {
qWarning("Failed to allocate descriptor set from new pool too, giving up: %d", err);
@@ -960,8 +1125,7 @@ static inline VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture
return srgb ? VK_FORMAT_ASTC_12x12_SRGB_BLOCK : VK_FORMAT_ASTC_12x12_UNORM_BLOCK;
default:
- Q_UNREACHABLE();
- return VK_FORMAT_R8G8B8A8_UNORM;
+ Q_UNREACHABLE_RETURN(VK_FORMAT_R8G8B8A8_UNORM);
}
}
@@ -1063,8 +1227,7 @@ bool QRhiVulkan::createTransientImage(VkFormat format,
VkResult err;
for (int i = 0; i < count; ++i) {
- VkImageCreateInfo imgInfo;
- memset(&imgInfo, 0, sizeof(imgInfo));
+ VkImageCreateInfo imgInfo = {};
imgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imgInfo.imageType = VK_IMAGE_TYPE_2D;
imgInfo.format = format;
@@ -1089,8 +1252,7 @@ bool QRhiVulkan::createTransientImage(VkFormat format,
df->vkGetImageMemoryRequirements(dev, images[i], &memReq);
}
- VkMemoryAllocateInfo memInfo;
- memset(&memInfo, 0, sizeof(memInfo));
+ VkMemoryAllocateInfo memInfo = {};
memInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memInfo.allocationSize = aligned(memReq.size, memReq.alignment) * VkDeviceSize(count);
@@ -1118,8 +1280,7 @@ bool QRhiVulkan::createTransientImage(VkFormat format,
}
ofs += aligned(memReq.size, memReq.alignment);
- VkImageViewCreateInfo imgViewInfo;
- memset(&imgViewInfo, 0, sizeof(imgViewInfo));
+ VkImageViewCreateInfo imgViewInfo = {};
imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
imgViewInfo.image = images[i];
imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
@@ -1173,18 +1334,18 @@ static void fillRenderPassCreateInfo(VkRenderPassCreateInfo *rpInfo,
{
memset(subpassDesc, 0, sizeof(VkSubpassDescription));
subpassDesc->pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- subpassDesc->colorAttachmentCount = uint32_t(rpD->colorRefs.count());
+ subpassDesc->colorAttachmentCount = uint32_t(rpD->colorRefs.size());
subpassDesc->pColorAttachments = !rpD->colorRefs.isEmpty() ? rpD->colorRefs.constData() : nullptr;
subpassDesc->pDepthStencilAttachment = rpD->hasDepthStencil ? &rpD->dsRef : nullptr;
subpassDesc->pResolveAttachments = !rpD->resolveRefs.isEmpty() ? rpD->resolveRefs.constData() : nullptr;
memset(rpInfo, 0, sizeof(VkRenderPassCreateInfo));
rpInfo->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
- rpInfo->attachmentCount = uint32_t(rpD->attDescs.count());
+ rpInfo->attachmentCount = uint32_t(rpD->attDescs.size());
rpInfo->pAttachments = rpD->attDescs.constData();
rpInfo->subpassCount = 1;
rpInfo->pSubpasses = subpassDesc;
- rpInfo->dependencyCount = uint32_t(rpD->subpassDeps.count());
+ rpInfo->dependencyCount = uint32_t(rpD->subpassDeps.size());
rpInfo->pDependencies = !rpD->subpassDeps.isEmpty() ? rpD->subpassDeps.constData() : nullptr;
}
@@ -1192,8 +1353,7 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD
{
// attachment list layout is color (1), ds (0-1), resolve (0-1)
- VkAttachmentDescription attDesc;
- memset(&attDesc, 0, sizeof(attDesc));
+ VkAttachmentDescription attDesc = {};
attDesc.format = colorFormat;
attDesc.samples = samples;
attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
@@ -1207,6 +1367,8 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD
rpD->colorRefs.append({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
rpD->hasDepthStencil = hasDepthStencil;
+ rpD->hasDepthStencilResolve = false;
+ rpD->multiViewCount = 0;
if (hasDepthStencil) {
// clear on load + no store + lazy alloc + transient image should play
@@ -1241,8 +1403,7 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD
}
// Replace the first implicit dep (TOP_OF_PIPE / ALL_COMMANDS) with our own.
- VkSubpassDependency subpassDep;
- memset(&subpassDep, 0, sizeof(subpassDep));
+ VkSubpassDependency subpassDep = {};
subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL;
subpassDep.dstSubpass = 0;
subpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
@@ -1277,29 +1438,190 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD
return true;
}
+struct MultiViewRenderPassSetupHelper
+{
+ bool prepare(VkRenderPassCreateInfo *rpInfo, int multiViewCount, bool multiViewCap)
+ {
+ if (multiViewCount < 2)
+ return true;
+ if (!multiViewCap) {
+ qWarning("Cannot create multiview render pass without support for the Vulkan 1.1 multiview feature");
+ return false;
+ }
+#ifdef VK_VERSION_1_1
+ uint32_t allViewsMask = 0;
+ for (uint32_t i = 0; i < uint32_t(multiViewCount); ++i)
+ allViewsMask |= (1 << i);
+ multiViewMask = allViewsMask;
+ multiViewCorrelationMask = allViewsMask;
+ multiViewInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
+ multiViewInfo.subpassCount = 1;
+ multiViewInfo.pViewMasks = &multiViewMask;
+ multiViewInfo.correlationMaskCount = 1;
+ multiViewInfo.pCorrelationMasks = &multiViewCorrelationMask;
+ rpInfo->pNext = &multiViewInfo;
+#endif
+ return true;
+ }
+
+#ifdef VK_VERSION_1_1
+ VkRenderPassMultiviewCreateInfo multiViewInfo = {};
+ uint32_t multiViewMask = 0;
+ uint32_t multiViewCorrelationMask = 0;
+#endif
+};
+
+#ifdef VK_KHR_create_renderpass2
+// Effectively converts a VkRenderPassCreateInfo into a VkRenderPassCreateInfo2,
+// adding depth-stencil resolve support. Assumes a single subpass and no subpass
+// dependencies.
+struct RenderPass2SetupHelper
+{
+ bool prepare(VkRenderPassCreateInfo2 *rpInfo2, const VkRenderPassCreateInfo *rpInfo, const QVkRenderPassDescriptor *rpD, int multiViewCount) {
+ *rpInfo2 = {};
+
+ viewMask = 0;
+ if (multiViewCount >= 2) {
+ for (uint32_t i = 0; i < uint32_t(multiViewCount); ++i)
+ viewMask |= (1 << i);
+ }
+
+ attDescs2.resize(rpInfo->attachmentCount);
+ for (qsizetype i = 0; i < attDescs2.count(); ++i) {
+ VkAttachmentDescription2KHR &att2(attDescs2[i]);
+ const VkAttachmentDescription &att(rpInfo->pAttachments[i]);
+ att2 = {};
+ att2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
+ att2.flags = att.flags;
+ att2.format = att.format;
+ att2.samples = att.samples;
+ att2.loadOp = att.loadOp;
+ att2.storeOp = att.storeOp;
+ att2.stencilLoadOp = att.stencilLoadOp;
+ att2.stencilStoreOp = att.stencilStoreOp;
+ att2.initialLayout = att.initialLayout;
+ att2.finalLayout = att.finalLayout;
+ }
+
+ attRefs2.clear();
+ subpass2 = {};
+ subpass2.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR;
+ const VkSubpassDescription &subpassDesc(rpInfo->pSubpasses[0]);
+ subpass2.flags = subpassDesc.flags;
+ subpass2.pipelineBindPoint = subpassDesc.pipelineBindPoint;
+ if (multiViewCount >= 2)
+ subpass2.viewMask = viewMask;
+
+ // color attachment refs
+ qsizetype startIndex = attRefs2.count();
+ for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) {
+ attRefs2.append({});
+ VkAttachmentReference2KHR &attref2(attRefs2.last());
+ const VkAttachmentReference &attref(subpassDesc.pColorAttachments[j]);
+ attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
+ attref2.attachment = attref.attachment;
+ attref2.layout = attref.layout;
+ attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ }
+ subpass2.colorAttachmentCount = subpassDesc.colorAttachmentCount;
+ subpass2.pColorAttachments = attRefs2.constData() + startIndex;
+
+ // color resolve refs
+ if (subpassDesc.pResolveAttachments) {
+ startIndex = attRefs2.count();
+ for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) {
+ attRefs2.append({});
+ VkAttachmentReference2KHR &attref2(attRefs2.last());
+ const VkAttachmentReference &attref(subpassDesc.pResolveAttachments[j]);
+ attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
+ attref2.attachment = attref.attachment;
+ attref2.layout = attref.layout;
+ attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ }
+ subpass2.pResolveAttachments = attRefs2.constData() + startIndex;
+ }
+
+ // depth-stencil ref
+ if (subpassDesc.pDepthStencilAttachment) {
+ startIndex = attRefs2.count();
+ attRefs2.append({});
+ VkAttachmentReference2KHR &attref2(attRefs2.last());
+ const VkAttachmentReference &attref(*subpassDesc.pDepthStencilAttachment);
+ attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
+ attref2.attachment = attref.attachment;
+ attref2.layout = attref.layout;
+ attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ subpass2.pDepthStencilAttachment = attRefs2.constData() + startIndex;
+ }
+
+ // depth-stencil resolve ref
+#ifdef VK_KHR_depth_stencil_resolve
+ dsResolveDesc = {};
+ if (rpD->hasDepthStencilResolve) {
+ startIndex = attRefs2.count();
+ attRefs2.append({});
+ VkAttachmentReference2KHR &attref2(attRefs2.last());
+ attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
+ attref2.attachment = rpD->dsResolveRef.attachment;
+ attref2.layout = rpD->dsResolveRef.layout;
+ attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ dsResolveDesc.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR;
+ dsResolveDesc.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
+ dsResolveDesc.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
+ dsResolveDesc.pDepthStencilResolveAttachment = attRefs2.constData() + startIndex;
+ subpass2.pNext = &dsResolveDesc;
+ }
+#endif
+
+ rpInfo2->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR;
+ rpInfo2->pNext = nullptr; // the 1.1 VkRenderPassMultiviewCreateInfo is part of the '2' structs
+ rpInfo2->flags = rpInfo->flags;
+ rpInfo2->attachmentCount = rpInfo->attachmentCount;
+ rpInfo2->pAttachments = attDescs2.constData();
+ rpInfo2->subpassCount = 1;
+ rpInfo2->pSubpasses = &subpass2;
+ if (multiViewCount >= 2) {
+ rpInfo2->correlatedViewMaskCount = 1;
+ rpInfo2->pCorrelatedViewMasks = &viewMask;
+ }
+ return true;
+ }
+
+ QVarLengthArray<VkAttachmentDescription2KHR, 8> attDescs2;
+ QVarLengthArray<VkAttachmentReference2KHR, 8> attRefs2;
+ VkSubpassDescription2KHR subpass2;
+#ifdef VK_KHR_depth_stencil_resolve
+ VkSubpassDescriptionDepthStencilResolveKHR dsResolveDesc;
+#endif
+ uint32_t viewMask;
+};
+#endif // VK_KHR_create_renderpass2
+
bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
const QRhiColorAttachment *firstColorAttachment,
const QRhiColorAttachment *lastColorAttachment,
bool preserveColor,
bool preserveDs,
+ bool storeDs,
QRhiRenderBuffer *depthStencilBuffer,
- QRhiTexture *depthTexture)
+ QRhiTexture *depthTexture,
+ QRhiTexture *depthResolveTexture)
{
- // attachment list layout is color (0-8), ds (0-1), resolve (0-8)
+ // attachment list layout is color (0-8), ds (0-1), resolve (0-8), ds resolve (0-1)
+ int multiViewCount = 0;
for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) {
QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
Q_ASSERT(texD || rbD);
- const VkFormat vkformat = texD ? texD->vkformat : rbD->vkformat;
+ const VkFormat vkformat = texD ? texD->viewFormat : rbD->vkformat;
const VkSampleCountFlagBits samples = texD ? texD->samples : rbD->samples;
- VkAttachmentDescription attDesc;
- memset(&attDesc, 0, sizeof(attDesc));
+ VkAttachmentDescription attDesc = {};
attDesc.format = vkformat;
attDesc.samples = samples;
attDesc.loadOp = preserveColor ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR;
- attDesc.storeOp = it->resolveTexture() ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
+ attDesc.storeOp = (it->resolveTexture() && !preserveColor) ? 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
@@ -1307,31 +1629,41 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
rpD->attDescs.append(attDesc);
- const VkAttachmentReference ref = { uint32_t(rpD->attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ const VkAttachmentReference ref = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
rpD->colorRefs.append(ref);
+
+ if (it->multiViewCount() >= 2) {
+ if (multiViewCount > 0 && multiViewCount != it->multiViewCount())
+ qWarning("Inconsistent multiViewCount in color attachment set");
+ else
+ multiViewCount = it->multiViewCount();
+ } else if (multiViewCount > 0) {
+ qWarning("Mixing non-multiview color attachments within a multiview render pass");
+ }
}
+ Q_ASSERT(multiViewCount == 0 || multiViewCount >= 2);
+ rpD->multiViewCount = uint32_t(multiViewCount);
rpD->hasDepthStencil = depthStencilBuffer || depthTexture;
if (rpD->hasDepthStencil) {
- const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->vkformat
+ const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->viewFormat
: 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));
+ const VkAttachmentStoreOp storeOp = storeDs ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ VkAttachmentDescription 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.initialLayout = preserveDs ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
rpD->attDescs.append(attDesc);
}
- rpD->dsRef = { uint32_t(rpD->attDescs.count() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
+ rpD->dsRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) {
if (it->resolveTexture()) {
@@ -1351,9 +1683,8 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
int(srcFormat), int(dstFormat));
}
- VkAttachmentDescription attDesc;
- memset(&attDesc, 0, sizeof(attDesc));
- attDesc.format = dstFormat;
+ VkAttachmentDescription attDesc = {};
+ attDesc.format = rtexD->viewFormat;
attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored
attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
@@ -1363,14 +1694,39 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
rpD->attDescs.append(attDesc);
- const VkAttachmentReference ref = { uint32_t(rpD->attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ const VkAttachmentReference ref = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
rpD->resolveRefs.append(ref);
} else {
const VkAttachmentReference ref = { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
rpD->resolveRefs.append(ref);
}
}
- Q_ASSERT(rpD->colorRefs.count() == rpD->resolveRefs.count());
+ Q_ASSERT(rpD->colorRefs.size() == rpD->resolveRefs.size());
+
+ rpD->hasDepthStencilResolve = rpD->hasDepthStencil && depthResolveTexture;
+ if (rpD->hasDepthStencilResolve) {
+ QVkTexture *rtexD = QRHI_RES(QVkTexture, depthResolveTexture);
+ if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT)
+ qWarning("Resolving into a multisample depth texture is not supported");
+
+ QVkTexture *texD = QRHI_RES(QVkTexture, depthResolveTexture);
+ if (texD->vkformat != rtexD->vkformat) {
+ qWarning("Multisample resolve between different depth-stencil formats (%d and %d) is not supported.",
+ int(texD->vkformat), int(rtexD->vkformat));
+ }
+
+ VkAttachmentDescription attDesc = {};
+ attDesc.format = rtexD->viewFormat;
+ 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 = attDesc.loadOp;
+ attDesc.stencilStoreOp = attDesc.storeOp;
+ attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ rpD->attDescs.append(attDesc);
+ }
+ rpD->dsResolveRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
// rpD->subpassDeps stays empty: don't yet know the correct initial/final
// access and stage stuff for the implicit deps at this point, so leave it
@@ -1381,10 +1737,35 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
VkSubpassDescription subpassDesc;
fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
- VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
- if (err != VK_SUCCESS) {
- qWarning("Failed to create renderpass: %d", err);
+ MultiViewRenderPassSetupHelper multiViewHelper;
+ if (!multiViewHelper.prepare(&rpInfo, multiViewCount, caps.multiView))
return false;
+
+#ifdef VK_KHR_create_renderpass2
+ if (rpD->hasDepthStencilResolve && caps.renderPass2KHR) {
+ // Use the KHR extension, not the 1.2 core API, in order to support Vulkan 1.1.
+ VkRenderPassCreateInfo2KHR rpInfo2;
+ RenderPass2SetupHelper rp2Helper;
+ if (!rp2Helper.prepare(&rpInfo2, &rpInfo, rpD, multiViewCount))
+ return false;
+
+ VkResult err = vkCreateRenderPass2KHR(dev, &rpInfo2, nullptr, &rpD->rp);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create renderpass (using VkRenderPassCreateInfo2KHR): %d", err);
+ return false;
+ }
+ } else
+#endif
+ {
+ if (rpD->hasDepthStencilResolve) {
+ qWarning("Resolving multisample depth-stencil buffers is not supported without "
+ "VK_KHR_depth_stencil_resolve and VK_KHR_create_renderpass2");
+ }
+ VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create renderpass: %d", err);
+ return false;
+ }
}
return true;
@@ -1425,21 +1806,45 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
: surfaceCaps.currentTransform;
+ // This looks odd but matches how platforms work in practice.
+ //
+ // On Windows with NVIDIA for example, the only supportedCompositeAlpha
+ // value reported is OPAQUE, nothing else. Yet transparency works
+ // regardless, as long as the native window is set up correctly, so that's
+ // not something we need to handle here.
+ //
+ // On Linux with Intel and Mesa and running on xcb reports, on one
+ // particular system, INHERIT+PRE_MULTIPLIED. Tranparency works, regardless,
+ // presumably due to setting INHERIT.
+ //
+ // On the same setup with Wayland instead of xcb we see
+ // OPAQUE+PRE_MULTIPLIED reported. Here transparency won't work unless
+ // PRE_MULTIPLIED is set.
+ //
+ // Therefore our rules are:
+ // - Prefer INHERIT over OPAQUE.
+ // - Then based on the request, try the requested alpha mode, but if
+ // that's not reported as supported, try also the other (PRE/POST,
+ // POST/PRE) as that is better than nothing. This is not different from
+ // some other backends, e.g. D3D11 with DirectComposition there is also
+ // no control over being straight or pre-multiplied. Whereas with
+ // WGL/GLX/EGL we never had that sort of control.
+
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;
+ if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasPreMulAlpha)) {
+ if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
+ compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
+ else if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
+ compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
+ } else if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasNonPreMulAlpha)) {
+ if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
+ compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
+ else if (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
+ compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
}
VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
@@ -1447,9 +1852,16 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
if (swapChainD->supportsReadback && swapChainD->m_flags.testFlag(QRhiSwapChain::UsedAsTransferSource))
usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+ const bool stereo = bool(swapChainD->m_window) && (swapChainD->m_window->format().stereo())
+ && surfaceCaps.maxImageArrayLayers > 1;
+ swapChainD->stereo = stereo;
+
VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
if (swapChainD->m_flags.testFlag(QRhiSwapChain::NoVSync)) {
- if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_MAILBOX_KHR))
+ // Stereo has a weird bug, when using VK_PRESENT_MODE_MAILBOX_KHR,
+ // black screen is shown, but there is no validation error.
+ // Detected on Windows, with NVidia RTX A series (at least 4000 and 6000) driver 535.98
+ if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_MAILBOX_KHR) && !stereo)
presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
else if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_IMMEDIATE_KHR))
presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
@@ -1466,15 +1878,14 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
reuseExisting ? "recycled" : "new",
reqBufferCount, swapChainD->pixelSize.width(), swapChainD->pixelSize.height(), presentMode);
- VkSwapchainCreateInfoKHR swapChainInfo;
- memset(&swapChainInfo, 0, sizeof(swapChainInfo));
+ VkSwapchainCreateInfoKHR 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.imageArrayLayers = stereo ? 2u : 1u;
swapChainInfo.imageUsage = usage;
swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapChainInfo.preTransform = preTransform;
@@ -1532,12 +1943,13 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
}
}
- VkFenceCreateInfo fenceInfo;
- memset(&fenceInfo, 0, sizeof(fenceInfo));
+ VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
- swapChainD->imageRes.resize(swapChainD->bufferCount);
+ // Double up for stereo
+ swapChainD->imageRes.resize(swapChainD->bufferCount * (stereo ? 2u : 1u));
+
for (int i = 0; i < swapChainD->bufferCount; ++i) {
QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]);
image.image = swapChainImages[i];
@@ -1546,8 +1958,7 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
image.msaaImageView = msaaViews[i];
}
- VkImageViewCreateInfo imgViewInfo;
- memset(&imgViewInfo, 0, sizeof(imgViewInfo));
+ VkImageViewCreateInfo imgViewInfo = {};
imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
imgViewInfo.image = swapChainImages[i];
imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
@@ -1566,11 +1977,40 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
image.lastUse = QVkSwapChain::ImageResources::ScImageUseNone;
}
+ if (stereo) {
+ for (int i = 0; i < swapChainD->bufferCount; ++i) {
+ QVkSwapChain::ImageResources &image(swapChainD->imageRes[i + swapChainD->bufferCount]);
+ image.image = swapChainImages[i];
+ if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
+ image.msaaImage = msaaImages[i];
+ image.msaaImageView = msaaViews[i];
+ }
+
+ VkImageViewCreateInfo 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.baseArrayLayer = 1;
+ 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));
+ VkSemaphoreCreateInfo semInfo = {};
semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
@@ -1634,7 +2074,7 @@ void QRhiVulkan::releaseSwapChainResources(QRhiSwapChain *swapChain)
}
}
- for (int i = 0; i < swapChainD->bufferCount; ++i) {
+ for (int i = 0; i < swapChainD->bufferCount * (swapChainD->stereo ? 2 : 1); ++i) {
QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]);
if (image.fb) {
df->vkDestroyFramebuffer(dev, image.fb, nullptr);
@@ -1679,12 +2119,32 @@ void QRhiVulkan::ensureCommandPoolForNewFrame()
df->vkResetCommandPool(dev, cmdPool[currentFrameSlot], flags);
}
+double QRhiVulkan::elapsedSecondsFromTimestamp(quint64 timestamp[2], bool *ok)
+{
+ 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;
+ const double elapsedSec = elapsedMs / 1000.0;
+ *ok = true;
+ return elapsedSec;
+ }
+ *ok = false;
+ return 0;
+}
+
QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags)
{
QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
const int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0;
QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]);
+ inst->handle()->beginFrame(swapChainD->window);
+
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)
@@ -1728,30 +2188,6 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
// mess up A's in-flight commands (as they are not in flight anymore).
waitCommandCompletion(frameResIndex);
- // 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, uint32_t(frame.timestampQueryIndex), 2,
- 2 * sizeof(quint64), timestamp, sizeof(quint64),
- VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_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;
- runGpuFrameTimeCallbacks(elapsedMs);
- }
- } else {
- qWarning("Failed to query timestamp: %d", err);
- }
- }
-
currentFrameSlot = int(swapChainD->currentFrameSlot);
currentSwapChain = swapChainD;
if (swapChainD->ds)
@@ -1765,31 +2201,55 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
if (cbres != QRhi::FrameOpSuccess)
return cbres;
- // when profiling is enabled, pick a free query (pair) from the pool
- int timestampQueryIdx = -1;
- if (hasGpuFrameTimeCallback() && swapChainD->bufferCount > 1) { // no timestamps if not having at least 2 frames in flight
- for (int i = 0; i < timestampQueryPoolMap.count(); ++i) {
+ swapChainD->cbWrapper.cb = frame.cmdBuf;
+
+ QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
+ swapChainD->rtWrapper.d.fb = image.fb;
+
+ if (swapChainD->stereo) {
+ QVkSwapChain::ImageResources &image(
+ swapChainD->imageRes[swapChainD->currentImageIndex + swapChainD->bufferCount]);
+ swapChainD->rtWrapperRight.d.fb = image.fb;
+ }
+
+ prepareNewFrame(&swapChainD->cbWrapper);
+
+ // 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, uint32_t(frame.timestampQueryIndex), 2,
+ 2 * sizeof(quint64), timestamp, sizeof(quint64),
+ VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
+ timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2);
+ frame.timestampQueryIndex = -1;
+ if (err == VK_SUCCESS) {
+ bool ok = false;
+ const double elapsedSec = elapsedSecondsFromTimestamp(timestamp, &ok);
+ if (ok)
+ swapChainD->cbWrapper.lastGpuTime = elapsedSec;
+ } else {
+ qWarning("Failed to query timestamp: %d", err);
+ }
+ }
+
+ // No timestamps if the client did not opt in, or when not having at least 2 frames in flight.
+ if (rhiFlags.testFlag(QRhi::EnableTimestamps) && swapChainD->bufferCount > 1) {
+ int timestampQueryIdx = -1;
+ for (int i = 0; i < timestampQueryPoolMap.size(); ++i) {
if (!timestampQueryPoolMap.testBit(i)) {
timestampQueryPoolMap.setBit(i);
timestampQueryIdx = i * 2;
break;
}
}
+ if (timestampQueryIdx >= 0) {
+ df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
+ // record timestamp at the start of the command buffer
+ df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ timestampQueryPool, uint32_t(timestampQueryIdx));
+ frame.timestampQueryIndex = timestampQueryIdx;
+ }
}
- if (timestampQueryIdx >= 0) {
- df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
- // record timestamp at the start of the command buffer
- df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
- timestampQueryPool, uint32_t(timestampQueryIdx));
- frame.timestampQueryIndex = timestampQueryIdx;
- }
-
- swapChainD->cbWrapper.cb = frame.cmdBuf;
-
- QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
- swapChainD->rtWrapper.d.fb = image.fb;
-
- prepareNewFrame(&swapChainD->cbWrapper);
return QRhi::FrameOpSuccess;
}
@@ -1799,6 +2259,10 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram
QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
Q_ASSERT(currentSwapChain == swapChainD);
+ auto cleanup = qScopeGuard([this, swapChainD] {
+ inst->handle()->endFrame(swapChainD->window);
+ });
+
recordPrimaryCommandBuffer(&swapChainD->cbWrapper);
int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0;
@@ -1806,8 +2270,7 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram
QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
if (image.lastUse != QVkSwapChain::ImageResources::ScImageUseRender) {
- VkImageMemoryBarrier presTrans;
- memset(&presTrans, 0, sizeof(presTrans));
+ VkImageMemoryBarrier 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;
@@ -1856,8 +2319,7 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram
if (needsPresent) {
// add the Present to the queue
- VkPresentInfoKHR presInfo;
- memset(&presInfo, 0, sizeof(presInfo));
+ VkPresentInfoKHR presInfo = {};
presInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presInfo.swapchainCount = 1;
presInfo.pSwapchains = &swapChainD->sc;
@@ -1925,8 +2387,7 @@ void QRhiVulkan::prepareNewFrame(QRhiCommandBuffer *cb)
QRhi::FrameOpResult QRhiVulkan::startPrimaryCommandBuffer(VkCommandBuffer *cb)
{
if (!*cb) {
- VkCommandBufferAllocateInfo cmdBufInfo;
- memset(&cmdBufInfo, 0, sizeof(cmdBufInfo));
+ VkCommandBufferAllocateInfo cmdBufInfo = {};
cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmdBufInfo.commandPool = cmdPool[currentFrameSlot];
cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
@@ -1944,8 +2405,7 @@ QRhi::FrameOpResult QRhiVulkan::startPrimaryCommandBuffer(VkCommandBuffer *cb)
}
}
- VkCommandBufferBeginInfo cmdBufBeginInfo;
- memset(&cmdBufBeginInfo, 0, sizeof(cmdBufBeginInfo));
+ VkCommandBufferBeginInfo cmdBufBeginInfo = {};
cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
VkResult err = df->vkBeginCommandBuffer(*cb, &cmdBufBeginInfo);
@@ -1976,8 +2436,7 @@ QRhi::FrameOpResult QRhiVulkan::endAndSubmitPrimaryCommandBuffer(VkCommandBuffer
return QRhi::FrameOpError;
}
- VkSubmitInfo submitInfo;
- memset(&submitInfo, 0, sizeof(submitInfo));
+ VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &cb;
@@ -2008,7 +2467,7 @@ QRhi::FrameOpResult QRhiVulkan::endAndSubmitPrimaryCommandBuffer(VkCommandBuffer
void QRhiVulkan::waitCommandCompletion(int frameSlot)
{
- for (QVkSwapChain *sc : qAsConst(swapchains)) {
+ for (QVkSwapChain *sc : std::as_const(swapchains)) {
const int frameResIndex = sc->bufferCount > 1 ? frameSlot : 0;
QVkSwapChain::FrameResources &frame(sc->frameRes[frameResIndex]);
if (frame.cmdFenceWaitable) {
@@ -2044,6 +2503,24 @@ QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi
prepareNewFrame(cbWrapper);
ofr.active = true;
+ if (rhiFlags.testFlag(QRhi::EnableTimestamps)) {
+ int timestampQueryIdx = -1;
+ for (int i = 0; i < timestampQueryPoolMap.size(); ++i) {
+ if (!timestampQueryPoolMap.testBit(i)) {
+ timestampQueryPoolMap.setBit(i);
+ timestampQueryIdx = i * 2;
+ break;
+ }
+ }
+ if (timestampQueryIdx >= 0) {
+ df->vkCmdResetQueryPool(cbWrapper->cb, timestampQueryPool, uint32_t(timestampQueryIdx), 2);
+ // record timestamp at the start of the command buffer
+ df->vkCmdWriteTimestamp(cbWrapper->cb, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ timestampQueryPool, uint32_t(timestampQueryIdx));
+ ofr.timestampQueryIndex = timestampQueryIdx;
+ }
+ }
+
*cb = cbWrapper;
return QRhi::FrameOpSuccess;
}
@@ -2057,9 +2534,14 @@ QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame(QRhi::EndFrameFlags flags)
QVkCommandBuffer *cbWrapper(ofr.cbWrapper[currentFrameSlot]);
recordPrimaryCommandBuffer(cbWrapper);
+ // record another timestamp, when enabled
+ if (ofr.timestampQueryIndex >= 0) {
+ df->vkCmdWriteTimestamp(cbWrapper->cb, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
+ timestampQueryPool, uint32_t(ofr.timestampQueryIndex + 1));
+ }
+
if (!ofr.cmdFence) {
- VkFenceCreateInfo fenceInfo;
- memset(&fenceInfo, 0, sizeof(fenceInfo));
+ VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
VkResult err = df->vkCreateFence(dev, &fenceInfo, nullptr, &ofr.cmdFence);
if (err != VK_SUCCESS) {
@@ -2080,6 +2562,24 @@ QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame(QRhi::EndFrameFlags flags)
// previous) frame is safe since we waited for completion above.
finishActiveReadbacks(true);
+ // Read the timestamps, if we wrote them.
+ if (ofr.timestampQueryIndex >= 0) {
+ quint64 timestamp[2] = { 0, 0 };
+ VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, uint32_t(ofr.timestampQueryIndex), 2,
+ 2 * sizeof(quint64), timestamp, sizeof(quint64),
+ VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
+ timestampQueryPoolMap.clearBit(ofr.timestampQueryIndex / 2);
+ ofr.timestampQueryIndex = -1;
+ if (err == VK_SUCCESS) {
+ bool ok = false;
+ const double elapsedSec = elapsedSecondsFromTimestamp(timestamp, &ok);
+ if (ok)
+ cbWrapper->lastGpuTime = elapsedSec;
+ } else {
+ qWarning("Failed to query timestamp: %d", err);
+ }
+ }
+
return QRhi::FrameOpSuccess;
}
@@ -2197,6 +2697,13 @@ void QRhiVulkan::activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRe
QRhiPassResourceTracker::TexDepthOutputStage);
depthTexD->lastActiveFrameSlot = currentFrameSlot;
}
+ if (rtD->m_desc.depthResolveTexture()) {
+ QVkTexture *depthResolveTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthResolveTexture());
+ trackedRegisterTexture(&passResTracker, depthResolveTexD,
+ QRhiPassResourceTracker::TexDepthOutput,
+ QRhiPassResourceTracker::TexDepthOutputStage);
+ depthResolveTexD->lastActiveFrameSlot = currentFrameSlot;
+ }
}
void QRhiVulkan::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@@ -2215,8 +2722,7 @@ VkCommandBuffer QRhiVulkan::startSecondaryCommandBuffer(QVkRenderTargetData *rtD
secondaryCb = freeSecondaryCbs[currentFrameSlot].last();
freeSecondaryCbs[currentFrameSlot].removeLast();
} else {
- VkCommandBufferAllocateInfo cmdBufInfo;
- memset(&cmdBufInfo, 0, sizeof(cmdBufInfo));
+ VkCommandBufferAllocateInfo cmdBufInfo = {};
cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmdBufInfo.commandPool = cmdPool[currentFrameSlot];
cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
@@ -2229,12 +2735,10 @@ VkCommandBuffer QRhiVulkan::startSecondaryCommandBuffer(QVkRenderTargetData *rtD
}
}
- VkCommandBufferBeginInfo cmdBufBeginInfo;
- memset(&cmdBufBeginInfo, 0, sizeof(cmdBufBeginInfo));
+ VkCommandBufferBeginInfo cmdBufBeginInfo = {};
cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
cmdBufBeginInfo.flags = rtD ? VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT : 0;
- VkCommandBufferInheritanceInfo cmdBufInheritInfo;
- memset(&cmdBufInheritInfo, 0, sizeof(cmdBufInheritInfo));
+ VkCommandBufferInheritanceInfo cmdBufInheritInfo = {};
cmdBufInheritInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
cmdBufInheritInfo.subpass = 0;
if (rtD) {
@@ -2316,8 +2820,7 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
// 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));
+ VkRenderPassBeginInfo rpBeginInfo = {};
rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
rpBeginInfo.renderPass = rtD->rp->rp;
rpBeginInfo.framebuffer = rtD->fb;
@@ -2342,14 +2845,19 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
float(colorClearValue.alphaF()) } };
cvs.append(cv);
}
- rpBeginInfo.clearValueCount = uint32_t(cvs.count());
+ for (int i = 0; i < rtD->dsResolveAttCount; ++i) {
+ VkClearValue cv;
+ cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() };
+ cvs.append(cv);
+ }
+ rpBeginInfo.clearValueCount = uint32_t(cvs.size());
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::BeginRenderPass;
cmd.args.beginRenderPass.desc = rpBeginInfo;
- cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.count();
+ cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.size();
cmd.args.beginRenderPass.useSecondaryCb = cbD->passUsesSecondaryCb;
- cbD->pools.clearValue.append(cvs.constData(), cvs.count());
+ cbD->pools.clearValue.append(cvs.constData(), cvs.size());
if (cbD->passUsesSecondaryCb)
cbD->activeSecondaryCbStack.append(startSecondaryCommandBuffer(rtD));
@@ -2480,9 +2988,9 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
accessAndIsNewFlag = { 0, false };
QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, cbD->currentComputeSrb);
- const int bindingCount = srbD->m_bindings.count();
+ const int bindingCount = srbD->m_bindings.size();
for (int i = 0; i < bindingCount; ++i) {
- const QRhiShaderResourceBinding::Data *b = srbD->m_bindings.at(i).data();
+ const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
switch (b->type) {
case QRhiShaderResourceBinding::ImageLoad:
case QRhiShaderResourceBinding::ImageStore:
@@ -2515,8 +3023,7 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
if (accessInThisDispatch && !isNewInThisDispatch) {
if (it.key()->resourceType() == QRhiResource::Texture) {
QVkTexture *texD = QRHI_RES(QVkTexture, it.key());
- VkImageMemoryBarrier barrier;
- memset(&barrier, 0, sizeof(barrier));
+ VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
// won't care about subresources, pretend the whole resource was written
@@ -2532,8 +3039,7 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
imageBarriers.append(barrier);
} else {
QVkBuffer *bufD = QRHI_RES(QVkBuffer, it.key());
- VkBufferMemoryBarrier barrier;
- memset(&barrier, 0, sizeof(barrier));
+ VkBufferMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
@@ -2560,12 +3066,12 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, 0, nullptr,
0, nullptr,
- imageBarriers.count(), imageBarriers.constData());
+ imageBarriers.size(), imageBarriers.constData());
}
if (!bufferBarriers.isEmpty()) {
df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, 0, nullptr,
- bufferBarriers.count(), bufferBarriers.constData(),
+ bufferBarriers.size(), bufferBarriers.constData(),
0, nullptr);
}
df->vkCmdDispatch(secondaryCb, uint32_t(x), uint32_t(y), uint32_t(z));
@@ -2575,18 +3081,18 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
cmd.args.imageBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
cmd.args.imageBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
- cmd.args.imageBarrier.count = imageBarriers.count();
- cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count();
- cbD->pools.imageBarrier.append(imageBarriers.constData(), imageBarriers.count());
+ cmd.args.imageBarrier.count = imageBarriers.size();
+ cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
+ cbD->pools.imageBarrier.append(imageBarriers.constData(), imageBarriers.size());
}
if (!bufferBarriers.isEmpty()) {
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::BufferBarrier;
cmd.args.bufferBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
cmd.args.bufferBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
- cmd.args.bufferBarrier.count = bufferBarriers.count();
- cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.count();
- cbD->pools.bufferBarrier.append(bufferBarriers.constData(), bufferBarriers.count());
+ cmd.args.bufferBarrier.count = bufferBarriers.size();
+ cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.size();
+ cbD->pools.bufferBarrier.append(bufferBarriers.constData(), bufferBarriers.size());
}
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::Dispatch;
@@ -2598,8 +3104,7 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv)
{
- VkShaderModuleCreateInfo shaderInfo;
- memset(&shaderInfo, 0, sizeof(shaderInfo));
+ VkShaderModuleCreateInfo shaderInfo = {};
shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shaderInfo.codeSize = size_t(spirv.size());
shaderInfo.pCode = reinterpret_cast<const quint32 *>(spirv.constData());
@@ -2617,8 +3122,7 @@ bool QRhiVulkan::ensurePipelineCache(const void *initialData, size_t initialData
if (pipelineCache)
return true;
- VkPipelineCacheCreateInfo pipelineCacheInfo;
- memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo));
+ VkPipelineCacheCreateInfo pipelineCacheInfo = {};
pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
pipelineCacheInfo.initialDataSize = initialDataSize;
pipelineCacheInfo.pInitialData = initialData;
@@ -2643,12 +3147,11 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
const bool updateAll = descSetIdx < 0;
int frameSlot = updateAll ? 0 : descSetIdx;
while (frameSlot < (updateAll ? QVK_FRAMES_IN_FLIGHT : descSetIdx + 1)) {
- for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
- const QRhiShaderResourceBinding::Data *b = srbD->sortedBindings.at(i).data();
+ for (int i = 0, ie = srbD->sortedBindings.size(); i != ie; ++i) {
+ const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
QVkShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[frameSlot][i]);
- VkWriteDescriptorSet writeInfo;
- memset(&writeInfo, 0, sizeof(writeInfo));
+ VkWriteDescriptorSet writeInfo = {};
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.dstSet = srbD->descSets[frameSlot];
writeInfo.dstBinding = uint32_t(b->binding);
@@ -2668,11 +3171,11 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
bd.ubuf.generation = bufD->generation;
VkDescriptorBufferInfo bufInfo;
bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0];
- bufInfo.offset = VkDeviceSize(b->u.ubuf.offset);
- bufInfo.range = VkDeviceSize(b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size);
+ 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);
- bufferInfoIndex = bufferInfos.count();
+ bufferInfoIndex = bufferInfos.size();
bufferInfos.append(bufInfo);
}
break;
@@ -2694,7 +3197,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
bd.stex.count = data->count;
- imageInfoIndex = imageInfos.count();
+ imageInfoIndex = imageInfos.size();
imageInfos.append(imageInfo);
}
break;
@@ -2715,7 +3218,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
bd.stex.count = data->count;
- imageInfoIndex = imageInfos.count();
+ imageInfoIndex = imageInfos.size();
imageInfos.append(imageInfo);
}
break;
@@ -2731,7 +3234,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
imageInfo[0].sampler = samplerD->sampler;
imageInfo[0].imageView = VK_NULL_HANDLE;
imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
- imageInfoIndex = imageInfos.count();
+ imageInfoIndex = imageInfos.size();
imageInfos.append(imageInfo);
}
break;
@@ -2740,7 +3243,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
case QRhiShaderResourceBinding::ImageLoadStore:
{
QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
- VkImageView view = texD->imageViewForLevel(b->u.simage.level);
+ VkImageView view = texD->perLevelImageViewForLoadStore(b->u.simage.level);
if (view) {
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
bd.simage.id = texD->m_id;
@@ -2749,7 +3252,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
imageInfo[0].sampler = VK_NULL_HANDLE;
imageInfo[0].imageView = view;
imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
- imageInfoIndex = imageInfos.count();
+ imageInfoIndex = imageInfos.size();
imageInfos.append(imageInfo);
}
}
@@ -2764,9 +3267,9 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
bd.sbuf.generation = bufD->generation;
VkDescriptorBufferInfo bufInfo;
bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0];
- bufInfo.offset = VkDeviceSize(b->u.ubuf.offset);
- bufInfo.range = VkDeviceSize(b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size);
- bufferInfoIndex = bufferInfos.count();
+ bufInfo.offset = b->u.ubuf.offset;
+ bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size;
+ bufferInfoIndex = bufferInfos.size();
bufferInfos.append(bufInfo);
}
break;
@@ -2780,7 +3283,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
++frameSlot;
}
- for (int i = 0, writeInfoCount = writeInfos.count(); i < writeInfoCount; ++i) {
+ for (int i = 0, writeInfoCount = writeInfos.size(); i < writeInfoCount; ++i) {
const int bufferInfoIndex = infoIndices[i].first;
const int imageInfoIndex = infoIndices[i].second;
if (bufferInfoIndex >= 0)
@@ -2789,7 +3292,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
writeInfos[i].pImageInfo = imageInfos[imageInfoIndex].constData();
}
- df->vkUpdateDescriptorSets(dev, uint32_t(writeInfos.count()), writeInfos.constData(), 0, nullptr);
+ df->vkUpdateDescriptorSets(dev, uint32_t(writeInfos.size()), writeInfos.constData(), 0, nullptr);
}
static inline bool accessIsWrite(VkAccessFlags access)
@@ -2821,8 +3324,7 @@ void QRhiVulkan::trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, in
return;
}
- VkBufferMemoryBarrier bufMemBarrier;
- memset(&bufMemBarrier, 0, sizeof(bufMemBarrier));
+ VkBufferMemoryBarrier bufMemBarrier = {};
bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
@@ -2836,7 +3338,7 @@ void QRhiVulkan::trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, in
cmd.args.bufferBarrier.srcStageMask = s.stage;
cmd.args.bufferBarrier.dstStageMask = stage;
cmd.args.bufferBarrier.count = 1;
- cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.count();
+ cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.size();
cbD->pools.bufferBarrier.append(bufMemBarrier);
s.access = access;
@@ -2854,8 +3356,7 @@ void QRhiVulkan::trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD,
return;
}
- VkImageMemoryBarrier barrier;
- memset(&barrier, 0, sizeof(barrier));
+ VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.subresourceRange.aspectMask = aspectMaskForTextureFormat(texD->m_format);
barrier.subresourceRange.baseMipLevel = 0;
@@ -2878,7 +3379,7 @@ void QRhiVulkan::trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD,
cmd.args.imageBarrier.srcStageMask = srcStage;
cmd.args.imageBarrier.dstStageMask = stage;
cmd.args.imageBarrier.count = 1;
- cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count();
+ cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
cbD->pools.imageBarrier.append(barrier);
s.layout = layout;
@@ -2890,8 +3391,7 @@ void QRhiVulkan::depthStencilExplicitBarrier(QVkCommandBuffer *cbD, QVkRenderBuf
{
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
- VkImageMemoryBarrier barrier;
- memset(&barrier, 0, sizeof(barrier));
+ VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
barrier.subresourceRange.baseMipLevel = 0;
@@ -2913,7 +3413,7 @@ void QRhiVulkan::depthStencilExplicitBarrier(QVkCommandBuffer *cbD, QVkRenderBuf
cmd.args.imageBarrier.srcStageMask = stages;
cmd.args.imageBarrier.dstStageMask = stages;
cmd.args.imageBarrier.count = 1;
- cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count();
+ cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
cbD->pools.imageBarrier.append(barrier);
}
@@ -2925,8 +3425,7 @@ void QRhiVulkan::subresourceBarrier(QVkCommandBuffer *cbD, VkImage image,
int startLevel, int levelCount)
{
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
- VkImageMemoryBarrier barrier;
- memset(&barrier, 0, sizeof(barrier));
+ VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = uint32_t(startLevel);
@@ -2944,7 +3443,7 @@ void QRhiVulkan::subresourceBarrier(QVkCommandBuffer *cbD, VkImage image,
cmd.args.imageBarrier.srcStageMask = srcStage;
cmd.args.imageBarrier.dstStageMask = dstStage;
cmd.args.imageBarrier.count = 1;
- cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count();
+ cmd.args.imageBarrier.index = cbD->pools.imageBarrier.size();
cbD->pools.imageBarrier.append(barrier);
}
@@ -2967,9 +3466,9 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
qsizetype imageSizeBytes = 0;
const void *src = nullptr;
const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ const bool is1D = texD->m_flags.testFlag(QRhiTexture::OneDimensional);
- VkBufferImageCopy copyInfo;
- memset(&copyInfo, 0, sizeof(copyInfo));
+ VkBufferImageCopy copyInfo = {};
copyInfo.bufferOffset = *curOfs;
copyInfo.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyInfo.imageSubresource.mipLevel = uint32_t(level);
@@ -2978,6 +3477,8 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
copyInfo.imageExtent.depth = 1;
if (is3D)
copyInfo.imageOffset.z = uint32_t(layer);
+ if (is1D)
+ copyInfo.imageOffset.y = uint32_t(layer);
const QByteArray rawData = subresDesc.data();
const QPoint dp = subresDesc.destinationTopLeft();
@@ -3064,6 +3565,12 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
}
}
+void QRhiVulkan::printExtraErrorInfo(VkResult err)
+{
+ if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY)
+ qWarning() << "Out of device memory, current allocator statistics are" << statistics();
+}
+
void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates)
{
QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
@@ -3084,16 +3591,14 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
if (!bufD->stagingBuffers[currentFrameSlot]) {
- VkBufferCreateInfo bufferInfo;
- memset(&bufferInfo, 0, sizeof(bufferInfo));
+ VkBufferCreateInfo 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 = VkDeviceSize(bufD->m_size);
+ bufferInfo.size = bufD->m_size;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
- VmaAllocationCreateInfo allocInfo;
- memset(&allocInfo, 0, sizeof(allocInfo));
+ VmaAllocationCreateInfo allocInfo = {};
allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
VmaAllocation allocation;
@@ -3102,7 +3607,8 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
if (err == VK_SUCCESS) {
bufD->stagingAllocations[currentFrameSlot] = allocation;
} else {
- qWarning("Failed to create staging buffer of size %d: %d", bufD->m_size, err);
+ qWarning("Failed to create staging buffer of size %u: %d", bufD->m_size, err);
+ printExtraErrorInfo(err);
continue;
}
}
@@ -3114,18 +3620,17 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
qWarning("Failed to map buffer: %d", err);
continue;
}
- memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
+ memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), u.data.size());
+ vmaFlushAllocation(toVmaAllocator(allocator), a, u.offset, u.data.size());
vmaUnmapMemory(toVmaAllocator(allocator), a);
- vmaFlushAllocation(toVmaAllocator(allocator), a, VkDeviceSize(u.offset), VkDeviceSize(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 = VkDeviceSize(u.offset);
- copyInfo.dstOffset = VkDeviceSize(u.offset);
- copyInfo.size = VkDeviceSize(u.data.size());
+ VkBufferCopy copyInfo = {};
+ copyInfo.srcOffset = u.offset;
+ copyInfo.dstOffset = u.offset;
+ copyInfo.size = u.data.size();
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::CopyBuffer;
@@ -3161,7 +3666,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
if (err == VK_SUCCESS) {
u.result->data.resize(u.readSize);
- memcpy(u.result->data.data(), reinterpret_cast<char *>(p) + u.offset, size_t(u.readSize));
+ memcpy(u.result->data.data(), reinterpret_cast<char *>(p) + u.offset, u.readSize);
vmaUnmapMemory(toVmaAllocator(allocator), a);
}
if (u.result->completed)
@@ -3178,14 +3683,12 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
readback.result = u.result;
readback.byteSize = u.readSize;
- VkBufferCreateInfo bufferInfo;
- memset(&bufferInfo, 0, sizeof(bufferInfo));
+ VkBufferCreateInfo bufferInfo = {};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
- bufferInfo.size = VkDeviceSize(readback.byteSize);
+ bufferInfo.size = readback.byteSize;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
- VmaAllocationCreateInfo allocInfo;
- memset(&allocInfo, 0, sizeof(allocInfo));
+ VmaAllocationCreateInfo allocInfo = {};
allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
VmaAllocation allocation;
@@ -3194,15 +3697,15 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
readback.stagingAlloc = allocation;
} else {
qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
+ printExtraErrorInfo(err);
continue;
}
trackedBufferBarrier(cbD, bufD, 0, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
- VkBufferCopy copyInfo;
- memset(&copyInfo, 0, sizeof(copyInfo));
- copyInfo.srcOffset = VkDeviceSize(u.offset);
- copyInfo.size = VkDeviceSize(u.readSize);
+ VkBufferCopy copyInfo = {};
+ copyInfo.srcOffset = u.offset;
+ copyInfo.size = u.readSize;
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::CopyBuffer;
@@ -3223,22 +3726,20 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
// batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos
VkDeviceSize stagingSize = 0;
- for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
+ for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
- for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
stagingSize += subresUploadByteSize(subresDesc);
}
}
Q_ASSERT(!utexD->stagingBuffers[currentFrameSlot]);
- VkBufferCreateInfo bufferInfo;
- memset(&bufferInfo, 0, sizeof(bufferInfo));
+ VkBufferCreateInfo 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));
+ VmaAllocationCreateInfo allocInfo = {};
allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
VmaAllocation allocation;
@@ -3246,6 +3747,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
&utexD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
if (err != VK_SUCCESS) {
qWarning("Failed to create image staging buffer of size %d: %d", int(stagingSize), err);
+ printExtraErrorInfo(err);
continue;
}
utexD->stagingAllocations[currentFrameSlot] = allocation;
@@ -3260,19 +3762,19 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
continue;
}
- for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
+ for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
const QList<QRhiTextureSubresourceUploadDescription> &srd(u.subresDesc[layer][level]);
if (srd.isEmpty())
continue;
- for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(srd)) {
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(srd)) {
prepareUploadSubres(utexD, layer, level,
subresDesc, &curOfs, mp, &copyInfos);
}
}
}
- vmaUnmapMemory(toVmaAllocator(allocator), a);
vmaFlushAllocation(toVmaAllocator(allocator), a, 0, stagingSize);
+ vmaUnmapMemory(toVmaAllocator(allocator), a);
trackedImageBarrier(cbD, utexD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
@@ -3282,9 +3784,9 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
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());
+ cmd.args.copyBufferToImage.count = copyInfos.size();
+ cmd.args.copyBufferToImage.bufferImageCopyIndex = cbD->pools.bufferImageCopy.size();
+ cbD->pools.bufferImageCopy.append(copyInfos.constData(), copyInfos.size());
// no reuse of staging, this is intentional
QRhiVulkan::DeferredReleaseEntry e;
@@ -3311,9 +3813,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
- VkImageCopy region;
- memset(&region, 0, sizeof(region));
-
+ VkImageCopy region = {};
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.mipLevel = uint32_t(u.desc.sourceLevel());
region.srcSubresource.baseArrayLayer = srcIs3D ? 0 : uint32_t(u.desc.sourceLayer());
@@ -3390,14 +3890,12 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
textureFormatInfo(readback.format, readback.pixelSize, nullptr, &readback.byteSize, nullptr);
// Create a host visible readback buffer.
- VkBufferCreateInfo bufferInfo;
- memset(&bufferInfo, 0, sizeof(bufferInfo));
+ VkBufferCreateInfo bufferInfo = {};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = readback.byteSize;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
- VmaAllocationCreateInfo allocInfo;
- memset(&allocInfo, 0, sizeof(allocInfo));
+ VmaAllocationCreateInfo allocInfo = {};
allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
VmaAllocation allocation;
@@ -3406,12 +3904,12 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
readback.stagingAlloc = allocation;
} else {
qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
+ printExtraErrorInfo(err);
continue;
}
// Copy from the (optimal and not host visible) image into the buffer.
- VkBufferImageCopy copyDesc;
- memset(&copyDesc, 0, sizeof(copyDesc));
+ VkBufferImageCopy copyDesc = {};
copyDesc.bufferOffset = 0;
copyDesc.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyDesc.imageSubresource.mipLevel = uint32_t(u.rb.level());
@@ -3472,10 +3970,10 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
if (!origStage)
origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
- for (int layer = 0; layer < (isCube ? 6 : (isArray ? utexD->m_arraySize : 1)); ++layer) {
+ for (int layer = 0; layer < (isCube ? 6 : (isArray ? qMax(0, utexD->m_arraySize) : 1)); ++layer) {
int w = utexD->m_pixelSize.width();
int h = utexD->m_pixelSize.height();
- int depth = is3D ? utexD->m_depth : 1;
+ int depth = is3D ? qMax(1, utexD->m_depth) : 1;
for (int level = 1; level < int(utexD->mipLevelCount); ++level) {
if (level == 1) {
subresourceBarrier(cbD, utexD->image,
@@ -3500,9 +3998,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
layer, 1,
level, 1);
- VkImageBlit region;
- memset(&region, 0, sizeof(region));
-
+ VkImageBlit region = {};
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.mipLevel = uint32_t(level) - 1;
region.srcSubresource.baseArrayLayer = uint32_t(layer);
@@ -3573,18 +4069,18 @@ void QRhiVulkan::executeBufferHostWritesForSlot(QVkBuffer *bufD, int slot)
qWarning("Failed to map buffer: %d", err);
return;
}
- int changeBegin = -1;
- int changeEnd = -1;
- for (const QVkBuffer::DynamicUpdate &u : qAsConst(bufD->pendingDynamicUpdates[slot])) {
- memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
- if (changeBegin == -1 || u.offset < changeBegin)
+ quint32 changeBegin = UINT32_MAX;
+ quint32 changeEnd = 0;
+ for (const QVkBuffer::DynamicUpdate &u : std::as_const(bufD->pendingDynamicUpdates[slot])) {
+ memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), u.data.size());
+ if (u.offset < changeBegin)
changeBegin = u.offset;
- if (changeEnd == -1 || u.offset + u.data.size() > changeEnd)
+ if (u.offset + u.data.size() > changeEnd)
changeEnd = u.offset + u.data.size();
}
+ if (changeBegin < UINT32_MAX && changeBegin < changeEnd)
+ vmaFlushAllocation(toVmaAllocator(allocator), a, changeBegin, changeEnd - changeBegin);
vmaUnmapMemory(toVmaAllocator(allocator), a);
- if (changeBegin >= 0)
- vmaFlushAllocation(toVmaAllocator(allocator), a, VkDeviceSize(changeBegin), VkDeviceSize(changeEnd - changeBegin));
bufD->pendingDynamicUpdates[slot].clear();
}
@@ -3623,7 +4119,7 @@ static void qrhivk_releaseSampler(const QRhiVulkan::DeferredReleaseEntry &e, VkD
void QRhiVulkan::executeDeferredReleases(bool forced)
{
- for (int i = releaseQueue.count() - 1; i >= 0; --i) {
+ for (int i = releaseQueue.size() - 1; i >= 0; --i) {
const QRhiVulkan::DeferredReleaseEntry &e(releaseQueue[i]);
if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
switch (e.type) {
@@ -3656,6 +4152,8 @@ void QRhiVulkan::executeDeferredReleases(bool forced)
df->vkDestroyImageView(dev, e.textureRenderTarget.rtv[att], nullptr);
df->vkDestroyImageView(dev, e.textureRenderTarget.resrtv[att], nullptr);
}
+ df->vkDestroyImageView(dev, e.textureRenderTarget.dsv, nullptr);
+ df->vkDestroyImageView(dev, e.textureRenderTarget.resdsv, nullptr);
break;
case QRhiVulkan::DeferredReleaseEntry::RenderPass:
df->vkDestroyRenderPass(dev, e.renderPass.rp, nullptr);
@@ -3679,7 +4177,7 @@ void QRhiVulkan::finishActiveReadbacks(bool forced)
{
QVarLengthArray<std::function<void()>, 4> completedCallbacks;
- for (int i = activeTextureReadbacks.count() - 1; i >= 0; --i) {
+ for (int i = activeTextureReadbacks.size() - 1; i >= 0; --i) {
const QRhiVulkan::TextureReadback &readback(activeTextureReadbacks[i]);
if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
readback.result->format = readback.format;
@@ -3704,7 +4202,7 @@ void QRhiVulkan::finishActiveReadbacks(bool forced)
}
}
- for (int i = activeBufferReadbacks.count() - 1; i >= 0; --i) {
+ for (int i = activeBufferReadbacks.size() - 1; i >= 0; --i) {
const QRhiVulkan::BufferReadback &readback(activeBufferReadbacks[i]);
if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
VmaAllocation a = toVmaAllocation(readback.stagingAlloc);
@@ -3712,7 +4210,7 @@ void QRhiVulkan::finishActiveReadbacks(bool forced)
VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
if (err == VK_SUCCESS && p) {
readback.result->data.resize(readback.byteSize);
- memcpy(readback.result->data.data(), p, size_t(readback.byteSize));
+ memcpy(readback.result->data.data(), p, readback.byteSize);
vmaUnmapMemory(toVmaAllocator(allocator), a);
} else {
qWarning("Failed to map buffer readback buffer of size %d: %d", readback.byteSize, err);
@@ -3765,33 +4263,26 @@ QList<int> QRhiVulkan::supportedSampleCounts() const
return result;
}
-VkSampleCountFlagBits QRhiVulkan::effectiveSampleCount(int sampleCount)
+VkSampleCountFlagBits QRhiVulkan::effectiveSampleCountBits(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;
- }
+ const int s = effectiveSampleCount(sampleCount);
for (const auto &qvk_sampleCount : qvk_sampleCounts) {
- if (qvk_sampleCount.count == sampleCount)
+ if (qvk_sampleCount.count == s)
return qvk_sampleCount.mask;
}
- Q_UNREACHABLE();
- return VK_SAMPLE_COUNT_1_BIT;
+ Q_UNREACHABLE_RETURN(VK_SAMPLE_COUNT_1_BIT);
}
void QRhiVulkan::enqueueTransitionPassResources(QVkCommandBuffer *cbD)
{
cbD->passResTrackers.append(QRhiPassResourceTracker());
- cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
+ cbD->currentPassResTrackerIndex = cbD->passResTrackers.size() - 1;
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::TransitionPassResources;
- cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.count() - 1;
+ cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.size() - 1;
}
void QRhiVulkan::recordPrimaryCommandBuffer(QVkCommandBuffer *cbD)
@@ -3894,17 +4385,23 @@ void QRhiVulkan::recordPrimaryCommandBuffer(QVkCommandBuffer *cbD)
cmd.args.drawIndexed.firstInstance);
break;
case QVkCommandBuffer::Command::DebugMarkerBegin:
- cmd.args.debugMarkerBegin.marker.pMarkerName =
- cbD->pools.debugMarkerData[cmd.args.debugMarkerBegin.markerNameIndex].constData();
- vkCmdDebugMarkerBegin(cbD->cb, &cmd.args.debugMarkerBegin.marker);
+#ifdef VK_EXT_debug_utils
+ cmd.args.debugMarkerBegin.label.pLabelName =
+ cbD->pools.debugMarkerData[cmd.args.debugMarkerBegin.labelNameIndex].constData();
+ vkCmdBeginDebugUtilsLabelEXT(cbD->cb, &cmd.args.debugMarkerBegin.label);
+#endif
break;
case QVkCommandBuffer::Command::DebugMarkerEnd:
- vkCmdDebugMarkerEnd(cbD->cb);
+#ifdef VK_EXT_debug_utils
+ vkCmdEndDebugUtilsLabelEXT(cbD->cb);
+#endif
break;
case QVkCommandBuffer::Command::DebugMarkerInsert:
- cmd.args.debugMarkerInsert.marker.pMarkerName =
- cbD->pools.debugMarkerData[cmd.args.debugMarkerInsert.markerNameIndex].constData();
- vkCmdDebugMarkerInsert(cbD->cb, &cmd.args.debugMarkerInsert.marker);
+#ifdef VK_EXT_debug_utils
+ cmd.args.debugMarkerInsert.label.pLabelName =
+ cbD->pools.debugMarkerData[cmd.args.debugMarkerInsert.labelNameIndex].constData();
+ vkCmdInsertDebugUtilsLabelEXT(cbD->cb, &cmd.args.debugMarkerInsert.label);
+#endif
break;
case QVkCommandBuffer::Command::TransitionPassResources:
recordTransitionPassResources(cbD, cbD->passResTrackers[cmd.args.transitionResources.trackerIndex]);
@@ -4105,8 +4602,7 @@ void QRhiVulkan::recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhi
if (!accessIsWrite(access))
continue;
}
- VkBufferMemoryBarrier bufMemBarrier;
- memset(&bufMemBarrier, 0, sizeof(bufMemBarrier));
+ VkBufferMemoryBarrier bufMemBarrier = {};
bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
@@ -4130,8 +4626,7 @@ void QRhiVulkan::recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhi
if (!accessIsWrite(access))
continue;
}
- VkImageMemoryBarrier barrier;
- memset(&barrier, 0, sizeof(barrier));
+ VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.subresourceRange.aspectMask = aspectMaskForTextureFormat(texD->m_format);
barrier.subresourceRange.baseMipLevel = 0;
@@ -4156,10 +4651,18 @@ void QRhiVulkan::recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhi
QRhiSwapChain *QRhiVulkan::createSwapChain()
{
+ if (!vkGetPhysicalDeviceSurfaceCapabilitiesKHR
+ || !vkGetPhysicalDeviceSurfaceFormatsKHR
+ || !vkGetPhysicalDeviceSurfacePresentModesKHR)
+ {
+ qWarning("Physical device surface queries not available");
+ return nullptr;
+ }
+
return new QVkSwapChain(this);
}
-QRhiBuffer *QRhiVulkan::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
+QRhiBuffer *QRhiVulkan::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
{
return new QVkBuffer(this, type, usage, size);
}
@@ -4233,7 +4736,7 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
case QRhi::MultisampleRenderBuffer:
return true;
case QRhi::DebugMarkers:
- return caps.debugMarkers;
+ return caps.debugUtils;
case QRhi::Timestamps:
return timestampValidBits != 0;
case QRhi::Instancing:
@@ -4298,9 +4801,24 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::NonFillPolygonMode:
return caps.nonFillPolygonMode;
+ case QRhi::OneDimensionalTextures:
+ return true;
+ case QRhi::OneDimensionalTextureMipmaps:
+ return true;
+ case QRhi::HalfAttributes:
+ return true;
+ case QRhi::RenderToOneDimensionalTexture:
+ return true;
+ case QRhi::ThreeDimensionalTextureMipmaps:
+ return true;
+ case QRhi::MultiView:
+ return caps.multiView;
+ case QRhi::TextureViewFormat:
+ return true;
+ case QRhi::ResolveDepthStencil:
+ return caps.renderPass2KHR && caps.depthStencilResolveKHR;
default:
- Q_UNREACHABLE();
- return false;
+ Q_UNREACHABLE_RETURN(false);
}
}
@@ -4338,8 +4856,7 @@ int QRhiVulkan::resourceLimit(QRhi::ResourceLimit limit) const
case QRhi::MaxVertexOutputs:
return physDevProperties.limits.maxVertexOutputComponents / 4;
default:
- Q_UNREACHABLE();
- return 0;
+ Q_UNREACHABLE_RETURN(0);
}
}
@@ -4353,16 +4870,24 @@ QRhiDriverInfo QRhiVulkan::driverInfo() const
return driverInfoStruct;
}
-QRhiMemAllocStats QRhiVulkan::graphicsMemoryAllocationStatistics()
+QRhiStats QRhiVulkan::statistics()
{
- VmaStats stats;
- vmaCalculateStats(toVmaAllocator(allocator), &stats);
- return {
- stats.total.blockCount,
- stats.total.allocationCount,
- stats.total.usedBytes,
- stats.total.unusedBytes
- };
+ QRhiStats result;
+ result.totalPipelineCreationTime = totalPipelineCreationTime();
+
+ VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
+ vmaGetHeapBudgets(toVmaAllocator(allocator), budgets);
+
+ uint32_t count = toVmaAllocator(allocator)->GetMemoryHeapCount();
+ for (uint32_t i = 0; i < count; ++i) {
+ const VmaStatistics &stats(budgets[i].statistics);
+ result.blockCount += stats.blockCount;
+ result.allocCount += stats.allocationCount;
+ result.usedBytes += stats.allocationBytes;
+ result.unusedBytes += stats.blockBytes - stats.allocationBytes;
+ }
+
+ return result;
}
bool QRhiVulkan::makeThreadLocalNativeContextCurrent()
@@ -4404,7 +4929,7 @@ QByteArray QRhiVulkan::pipelineCacheData()
size_t dataSize = 0;
VkResult err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, nullptr);
if (err != VK_SUCCESS) {
- qWarning("Failed to get pipeline cache data size: %d", err);
+ qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data size: %d", err);
return QByteArray();
}
const size_t headerSize = sizeof(QVkPipelineCacheDataHeader);
@@ -4412,7 +4937,7 @@ QByteArray QRhiVulkan::pipelineCacheData()
data.resize(dataOffset + dataSize);
err = df->vkGetPipelineCacheData(dev, pipelineCache, &dataSize, data.data() + dataOffset);
if (err != VK_SUCCESS) {
- qWarning("Failed to get pipeline cache data of %d bytes: %d", int(dataSize), err);
+ qCDebug(QRHI_LOG_INFO, "Failed to get pipeline cache data of %d bytes: %d", int(dataSize), err);
return QByteArray();
}
@@ -4424,6 +4949,7 @@ QByteArray QRhiVulkan::pipelineCacheData()
header.deviceId = physDevProperties.deviceID;
header.dataSize = quint32(dataSize);
header.uuidSize = VK_UUID_SIZE;
+ header.reserved = 0;
memcpy(data.data(), &header, headerSize);
memcpy(data.data() + headerSize, physDevProperties.pipelineCacheUUID, VK_UUID_SIZE);
@@ -4437,7 +4963,7 @@ void QRhiVulkan::setPipelineCacheData(const QByteArray &data)
const size_t headerSize = sizeof(QVkPipelineCacheDataHeader);
if (data.size() < qsizetype(headerSize)) {
- qWarning("setPipelineCacheData: Invalid blob size");
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size");
return;
}
QVkPipelineCacheDataHeader header;
@@ -4445,49 +4971,49 @@ void QRhiVulkan::setPipelineCacheData(const QByteArray &data)
const quint32 rhiId = pipelineCacheRhiId();
if (header.rhiId != rhiId) {
- qWarning("setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
- rhiId, header.rhiId);
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
+ rhiId, header.rhiId);
return;
}
const quint32 arch = quint32(sizeof(void*));
if (header.arch != arch) {
- qWarning("setPipelineCacheData: Architecture does not match (%u, %u)",
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
arch, header.arch);
return;
}
if (header.driverVersion != physDevProperties.driverVersion) {
- qWarning("setPipelineCacheData: driverVersion does not match (%u, %u)",
- physDevProperties.driverVersion, header.driverVersion);
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: driverVersion does not match (%u, %u)",
+ physDevProperties.driverVersion, header.driverVersion);
return;
}
if (header.vendorId != physDevProperties.vendorID) {
- qWarning("setPipelineCacheData: vendorID does not match (%u, %u)",
- physDevProperties.vendorID, header.vendorId);
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: vendorID does not match (%u, %u)",
+ physDevProperties.vendorID, header.vendorId);
return;
}
if (header.deviceId != physDevProperties.deviceID) {
- qWarning("setPipelineCacheData: deviceID does not match (%u, %u)",
- physDevProperties.deviceID, header.deviceId);
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: deviceID does not match (%u, %u)",
+ physDevProperties.deviceID, header.deviceId);
return;
}
if (header.uuidSize != VK_UUID_SIZE) {
- qWarning("setPipelineCacheData: VK_UUID_SIZE does not match (%u, %u)",
- quint32(VK_UUID_SIZE), header.uuidSize);
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: VK_UUID_SIZE does not match (%u, %u)",
+ quint32(VK_UUID_SIZE), header.uuidSize);
return;
}
if (data.size() < qsizetype(headerSize + VK_UUID_SIZE)) {
- qWarning("setPipelineCacheData: Invalid blob, no uuid");
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, no uuid");
return;
}
if (memcmp(data.constData() + headerSize, physDevProperties.pipelineCacheUUID, VK_UUID_SIZE)) {
- qWarning("setPipelineCacheData: pipelineCacheUUID does not match");
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: pipelineCacheUUID does not match");
return;
}
const size_t dataOffset = headerSize + VK_UUID_SIZE;
if (data.size() < qsizetype(dataOffset + header.dataSize)) {
- qWarning("setPipelineCacheData: Invalid blob, data missing");
+ qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob, data missing");
return;
}
@@ -4500,7 +5026,7 @@ void QRhiVulkan::setPipelineCacheData(const QByteArray &data)
qCDebug(QRHI_LOG_INFO, "Created pipeline cache with initial data of %d bytes",
int(header.dataSize));
} else {
- qWarning("Failed to create pipeline cache with initial data specified");
+ qCDebug(QRHI_LOG_INFO, "Failed to create pipeline cache with initial data specified");
}
}
@@ -4595,8 +5121,8 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
// 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 QRhiShaderResourceBinding::Data *b = srbD->sortedBindings[i].data();
+ for (int i = 0, ie = srbD->sortedBindings.size(); i != ie; ++i) {
+ const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings[i]);
QVkShaderResourceBindings::BoundResourceData &bd(descSetBd[i]);
switch (b->type) {
case QRhiShaderResourceBinding::UniformBuffer:
@@ -4742,8 +5268,8 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
// 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 QRhiShaderResourceBinding::Data *b = binding.data();
+ for (const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
+ const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.hasDynamicOffset) {
uint32_t offset = 0;
for (int i = 0; i < dynamicOffsetCount; ++i) {
@@ -4763,8 +5289,8 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
gfxPsD ? gfxPsD->layout : compPsD->layout,
0, 1, &srbD->descSets[descSetIdx],
- uint32_t(dynOfs.count()),
- dynOfs.count() ? dynOfs.constData() : nullptr);
+ uint32_t(dynOfs.size()),
+ dynOfs.size() ? dynOfs.constData() : nullptr);
} else {
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::BindDescriptorSet;
@@ -4772,9 +5298,9 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
: 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());
+ cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.size();
+ cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.size();
+ cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.size());
}
if (gfxPsD) {
@@ -4833,16 +5359,16 @@ void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb,
if (cbD->passUsesSecondaryCb) {
df->vkCmdBindVertexBuffers(cbD->activeSecondaryCbStack.last(), uint32_t(startBinding),
- uint32_t(bufs.count()), bufs.constData(), ofs.constData());
+ uint32_t(bufs.size()), bufs.constData(), ofs.constData());
} else {
QVkCommandBuffer::Command &cmd(cbD->commands.get());
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());
+ cmd.args.bindVertexBuffer.count = bufs.size();
+ cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.size();
+ cbD->pools.vertexBuffer.append(bufs.constData(), bufs.size());
+ cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.size();
+ cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.size());
}
}
@@ -4891,7 +5417,7 @@ void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport
// 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))
+ if (!qrhi_toTopLeftRenderTargetRect<UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
return;
QVkCommandBuffer::Command &cmd(cbD->commands.get());
@@ -4910,9 +5436,12 @@ void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport
cmd.cmd = QVkCommandBuffer::Command::SetViewport;
}
- if (!QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
+ if (cbD->currentGraphicsPipeline
+ && !QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)
+ ->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
QVkCommandBuffer::Command &cmd(cbD->commands.get());
VkRect2D *s = &cmd.args.setScissor.scissor;
+ qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, viewport.viewport(), &x, &y, &w, &h);
s->offset.x = int32_t(x);
s->offset.y = int32_t(y);
s->extent.width = uint32_t(w);
@@ -4935,7 +5464,7 @@ void QRhiVulkan::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
// 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))
+ if (!qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
return;
QVkCommandBuffer::Command &cmd(cbD->commands.get());
@@ -5025,60 +5554,72 @@ void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
{
- if (!debugMarkers || !caps.debugMarkers)
+#ifdef VK_EXT_debug_utils
+ if (!debugMarkers || !caps.debugUtils)
return;
- VkDebugMarkerMarkerInfoEXT marker;
- memset(&marker, 0, sizeof(marker));
- marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
+ VkDebugUtilsLabelEXT label = {};
+ label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) {
- marker.pMarkerName = name.constData();
- vkCmdDebugMarkerBegin(cbD->activeSecondaryCbStack.last(), &marker);
+ label.pLabelName = name.constData();
+ vkCmdBeginDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label);
} else {
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin;
- cmd.args.debugMarkerBegin.marker = marker;
- cmd.args.debugMarkerBegin.markerNameIndex = cbD->pools.debugMarkerData.count();
+ cmd.args.debugMarkerBegin.label = label;
+ cmd.args.debugMarkerBegin.labelNameIndex = cbD->pools.debugMarkerData.size();
cbD->pools.debugMarkerData.append(name);
}
+#else
+ Q_UNUSED(cb);
+ Q_UNUSED(name);
+#endif
}
void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
{
- if (!debugMarkers || !caps.debugMarkers)
+#ifdef VK_EXT_debug_utils
+ if (!debugMarkers || !caps.debugUtils)
return;
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) {
- vkCmdDebugMarkerEnd(cbD->activeSecondaryCbStack.last());
+ vkCmdEndDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last());
} else {
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd;
}
+#else
+ Q_UNUSED(cb);
+#endif
}
void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
{
- if (!debugMarkers || !caps.debugMarkers)
+#ifdef VK_EXT_debug_utils
+ if (!debugMarkers || !caps.debugUtils)
return;
- VkDebugMarkerMarkerInfoEXT marker;
- memset(&marker, 0, sizeof(marker));
- marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
+ VkDebugUtilsLabelEXT label = {};
+ label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) {
- marker.pMarkerName = msg.constData();
- vkCmdDebugMarkerInsert(cbD->activeSecondaryCbStack.last(), &marker);
+ label.pLabelName = msg.constData();
+ vkCmdInsertDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label);
} else {
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert;
- cmd.args.debugMarkerInsert.marker = marker;
- cmd.args.debugMarkerInsert.markerNameIndex = cbD->pools.debugMarkerData.count();
+ cmd.args.debugMarkerInsert.label = label;
+ cmd.args.debugMarkerInsert.labelNameIndex = cbD->pools.debugMarkerData.size();
cbD->pools.debugMarkerData.append(msg);
}
+#else
+ Q_UNUSED(cb);
+ Q_UNUSED(msg);
+#endif
}
const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb)
@@ -5159,23 +5700,35 @@ void QRhiVulkan::endExternal(QRhiCommandBuffer *cb)
cbD->resetCachedState();
}
-void QRhiVulkan::setObjectName(uint64_t object, VkDebugReportObjectTypeEXT type, const QByteArray &name, int slot)
+double QRhiVulkan::lastCompletedGpuTime(QRhiCommandBuffer *cb)
{
- if (!debugMarkers || !caps.debugMarkers || name.isEmpty())
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ return cbD->lastGpuTime;
+}
+
+void QRhiVulkan::setObjectName(uint64_t object, VkObjectType type, const QByteArray &name, int slot)
+{
+#ifdef VK_EXT_debug_utils
+ if (!debugMarkers || !caps.debugUtils || name.isEmpty())
return;
- VkDebugMarkerObjectNameInfoEXT nameInfo;
- memset(&nameInfo, 0, sizeof(nameInfo));
- nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT;
+ VkDebugUtilsObjectNameInfoEXT nameInfo = {};
+ nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
nameInfo.objectType = type;
- nameInfo.object = object;
+ nameInfo.objectHandle = object;
QByteArray decoratedName = name;
if (slot >= 0) {
decoratedName += '/';
decoratedName += QByteArray::number(slot);
}
nameInfo.pObjectName = decoratedName.constData();
- vkDebugMarkerSetObjectName(dev, &nameInfo);
+ vkSetDebugUtilsObjectNameEXT(dev, &nameInfo);
+#else
+ Q_UNUSED(object);
+ Q_UNUSED(type);
+ Q_UNUSED(name);
+ Q_UNUSED(slot);
+#endif
}
static inline VkBufferUsageFlagBits toVkBufferUsage(QRhiBuffer::UsageFlags usage)
@@ -5200,8 +5753,7 @@ static inline VkFilter toVkFilter(QRhiSampler::Filter f)
case QRhiSampler::Linear:
return VK_FILTER_LINEAR;
default:
- Q_UNREACHABLE();
- return VK_FILTER_NEAREST;
+ Q_UNREACHABLE_RETURN(VK_FILTER_NEAREST);
}
}
@@ -5215,8 +5767,7 @@ static inline VkSamplerMipmapMode toVkMipmapMode(QRhiSampler::Filter f)
case QRhiSampler::Linear:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
default:
- Q_UNREACHABLE();
- return VK_SAMPLER_MIPMAP_MODE_NEAREST;
+ Q_UNREACHABLE_RETURN(VK_SAMPLER_MIPMAP_MODE_NEAREST);
}
}
@@ -5230,8 +5781,7 @@ static inline VkSamplerAddressMode toVkAddressMode(QRhiSampler::AddressMode m)
case QRhiSampler::Mirror:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
default:
- Q_UNREACHABLE();
- return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ Q_UNREACHABLE_RETURN(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);
}
}
@@ -5251,8 +5801,7 @@ static inline VkShaderStageFlagBits toVkShaderStage(QRhiShaderStage::Type type)
case QRhiShaderStage::Geometry:
return VK_SHADER_STAGE_GEOMETRY_BIT;
default:
- Q_UNREACHABLE();
- return VK_SHADER_STAGE_VERTEX_BIT;
+ Q_UNREACHABLE_RETURN(VK_SHADER_STAGE_VERTEX_BIT);
}
}
@@ -5289,9 +5838,32 @@ static inline VkFormat toVkAttributeFormat(QRhiVertexInputAttribute::Format form
return VK_FORMAT_R32G32_SINT;
case QRhiVertexInputAttribute::SInt:
return VK_FORMAT_R32_SINT;
+ case QRhiVertexInputAttribute::Half4:
+ return VK_FORMAT_R16G16B16A16_SFLOAT;
+ case QRhiVertexInputAttribute::Half3:
+ return VK_FORMAT_R16G16B16_SFLOAT;
+ case QRhiVertexInputAttribute::Half2:
+ return VK_FORMAT_R16G16_SFLOAT;
+ case QRhiVertexInputAttribute::Half:
+ return VK_FORMAT_R16_SFLOAT;
+ case QRhiVertexInputAttribute::UShort4:
+ return VK_FORMAT_R16G16B16A16_UINT;
+ case QRhiVertexInputAttribute::UShort3:
+ return VK_FORMAT_R16G16B16_UINT;
+ case QRhiVertexInputAttribute::UShort2:
+ return VK_FORMAT_R16G16_UINT;
+ case QRhiVertexInputAttribute::UShort:
+ return VK_FORMAT_R16_UINT;
+ case QRhiVertexInputAttribute::SShort4:
+ return VK_FORMAT_R16G16B16A16_SINT;
+ case QRhiVertexInputAttribute::SShort3:
+ return VK_FORMAT_R16G16B16_SINT;
+ case QRhiVertexInputAttribute::SShort2:
+ return VK_FORMAT_R16G16_SINT;
+ case QRhiVertexInputAttribute::SShort:
+ return VK_FORMAT_R16_SINT;
default:
- Q_UNREACHABLE();
- return VK_FORMAT_R32G32B32A32_SFLOAT;
+ Q_UNREACHABLE_RETURN(VK_FORMAT_R32G32B32A32_SFLOAT);
}
}
@@ -5313,8 +5885,7 @@ static inline VkPrimitiveTopology toVkTopology(QRhiGraphicsPipeline::Topology t)
case QRhiGraphicsPipeline::Patches:
return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
default:
- Q_UNREACHABLE();
- return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+ Q_UNREACHABLE_RETURN(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
}
}
@@ -5328,8 +5899,7 @@ static inline VkCullModeFlags toVkCullMode(QRhiGraphicsPipeline::CullMode c)
case QRhiGraphicsPipeline::Back:
return VK_CULL_MODE_BACK_BIT;
default:
- Q_UNREACHABLE();
- return VK_CULL_MODE_NONE;
+ Q_UNREACHABLE_RETURN(VK_CULL_MODE_NONE);
}
}
@@ -5341,8 +5911,7 @@ static inline VkFrontFace toVkFrontFace(QRhiGraphicsPipeline::FrontFace f)
case QRhiGraphicsPipeline::CW:
return VK_FRONT_FACE_CLOCKWISE;
default:
- Q_UNREACHABLE();
- return VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ Q_UNREACHABLE_RETURN(VK_FRONT_FACE_COUNTER_CLOCKWISE);
}
}
@@ -5402,8 +5971,7 @@ static inline VkBlendFactor toVkBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
default:
- Q_UNREACHABLE();
- return VK_BLEND_FACTOR_ZERO;
+ Q_UNREACHABLE_RETURN(VK_BLEND_FACTOR_ZERO);
}
}
@@ -5421,8 +5989,7 @@ static inline VkBlendOp toVkBlendOp(QRhiGraphicsPipeline::BlendOp op)
case QRhiGraphicsPipeline::Max:
return VK_BLEND_OP_MAX;
default:
- Q_UNREACHABLE();
- return VK_BLEND_OP_ADD;
+ Q_UNREACHABLE_RETURN(VK_BLEND_OP_ADD);
}
}
@@ -5446,8 +6013,7 @@ static inline VkCompareOp toVkCompareOp(QRhiGraphicsPipeline::CompareOp op)
case QRhiGraphicsPipeline::Always:
return VK_COMPARE_OP_ALWAYS;
default:
- Q_UNREACHABLE();
- return VK_COMPARE_OP_ALWAYS;
+ Q_UNREACHABLE_RETURN(VK_COMPARE_OP_ALWAYS);
}
}
@@ -5471,8 +6037,7 @@ static inline VkStencilOp toVkStencilOp(QRhiGraphicsPipeline::StencilOp op)
case QRhiGraphicsPipeline::DecrementAndWrap:
return VK_STENCIL_OP_DECREMENT_AND_WRAP;
default:
- Q_UNREACHABLE();
- return VK_STENCIL_OP_KEEP;
+ Q_UNREACHABLE_RETURN(VK_STENCIL_OP_KEEP);
}
}
@@ -5484,8 +6049,7 @@ static inline VkPolygonMode toVkPolygonMode(QRhiGraphicsPipeline::PolygonMode mo
case QRhiGraphicsPipeline::Line:
return VK_POLYGON_MODE_LINE;
default:
- Q_UNREACHABLE();
- return VK_POLYGON_MODE_FILL;
+ Q_UNREACHABLE_RETURN(VK_POLYGON_MODE_FILL);
}
}
@@ -5524,8 +6088,7 @@ static inline VkDescriptorType toVkDescriptorType(const QRhiShaderResourceBindin
return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
default:
- Q_UNREACHABLE();
- return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ Q_UNREACHABLE_RETURN(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
}
}
@@ -5567,12 +6130,11 @@ static inline VkCompareOp toVkTextureCompareOp(QRhiSampler::CompareOp op)
case QRhiSampler::Always:
return VK_COMPARE_OP_ALWAYS;
default:
- Q_UNREACHABLE();
- return VK_COMPARE_OP_NEVER;
+ Q_UNREACHABLE_RETURN(VK_COMPARE_OP_NEVER);
}
}
-QVkBuffer::QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
+QVkBuffer::QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
: QRhiBuffer(rhi, type, usage, size)
{
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
@@ -5628,16 +6190,14 @@ bool QVkBuffer::create()
return false;
}
- const int nonZeroSize = m_size <= 0 ? 256 : m_size;
+ const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
- VkBufferCreateInfo bufferInfo;
- memset(&bufferInfo, 0, sizeof(bufferInfo));
+ VkBufferCreateInfo bufferInfo = {};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
- bufferInfo.size = uint32_t(nonZeroSize);
+ bufferInfo.size = nonZeroSize;
bufferInfo.usage = toVkBufferUsage(m_usage);
- VmaAllocationCreateInfo allocInfo;
- memset(&allocInfo, 0, sizeof(allocInfo));
+ VmaAllocationCreateInfo allocInfo = {};
if (m_type == Dynamic) {
#ifndef Q_OS_DARWIN // not for MoltenVK
@@ -5665,13 +6225,14 @@ bool QVkBuffer::create()
if (err != VK_SUCCESS)
break;
allocations[i] = allocation;
- rhiD->setObjectName(uint64_t(buffers[i]), VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, m_objectName,
+ rhiD->setObjectName(uint64_t(buffers[i]), VK_OBJECT_TYPE_BUFFER, m_objectName,
m_type == Dynamic ? i : -1);
}
}
if (err != VK_SUCCESS) {
- qWarning("Failed to create buffer: %d", err);
+ qWarning("Failed to create buffer of size %u: %d", nonZeroSize, err);
+ rhiD->printExtraErrorInfo(err);
return false;
}
@@ -5723,8 +6284,8 @@ void QVkBuffer::endFullDynamicBufferUpdateForCurrentFrame()
QRHI_RES_RHI(QRhiVulkan);
const int slot = rhiD->currentFrameSlot;
VmaAllocation a = toVmaAllocation(allocations[slot]);
- vmaUnmapMemory(toVmaAllocator(rhiD->allocator), a);
vmaFlushAllocation(toVmaAllocator(rhiD->allocator), a, 0, m_size);
+ vmaUnmapMemory(toVmaAllocator(rhiD->allocator), a);
}
QVkRenderBuffer::QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
@@ -5779,7 +6340,7 @@ bool QVkRenderBuffer::create()
return false;
QRHI_RES_RHI(QRhiVulkan);
- samples = rhiD->effectiveSampleCount(m_sampleCount);
+ samples = rhiD->effectiveSampleCountBits(m_sampleCount);
switch (m_type) {
case QRhiRenderBuffer::Color:
@@ -5815,7 +6376,7 @@ bool QVkRenderBuffer::create()
{
return false;
}
- rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName);
+ rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName);
break;
default:
Q_UNREACHABLE();
@@ -5897,6 +6458,15 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize)
QRHI_RES_RHI(QRhiVulkan);
vkformat = toVkTextureFormat(m_format, m_flags);
+ if (m_writeViewFormat.format != UnknownFormat)
+ viewFormat = toVkTextureFormat(m_writeViewFormat.format, m_writeViewFormat.srgb ? sRGB : Flags());
+ else
+ viewFormat = vkformat;
+ if (m_readViewFormat.format != UnknownFormat)
+ viewFormatForSampling = toVkTextureFormat(m_readViewFormat.format, m_readViewFormat.srgb ? sRGB : Flags());
+ else
+ viewFormatForSampling = vkformat;
+
VkFormatProperties props;
rhiD->f->vkGetPhysicalDeviceFormatProperties(rhiD->physDev, vkformat, &props);
const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
@@ -5905,19 +6475,22 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize)
return false;
}
- const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool is1D = m_flags.testFlag(OneDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
+ : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
+
mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
const int maxLevels = QRhi::MAX_MIP_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);
+ samples = rhiD->effectiveSampleCountBits(m_sampleCount);
if (samples > VK_SAMPLE_COUNT_1_BIT) {
if (isCube) {
qWarning("Cubemap texture cannot be multisample");
@@ -5940,12 +6513,18 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both array and 3D");
return false;
}
- m_depth = qMax(1, m_depth);
+ if (isCube && is1D) {
+ qWarning("Texture cannot be both cube and 1D");
+ return false;
+ }
+ if (is1D && is3D) {
+ qWarning("Texture cannot be both 1D and 3D");
+ return false;
+ }
if (m_depth > 1 && !is3D) {
qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
return false;
}
- m_arraySize = qMax(0, m_arraySize);
if (m_arraySize > 0 && !isArray) {
qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
return false;
@@ -5973,15 +6552,17 @@ bool QVkTexture::finishCreate()
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool is1D = m_flags.testFlag(OneDimensional);
- VkImageViewCreateInfo viewInfo;
- memset(&viewInfo, 0, sizeof(viewInfo));
+ VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
- viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE
- : (is3D ? VK_IMAGE_VIEW_TYPE_3D
- : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D));
- viewInfo.format = vkformat;
+ viewInfo.viewType = isCube
+ ? VK_IMAGE_VIEW_TYPE_CUBE
+ : (is3D ? VK_IMAGE_VIEW_TYPE_3D
+ : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
+ : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
+ viewInfo.format = viewFormatForSampling;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
@@ -5992,7 +6573,7 @@ bool QVkTexture::finishCreate()
viewInfo.subresourceRange.baseArrayLayer = uint32_t(m_arrayRangeStart);
viewInfo.subresourceRange.layerCount = uint32_t(m_arrayRangeLength);
} else {
- viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? m_arraySize : 1);
+ viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
}
VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &imageView);
@@ -6019,9 +6600,9 @@ bool QVkTexture::create()
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool is1D = m_flags.testFlag(OneDimensional);
- VkImageCreateInfo imageInfo;
- memset(&imageInfo, 0, sizeof(imageInfo));
+ VkImageCreateInfo imageInfo = {};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.flags = 0;
if (isCube)
@@ -6042,13 +6623,13 @@ bool QVkTexture::create()
#endif
}
- imageInfo.imageType = is3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
+ imageInfo.imageType = is1D ? VK_IMAGE_TYPE_1D : is3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
imageInfo.format = vkformat;
imageInfo.extent.width = uint32_t(size.width());
imageInfo.extent.height = uint32_t(size.height());
- imageInfo.extent.depth = is3D ? m_depth : 1;
+ imageInfo.extent.depth = is3D ? qMax(1, m_depth) : 1;
imageInfo.mipLevels = mipLevelCount;
- imageInfo.arrayLayers = isCube ? 6 : (isArray ? m_arraySize : 1);
+ imageInfo.arrayLayers = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
imageInfo.samples = samples;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
@@ -6067,14 +6648,20 @@ bool QVkTexture::create()
if (m_flags.testFlag(QRhiTexture::UsedWithLoadStore))
imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
- VmaAllocationCreateInfo allocInfo;
- memset(&allocInfo, 0, sizeof(allocInfo));
+ VmaAllocationCreateInfo allocInfo = {};
allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
VmaAllocation allocation;
VkResult err = vmaCreateImage(toVmaAllocator(rhiD->allocator), &imageInfo, &allocInfo, &image, &allocation, nullptr);
if (err != VK_SUCCESS) {
- qWarning("Failed to create image: %d", err);
+ qWarning("Failed to create image (with VkImageCreateInfo %ux%u depth %u vkformat 0x%X mips %u layers %u vksamples 0x%X): %d",
+ imageInfo.extent.width, imageInfo.extent.height, imageInfo.extent.depth,
+ int(imageInfo.format),
+ imageInfo.mipLevels,
+ imageInfo.arrayLayers,
+ int(imageInfo.samples),
+ err);
+ rhiD->printExtraErrorInfo(err);
return false;
}
imageAlloc = allocation;
@@ -6082,7 +6669,7 @@ bool QVkTexture::create()
if (!finishCreate())
return false;
- rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName);
+ rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName);
owns = true;
rhiD->registerResource(this);
@@ -6121,7 +6708,7 @@ void QVkTexture::setNativeLayout(int layout)
usageState.layout = VkImageLayout(layout);
}
-VkImageView QVkTexture::imageViewForLevel(int level)
+VkImageView QVkTexture::perLevelImageViewForLoadStore(int level)
{
Q_ASSERT(level >= 0 && level < int(mipLevelCount));
if (perLevelImageViews[level] != VK_NULL_HANDLE)
@@ -6131,15 +6718,17 @@ VkImageView QVkTexture::imageViewForLevel(int level)
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool is1D = m_flags.testFlag(OneDimensional);
- VkImageViewCreateInfo viewInfo;
- memset(&viewInfo, 0, sizeof(viewInfo));
+ VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
- viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE
- : (is3D ? VK_IMAGE_VIEW_TYPE_3D
- : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D));
- viewInfo.format = vkformat;
+ viewInfo.viewType = isCube
+ ? VK_IMAGE_VIEW_TYPE_CUBE
+ : (is3D ? VK_IMAGE_VIEW_TYPE_3D
+ : (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
+ : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
+ viewInfo.format = viewFormat; // this is writeViewFormat, regardless of Load, Store, or LoadStore; intentional
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
@@ -6148,7 +6737,7 @@ VkImageView QVkTexture::imageViewForLevel(int level)
viewInfo.subresourceRange.baseMipLevel = uint32_t(level);
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
- viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? m_arraySize : 1);
+ viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? qMax(0, m_arraySize) : 1);
VkImageView v = VK_NULL_HANDLE;
QRHI_RES_RHI(QRhiVulkan);
@@ -6197,8 +6786,7 @@ bool QVkSampler::create()
if (sampler)
destroy();
- VkSamplerCreateInfo samplerInfo;
- memset(&samplerInfo, 0, sizeof(samplerInfo));
+ VkSamplerCreateInfo samplerInfo = {};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = toVkFilter(m_magFilter);
samplerInfo.minFilter = toVkFilter(m_minFilter);
@@ -6227,7 +6815,7 @@ bool QVkSampler::create()
QVkRenderPassDescriptor::QVkRenderPassDescriptor(QRhiImplementation *rhi)
: QRhiRenderPassDescriptor(rhi)
{
- serializedFormatData.reserve(32);
+ serializedFormatData.reserve(64);
}
QVkRenderPassDescriptor::~QVkRenderPassDescriptor()
@@ -6282,16 +6870,20 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other
const QVkRenderPassDescriptor *o = QRHI_RES(const QVkRenderPassDescriptor, other);
- if (attDescs.count() != o->attDescs.count())
+ if (attDescs.size() != o->attDescs.size())
return false;
- if (colorRefs.count() != o->colorRefs.count())
+ if (colorRefs.size() != o->colorRefs.size())
return false;
- if (resolveRefs.count() != o->resolveRefs.count())
+ if (resolveRefs.size() != o->resolveRefs.size())
return false;
if (hasDepthStencil != o->hasDepthStencil)
return false;
+ if (hasDepthStencilResolve != o->hasDepthStencilResolve)
+ return false;
+ if (multiViewCount != o->multiViewCount)
+ return false;
- for (int i = 0, ie = colorRefs.count(); i != ie; ++i) {
+ for (int i = 0, ie = colorRefs.size(); i != ie; ++i) {
const uint32_t attIdx = colorRefs[i].attachment;
if (attIdx != o->colorRefs[i].attachment)
return false;
@@ -6307,7 +6899,7 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other
return false;
}
- for (int i = 0, ie = resolveRefs.count(); i != ie; ++i) {
+ for (int i = 0, ie = resolveRefs.size(); i != ie; ++i) {
const uint32_t attIdx = resolveRefs[i].attachment;
if (attIdx != o->resolveRefs[i].attachment)
return false;
@@ -6315,6 +6907,14 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other
return false;
}
+ if (hasDepthStencilResolve) {
+ const uint32_t attIdx = dsResolveRef.attachment;
+ if (attIdx != o->dsResolveRef.attachment)
+ return false;
+ if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
+ return false;
+ }
+
// subpassDeps is not included
return true;
@@ -6325,10 +6925,12 @@ void QVkRenderPassDescriptor::updateSerializedFormat()
serializedFormatData.clear();
auto p = std::back_inserter(serializedFormatData);
- *p++ = attDescs.count();
- *p++ = colorRefs.count();
- *p++ = resolveRefs.count();
+ *p++ = attDescs.size();
+ *p++ = colorRefs.size();
+ *p++ = resolveRefs.size();
*p++ = hasDepthStencil;
+ *p++ = hasDepthStencilResolve;
+ *p++ = multiViewCount;
auto serializeAttachmentData = [this, &p](uint32_t attIdx) {
const bool used = attIdx != VK_ATTACHMENT_UNUSED;
@@ -6343,7 +6945,7 @@ void QVkRenderPassDescriptor::updateSerializedFormat()
*p++ = used ? a->finalLayout : 0;
};
- for (int i = 0, ie = colorRefs.count(); i != ie; ++i) {
+ for (int i = 0, ie = colorRefs.size(); i != ie; ++i) {
const uint32_t attIdx = colorRefs[i].attachment;
*p++ = attIdx;
serializeAttachmentData(attIdx);
@@ -6355,11 +6957,17 @@ void QVkRenderPassDescriptor::updateSerializedFormat()
serializeAttachmentData(attIdx);
}
- for (int i = 0, ie = resolveRefs.count(); i != ie; ++i) {
+ for (int i = 0, ie = resolveRefs.size(); i != ie; ++i) {
const uint32_t attIdx = resolveRefs[i].attachment;
*p++ = attIdx;
serializeAttachmentData(attIdx);
}
+
+ if (hasDepthStencilResolve) {
+ const uint32_t attIdx = dsResolveRef.attachment;
+ *p++ = attIdx;
+ serializeAttachmentData(attIdx);
+ }
}
QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
@@ -6372,13 +6980,22 @@ QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescri
rpD->resolveRefs = resolveRefs;
rpD->subpassDeps = subpassDeps;
rpD->hasDepthStencil = hasDepthStencil;
+ rpD->hasDepthStencilResolve = hasDepthStencilResolve;
+ rpD->multiViewCount = multiViewCount;
rpD->dsRef = dsRef;
+ rpD->dsResolveRef = dsResolveRef;
VkRenderPassCreateInfo rpInfo;
VkSubpassDescription subpassDesc;
fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
QRHI_RES_RHI(QRhiVulkan);
+ MultiViewRenderPassSetupHelper multiViewHelper;
+ if (!multiViewHelper.prepare(&rpInfo, multiViewCount, rhiD->caps.multiView)) {
+ delete rpD;
+ return nullptr;
+ }
+
VkResult err = rhiD->df->vkCreateRenderPass(rhiD->dev, &rpInfo, nullptr, &rpD->rp);
if (err != VK_SUCCESS) {
qWarning("Failed to create renderpass: %d", err);
@@ -6467,6 +7084,11 @@ void QVkTextureRenderTarget::destroy()
resrtv[att] = VK_NULL_HANDLE;
}
+ e.textureRenderTarget.dsv = dsv;
+ dsv = VK_NULL_HANDLE;
+ e.textureRenderTarget.resdsv = resdsv;
+ resdsv = VK_NULL_HANDLE;
+
QRHI_RES_RHI(QRhiVulkan);
if (rhiD) {
rhiD->releaseQueue.append(e);
@@ -6485,8 +7107,10 @@ QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescrip
m_desc.cendColorAttachments(),
m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents),
m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents),
+ m_desc.depthTexture() && !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture(),
m_desc.depthStencilBuffer(),
- m_desc.depthTexture()))
+ m_desc.depthTexture(),
+ m_desc.depthResolveTexture()))
{
delete rp;
return nullptr;
@@ -6503,13 +7127,13 @@ bool QVkTextureRenderTarget::create()
if (d.fb)
destroy();
- const bool hasColorAttachments = m_desc.cbeginColorAttachments() != m_desc.cendColorAttachments();
- Q_ASSERT(hasColorAttachments || m_desc.depthTexture());
+ Q_ASSERT(m_desc.colorAttachmentCount() > 0 || 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.multiViewCount = 0;
d.colorAttCount = 0;
int attIndex = 0;
@@ -6520,12 +7144,17 @@ bool QVkTextureRenderTarget::create()
Q_ASSERT(texD || rbD);
if (texD) {
Q_ASSERT(texD->flags().testFlag(QRhiTexture::RenderTarget));
- VkImageViewCreateInfo viewInfo;
- memset(&viewInfo, 0, sizeof(viewInfo));
+ const bool is1D = texD->flags().testFlag(QRhiTexture::OneDimensional);
+ const bool isMultiView = it->multiViewCount() >= 2;
+ if (isMultiView && d.multiViewCount == 0)
+ d.multiViewCount = it->multiViewCount();
+ VkImageViewCreateInfo 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.viewType = is1D ? VK_IMAGE_VIEW_TYPE_1D
+ : (isMultiView ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
+ : VK_IMAGE_VIEW_TYPE_2D);
+ viewInfo.format = texD->viewFormat;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
@@ -6534,7 +7163,7 @@ bool QVkTextureRenderTarget::create()
viewInfo.subresourceRange.baseMipLevel = uint32_t(it->level());
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = uint32_t(it->layer());
- viewInfo.subresourceRange.layerCount = 1;
+ viewInfo.subresourceRange.layerCount = uint32_t(isMultiView ? it->multiViewCount() : 1);
VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &rtv[attIndex]);
if (err != VK_SUCCESS) {
qWarning("Failed to create render target image view: %d", err);
@@ -6559,7 +7188,25 @@ bool QVkTextureRenderTarget::create()
if (hasDepthStencil) {
if (m_desc.depthTexture()) {
QVkTexture *depthTexD = QRHI_RES(QVkTexture, m_desc.depthTexture());
- views.append(depthTexD->imageView);
+ // need a dedicated view just because viewFormat may differ from vkformat
+ VkImageViewCreateInfo viewInfo = {};
+ viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewInfo.image = depthTexD->image;
+ viewInfo.viewType = d.multiViewCount > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.format = depthTexD->viewFormat;
+ 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_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ viewInfo.subresourceRange.levelCount = 1;
+ viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
+ VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &dsv);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create depth-stencil image view for rt: %d", err);
+ return false;
+ }
+ views.append(dsv);
if (d.colorAttCount == 0) {
d.pixelSize = depthTexD->pixelSize();
d.sampleCount = depthTexD->samples;
@@ -6579,18 +7226,19 @@ bool QVkTextureRenderTarget::create()
d.resolveAttCount = 0;
attIndex = 0;
+ Q_ASSERT(d.multiViewCount == 0 || d.multiViewCount >= 2);
for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
if (it->resolveTexture()) {
QVkTexture *resTexD = QRHI_RES(QVkTexture, it->resolveTexture());
Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget));
d.resolveAttCount += 1;
- VkImageViewCreateInfo viewInfo;
- memset(&viewInfo, 0, sizeof(viewInfo));
+ VkImageViewCreateInfo 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.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
+ : VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.format = resTexD->viewFormat;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
@@ -6599,7 +7247,7 @@ bool QVkTextureRenderTarget::create()
viewInfo.subresourceRange.baseMipLevel = uint32_t(it->resolveLevel());
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = uint32_t(it->resolveLayer());
- viewInfo.subresourceRange.layerCount = 1;
+ viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resrtv[attIndex]);
if (err != VK_SUCCESS) {
qWarning("Failed to create render target resolve image view: %d", err);
@@ -6609,17 +7257,46 @@ bool QVkTextureRenderTarget::create()
}
}
+ if (m_desc.depthResolveTexture()) {
+ QVkTexture *resTexD = QRHI_RES(QVkTexture, m_desc.depthResolveTexture());
+ Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget));
+
+ VkImageViewCreateInfo viewInfo = {};
+ viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewInfo.image = resTexD->image;
+ viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
+ : VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.format = resTexD->viewFormat;
+ 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_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ viewInfo.subresourceRange.baseMipLevel = 0;
+ viewInfo.subresourceRange.levelCount = 1;
+ viewInfo.subresourceRange.baseArrayLayer = 0;
+ viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
+ VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resdsv);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create render target depth resolve image view: %d", err);
+ return false;
+ }
+ views.append(resdsv);
+ d.dsResolveAttCount = 1;
+ } else {
+ d.dsResolveAttCount = 0;
+ }
+
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));
+ VkFramebufferCreateInfo fbInfo = {};
fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbInfo.renderPass = d.rp->rp;
- fbInfo.attachmentCount = uint32_t(d.colorAttCount + d.dsAttCount + d.resolveAttCount);
+ fbInfo.attachmentCount = uint32_t(d.colorAttCount + d.dsAttCount + d.resolveAttCount + d.dsResolveAttCount);
fbInfo.pAttachments = views.constData();
fbInfo.width = uint32_t(d.pixelSize.width());
fbInfo.height = uint32_t(d.pixelSize.height());
@@ -6708,16 +7385,12 @@ bool QVkShaderResourceBindings::create()
sortedBindings.clear();
std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
- std::sort(sortedBindings.begin(), sortedBindings.end(),
- [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
- {
- return a.data()->binding < b.data()->binding;
- });
+ std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
hasSlottedResource = false;
hasDynamicOffset = false;
- for (const QRhiShaderResourceBinding &binding : qAsConst(sortedBindings)) {
- const QRhiShaderResourceBinding::Data *b = binding.data();
+ for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) {
+ const QRhiShaderResourceBinding::Data *b = QRhiImplementation::shaderResourceBindingData(binding);
if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.buf) {
if (QRHI_RES(QVkBuffer, b->u.ubuf.buf)->type() == QRhiBuffer::Dynamic)
hasSlottedResource = true;
@@ -6727,10 +7400,9 @@ bool QVkShaderResourceBindings::create()
}
QVarLengthArray<VkDescriptorSetLayoutBinding, 4> vkbindings;
- for (const QRhiShaderResourceBinding &binding : qAsConst(sortedBindings)) {
- const QRhiShaderResourceBinding::Data *b = binding.data();
- VkDescriptorSetLayoutBinding vkbinding;
- memset(&vkbinding, 0, sizeof(vkbinding));
+ for (const QRhiShaderResourceBinding &binding : std::as_const(sortedBindings)) {
+ const QRhiShaderResourceBinding::Data *b = QRhiImplementation::shaderResourceBindingData(binding);
+ VkDescriptorSetLayoutBinding vkbinding = {};
vkbinding.binding = uint32_t(b->binding);
vkbinding.descriptorType = toVkDescriptorType(b);
if (b->type == QRhiShaderResourceBinding::SampledTexture || b->type == QRhiShaderResourceBinding::Texture)
@@ -6741,10 +7413,9 @@ bool QVkShaderResourceBindings::create()
vkbindings.append(vkbinding);
}
- VkDescriptorSetLayoutCreateInfo layoutInfo;
- memset(&layoutInfo, 0, sizeof(layoutInfo));
+ VkDescriptorSetLayoutCreateInfo layoutInfo = {};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
- layoutInfo.bindingCount = uint32_t(vkbindings.count());
+ layoutInfo.bindingCount = uint32_t(vkbindings.size());
layoutInfo.pBindings = vkbindings.constData();
VkResult err = rhiD->df->vkCreateDescriptorSetLayout(rhiD->dev, &layoutInfo, nullptr, &layout);
@@ -6753,8 +7424,7 @@ bool QVkShaderResourceBindings::create()
return false;
}
- VkDescriptorSetAllocateInfo allocInfo;
- memset(&allocInfo, 0, sizeof(allocInfo));
+ VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorSetCount = QVK_FRAMES_IN_FLIGHT;
VkDescriptorSetLayout layouts[QVK_FRAMES_IN_FLIGHT];
@@ -6765,7 +7435,7 @@ bool QVkShaderResourceBindings::create()
return false;
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
- boundResourceData[i].resize(sortedBindings.count());
+ boundResourceData[i].resize(sortedBindings.size());
for (BoundResourceData &bd : boundResourceData[i])
memset(&bd, 0, sizeof(BoundResourceData));
}
@@ -6780,13 +7450,8 @@ void QVkShaderResourceBindings::updateResources(UpdateFlags flags)
{
sortedBindings.clear();
std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
- if (!flags.testFlag(BindingsAreSorted)) {
- std::sort(sortedBindings.begin(), sortedBindings.end(),
- [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
- {
- return a.data()->binding < b.data()->binding;
- });
- }
+ if (!flags.testFlag(BindingsAreSorted))
+ std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
// Reset the state tracking table too - it can deal with assigning a
// different QRhiBuffer/Texture/Sampler for a binding point, but it cannot
@@ -6798,7 +7463,7 @@ void QVkShaderResourceBindings::updateResources(UpdateFlags flags)
// complicating the checks in setShaderResources(), reset the table here
// just like we do in create().
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
- Q_ASSERT(boundResourceData[i].count() == sortedBindings.count());
+ Q_ASSERT(boundResourceData[i].size() == sortedBindings.size());
for (BoundResourceData &bd : boundResourceData[i])
memset(&bd, 0, sizeof(BoundResourceData));
}
@@ -6844,14 +7509,14 @@ bool QVkGraphicsPipeline::create()
destroy();
QRHI_RES_RHI(QRhiVulkan);
+ rhiD->pipelineCreationStart();
if (!rhiD->sanityCheckGraphicsPipeline(this))
return false;
if (!rhiD->ensurePipelineCache())
return false;
- VkPipelineLayoutCreateInfo pipelineLayoutInfo;
- memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo));
+ VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 1;
QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, m_shaderResourceBindings);
@@ -6863,8 +7528,7 @@ bool QVkGraphicsPipeline::create()
return false;
}
- VkGraphicsPipelineCreateInfo pipelineInfo;
- memset(&pipelineInfo, 0, sizeof(pipelineInfo));
+ VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
QVarLengthArray<VkShaderModule, 4> shaders;
@@ -6879,8 +7543,7 @@ bool QVkGraphicsPipeline::create()
VkShaderModule shader = rhiD->createShader(spirv.shader());
if (shader) {
shaders.append(shader);
- VkPipelineShaderStageCreateInfo shaderInfo;
- memset(&shaderInfo, 0, sizeof(shaderInfo));
+ VkPipelineShaderStageCreateInfo shaderInfo = {};
shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderInfo.stage = toVkShaderStage(shaderStage.type());
shaderInfo.module = shader;
@@ -6888,11 +7551,13 @@ bool QVkGraphicsPipeline::create()
shaderStageCreateInfos.append(shaderInfo);
}
}
- pipelineInfo.stageCount = uint32_t(shaderStageCreateInfos.count());
+ pipelineInfo.stageCount = uint32_t(shaderStageCreateInfos.size());
pipelineInfo.pStages = shaderStageCreateInfos.constData();
QVarLengthArray<VkVertexInputBindingDescription, 4> vertexBindings;
+#ifdef VK_EXT_vertex_attribute_divisor
QVarLengthArray<VkVertexInputBindingDivisorDescriptionEXT> nonOneStepRates;
+#endif
int bindingIndex = 0;
for (auto it = m_vertexInputLayout.cbeginBindings(), itEnd = m_vertexInputLayout.cendBindings();
it != itEnd; ++it, ++bindingIndex)
@@ -6904,9 +7569,12 @@ bool QVkGraphicsPipeline::create()
? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE
};
if (it->classification() == QRhiVertexInputBinding::PerInstance && it->instanceStepRate() != 1) {
+#ifdef VK_EXT_vertex_attribute_divisor
if (rhiD->caps.vertexAttribDivisor) {
- nonOneStepRates.append({ uint32_t(bindingIndex), uint32_t(it->instanceStepRate()) });
- } else {
+ nonOneStepRates.append({ uint32_t(bindingIndex), it->instanceStepRate() });
+ } else
+#endif
+ {
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");
@@ -6926,21 +7594,21 @@ bool QVkGraphicsPipeline::create()
};
vertexAttributes.append(attributeInfo);
}
- VkPipelineVertexInputStateCreateInfo vertexInputInfo;
- memset(&vertexInputInfo, 0, sizeof(vertexInputInfo));
+ VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
- vertexInputInfo.vertexBindingDescriptionCount = uint32_t(vertexBindings.count());
+ vertexInputInfo.vertexBindingDescriptionCount = uint32_t(vertexBindings.size());
vertexInputInfo.pVertexBindingDescriptions = vertexBindings.constData();
- vertexInputInfo.vertexAttributeDescriptionCount = uint32_t(vertexAttributes.count());
+ vertexInputInfo.vertexAttributeDescriptionCount = uint32_t(vertexAttributes.size());
vertexInputInfo.pVertexAttributeDescriptions = vertexAttributes.constData();
- VkPipelineVertexInputDivisorStateCreateInfoEXT divisorInfo;
+#ifdef VK_EXT_vertex_attribute_divisor
+ 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 = uint32_t(nonOneStepRates.count());
+ divisorInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT;
+ divisorInfo.vertexBindingDivisorCount = uint32_t(nonOneStepRates.size());
divisorInfo.pVertexBindingDivisors = nonOneStepRates.constData();
vertexInputInfo.pNext = &divisorInfo;
}
+#endif
pipelineInfo.pVertexInputState = &vertexInputInfo;
QVarLengthArray<VkDynamicState, 8> dynEnable;
@@ -6951,32 +7619,28 @@ bool QVkGraphicsPipeline::create()
if (m_flags.testFlag(QRhiGraphicsPipeline::UsesStencilRef))
dynEnable << VK_DYNAMIC_STATE_STENCIL_REFERENCE;
- VkPipelineDynamicStateCreateInfo dynamicInfo;
- memset(&dynamicInfo, 0, sizeof(dynamicInfo));
+ VkPipelineDynamicStateCreateInfo dynamicInfo = {};
dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
- dynamicInfo.dynamicStateCount = uint32_t(dynEnable.count());
+ dynamicInfo.dynamicStateCount = uint32_t(dynEnable.size());
dynamicInfo.pDynamicStates = dynEnable.constData();
pipelineInfo.pDynamicState = &dynamicInfo;
- VkPipelineViewportStateCreateInfo viewportInfo;
- memset(&viewportInfo, 0, sizeof(viewportInfo));
+ VkPipelineViewportStateCreateInfo 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));
+ VkPipelineInputAssemblyStateCreateInfo 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;
- VkPipelineTessellationStateCreateInfo tessInfo;
+ VkPipelineTessellationStateCreateInfo tessInfo = {};
#ifdef VK_VERSION_1_1
- VkPipelineTessellationDomainOriginStateCreateInfo originInfo;
+ VkPipelineTessellationDomainOriginStateCreateInfo originInfo = {};
#endif
if (m_topology == Patches) {
- memset(&tessInfo, 0, sizeof(tessInfo));
tessInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
tessInfo.patchControlPoints = uint32_t(qMax(1, m_patchControlPointCount));
@@ -6986,8 +7650,7 @@ bool QVkGraphicsPipeline::create()
// still have it working with both APIs. This requires Vulkan 1.1 (or
// VK_KHR_maintenance2 but don't bother with that).
#ifdef VK_VERSION_1_1
- if (rhiD->caps.vulkan11OrHigher) {
- memset(&originInfo, 0, sizeof(originInfo));
+ if (rhiD->caps.apiVersion >= QVersionNumber(1, 1)) {
originInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO;
originInfo.domainOrigin = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT;
tessInfo.pNext = &originInfo;
@@ -7001,8 +7664,7 @@ bool QVkGraphicsPipeline::create()
pipelineInfo.pTessellationState = &tessInfo;
}
- VkPipelineRasterizationStateCreateInfo rastInfo;
- memset(&rastInfo, 0, sizeof(rastInfo));
+ VkPipelineRasterizationStateCreateInfo rastInfo = {};
rastInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rastInfo.cullMode = toVkCullMode(m_cullMode);
rastInfo.frontFace = toVkFrontFace(m_frontFace);
@@ -7015,14 +7677,12 @@ bool QVkGraphicsPipeline::create()
rastInfo.polygonMode = toVkPolygonMode(m_polygonMode);
pipelineInfo.pRasterizationState = &rastInfo;
- VkPipelineMultisampleStateCreateInfo msInfo;
- memset(&msInfo, 0, sizeof(msInfo));
+ VkPipelineMultisampleStateCreateInfo msInfo = {};
msInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
- msInfo.rasterizationSamples = rhiD->effectiveSampleCount(m_sampleCount);
+ msInfo.rasterizationSamples = rhiD->effectiveSampleCountBits(m_sampleCount);
pipelineInfo.pMultisampleState = &msInfo;
- VkPipelineDepthStencilStateCreateInfo dsInfo;
- memset(&dsInfo, 0, sizeof(dsInfo));
+ VkPipelineDepthStencilStateCreateInfo dsInfo = {};
dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
dsInfo.depthTestEnable = m_depthTest;
dsInfo.depthWriteEnable = m_depthWrite;
@@ -7038,13 +7698,11 @@ bool QVkGraphicsPipeline::create()
}
pipelineInfo.pDepthStencilState = &dsInfo;
- VkPipelineColorBlendStateCreateInfo blendInfo;
- memset(&blendInfo, 0, sizeof(blendInfo));
+ VkPipelineColorBlendStateCreateInfo 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));
+ for (const QRhiGraphicsPipeline::TargetBlend &b : std::as_const(m_targetBlends)) {
+ VkPipelineColorBlendAttachmentState blend = {};
blend.blendEnable = b.enable;
blend.srcColorBlendFactor = toVkBlendFactor(b.srcColor);
blend.dstColorBlendFactor = toVkBlendFactor(b.dstColor);
@@ -7056,13 +7714,12 @@ bool QVkGraphicsPipeline::create()
vktargetBlends.append(blend);
}
if (vktargetBlends.isEmpty()) {
- VkPipelineColorBlendAttachmentState blend;
- memset(&blend, 0, sizeof(blend));
+ VkPipelineColorBlendAttachmentState 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 = uint32_t(vktargetBlends.count());
+ blendInfo.attachmentCount = uint32_t(vktargetBlends.size());
blendInfo.pAttachments = vktargetBlends.constData();
pipelineInfo.pColorBlendState = &blendInfo;
@@ -7081,6 +7738,7 @@ bool QVkGraphicsPipeline::create()
return false;
}
+ rhiD->pipelineCreationEnd();
lastActiveFrameSlot = -1;
generation += 1;
rhiD->registerResource(this);
@@ -7125,11 +7783,11 @@ bool QVkComputePipeline::create()
destroy();
QRHI_RES_RHI(QRhiVulkan);
+ rhiD->pipelineCreationStart();
if (!rhiD->ensurePipelineCache())
return false;
- VkPipelineLayoutCreateInfo pipelineLayoutInfo;
- memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo));
+ VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 1;
QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, m_shaderResourceBindings);
@@ -7141,8 +7799,7 @@ bool QVkComputePipeline::create()
return false;
}
- VkComputePipelineCreateInfo pipelineInfo;
- memset(&pipelineInfo, 0, sizeof(pipelineInfo));
+ VkComputePipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
pipelineInfo.layout = layout;
@@ -7161,8 +7818,7 @@ bool QVkComputePipeline::create()
return false;
}
VkShaderModule shader = rhiD->createShader(spirv.shader());
- VkPipelineShaderStageCreateInfo shaderInfo;
- memset(&shaderInfo, 0, sizeof(shaderInfo));
+ VkPipelineShaderStageCreateInfo shaderInfo = {};
shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
shaderInfo.module = shader;
@@ -7176,6 +7832,7 @@ bool QVkComputePipeline::create()
return false;
}
+ rhiD->pipelineCreationEnd();
lastActiveFrameSlot = -1;
generation += 1;
rhiD->registerResource(this);
@@ -7221,6 +7878,7 @@ const QRhiNativeHandles *QVkCommandBuffer::nativeHandles()
QVkSwapChain::QVkSwapChain(QRhiImplementation *rhi)
: QRhiSwapChain(rhi),
rtWrapper(rhi, this),
+ rtWrapperRight(rhi, this),
cbWrapper(rhi)
{
}
@@ -7263,6 +7921,11 @@ QRhiRenderTarget *QVkSwapChain::currentFrameRenderTarget()
return &rtWrapper;
}
+QRhiRenderTarget *QVkSwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer)
+{
+ return !stereo || targetBuffer == StereoTargetBuffer::LeftBuffer ? &rtWrapper : &rtWrapperRight;
+}
+
QSize QVkSwapChain::surfacePixelSize()
{
if (!ensureSurface())
@@ -7270,8 +7933,7 @@ QSize QVkSwapChain::surfacePixelSize()
// 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));
+ VkSurfaceCapabilitiesKHR surfaceCaps = {};
QRHI_RES_RHI(QRhiVulkan);
rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(rhiD->physDev, surface, &surfaceCaps);
VkExtent2D bufferSize = surfaceCaps.currentExtent;
@@ -7291,6 +7953,9 @@ static inline bool hdrFormatMatchesVkSurfaceFormat(QRhiSwapChain::Format f, cons
case QRhiSwapChain::HDR10:
return (s.format == VK_FORMAT_A2B10G10R10_UNORM_PACK32 || s.format == VK_FORMAT_A2R10G10B10_UNORM_PACK32)
&& s.colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT;
+ case QRhiSwapChain::HDRExtendedDisplayP3Linear:
+ return s.format == VK_FORMAT_R16G16B16A16_SFLOAT
+ && s.colorSpace == VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT;
default:
break;
}
@@ -7384,11 +8049,9 @@ bool QVkSwapChain::ensureSurface()
surface = surf;
QRHI_RES_RHI(QRhiVulkan);
- if (rhiD->gfxQueueFamilyIdx != -1) {
- if (!rhiD->inst->supportsPresent(rhiD->physDev, uint32_t(rhiD->gfxQueueFamilyIdx), m_window)) {
- qWarning("Presenting not supported on this window");
- return false;
- }
+ if (!rhiD->inst->supportsPresent(rhiD->physDev, rhiD->gfxQueueFamilyIdx, m_window)) {
+ qWarning("Presenting not supported on this window");
+ return false;
}
quint32 formatCount = 0;
@@ -7402,11 +8065,9 @@ bool QVkSwapChain::ensureSurface()
const bool srgbRequested = m_flags.testFlag(sRGB);
for (int i = 0; i < int(formatCount); ++i) {
if (formats[i].format != VK_FORMAT_UNDEFINED) {
- bool ok = false;
- if (m_format == SDR)
- ok = srgbRequested == isSrgbFormat(formats[i].format);
- else
- ok = hdrFormatMatchesVkSurfaceFormat(m_format, formats[i]);
+ bool ok = srgbRequested == isSrgbFormat(formats[i].format);
+ if (m_format != SDR)
+ ok &= hdrFormatMatchesVkSurfaceFormat(m_format, formats[i]);
if (ok) {
colorFormat = formats[i].format;
colorSpace = formats[i].colorSpace;
@@ -7415,7 +8076,7 @@ bool QVkSwapChain::ensureSurface()
}
}
- samples = rhiD->effectiveSampleCount(m_sampleCount);
+ samples = rhiD->effectiveSampleCountBits(m_sampleCount);
quint32 presModeCount = 0;
rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount, nullptr);
@@ -7486,6 +8147,7 @@ bool QVkSwapChain::createOrResize()
rtWrapper.d.dsAttCount = 0;
ds = nullptr;
}
+ rtWrapper.d.dsResolveAttCount = 0;
if (samples > VK_SAMPLE_COUNT_1_BIT)
rtWrapper.d.resolveAttCount = 1;
else
@@ -7499,11 +8161,10 @@ bool QVkSwapChain::createOrResize()
samples > VK_SAMPLE_COUNT_1_BIT ? image.imageView : VK_NULL_HANDLE
};
- VkFramebufferCreateInfo fbInfo;
- memset(&fbInfo, 0, sizeof(fbInfo));
+ VkFramebufferCreateInfo fbInfo = {};
fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbInfo.renderPass = rtWrapper.d.rp->rp;
- fbInfo.attachmentCount = uint32_t(rtWrapper.d.colorAttCount + rtWrapper.d.dsAttCount + rtWrapper.d.resolveAttCount);
+ fbInfo.attachmentCount = uint32_t(rtWrapper.d.colorAttCount + rtWrapper.d.dsAttCount + rtWrapper.d.resolveAttCount + rtWrapper.d.dsResolveAttCount);
fbInfo.pAttachments = views;
fbInfo.width = uint32_t(pixelSize.width());
fbInfo.height = uint32_t(pixelSize.height());
@@ -7516,6 +8177,56 @@ bool QVkSwapChain::createOrResize()
}
}
+ if (stereo) {
+ rtWrapperRight.setRenderPassDescriptor(
+ m_renderPassDesc); // for the public getter in QRhiRenderTarget
+ rtWrapperRight.d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc);
+ Q_ASSERT(rtWrapperRight.d.rp && rtWrapperRight.d.rp->rp);
+
+ rtWrapperRight.d.pixelSize = pixelSize;
+ rtWrapperRight.d.dpr = float(window->devicePixelRatio());
+ rtWrapperRight.d.sampleCount = samples;
+ rtWrapperRight.d.colorAttCount = 1;
+ if (m_depthStencil) {
+ rtWrapperRight.d.dsAttCount = 1;
+ ds = QRHI_RES(QVkRenderBuffer, m_depthStencil);
+ } else {
+ rtWrapperRight.d.dsAttCount = 0;
+ ds = nullptr;
+ }
+ rtWrapperRight.d.dsResolveAttCount = 0;
+ if (samples > VK_SAMPLE_COUNT_1_BIT)
+ rtWrapperRight.d.resolveAttCount = 1;
+ else
+ rtWrapperRight.d.resolveAttCount = 0;
+
+ for (int i = 0; i < bufferCount; ++i) {
+ QVkSwapChain::ImageResources &image(imageRes[i + bufferCount]);
+ 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 = {};
+ fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ fbInfo.renderPass = rtWrapperRight.d.rp->rp;
+ fbInfo.attachmentCount = uint32_t(rtWrapperRight.d.colorAttCount + rtWrapperRight.d.dsAttCount
+ + rtWrapperRight.d.resolveAttCount + rtWrapperRight.d.dsResolveAttCount);
+ fbInfo.pAttachments = views;
+ fbInfo.width = uint32_t(pixelSize.width());
+ fbInfo.height = uint32_t(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;
if (needsRegistration)
diff --git a/src/gui/rhi/qrhivulkan_p.h b/src/gui/rhi/qrhivulkan_p.h
index d2c928ccb8..ad8687de5d 100644
--- a/src/gui/rhi/qrhivulkan_p.h
+++ b/src/gui/rhi/qrhivulkan_p.h
@@ -1,8 +1,8 @@
-// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QRHIVULKAN_H
-#define QRHIVULKAN_H
+#ifndef QRHIVULKAN_P_H
+#define QRHIVULKAN_P_H
//
// W A R N I N G
@@ -15,44 +15,1025 @@
// We mean it.
//
-#include <private/qrhi_p.h>
-#include <QtGui/qvulkaninstance.h> // this is where vulkan.h gets pulled in
+#include "qrhi_p.h"
QT_BEGIN_NAMESPACE
-struct Q_GUI_EXPORT QRhiVulkanInitParams : public QRhiInitParams
+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
{
- QVulkanInstance *inst = nullptr;
- QWindow *window = nullptr;
- QByteArrayList deviceExtensions;
+ QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size);
+ ~QVkBuffer();
+ void destroy() override;
+ bool create() override;
+ QRhiBuffer::NativeBuffer nativeBuffer() override;
+ char *beginFullDynamicBufferUpdateForCurrentFrame() override;
+ void endFullDynamicBufferUpdateForCurrentFrame() override;
- static QByteArrayList preferredInstanceExtensions();
- static QByteArrayList preferredExtensionsForImportedDevice();
+ VkBuffer buffers[QVK_FRAMES_IN_FLIGHT];
+ QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT];
+ struct DynamicUpdate {
+ quint32 offset;
+ QRhiBufferData data;
+ };
+ QVarLengthArray<DynamicUpdate, 16> 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 Q_GUI_EXPORT QRhiVulkanNativeHandles : public QRhiNativeHandles
+Q_DECLARE_TYPEINFO(QVkBuffer::DynamicUpdate, Q_RELOCATABLE_TYPE);
+
+struct QVkTexture;
+
+struct QVkRenderBuffer : public QRhiRenderBuffer
{
- // to import a physical device (always required)
- VkPhysicalDevice physDev = VK_NULL_HANDLE;
- // to import a device and queue
- VkDevice dev = VK_NULL_HANDLE;
- int gfxQueueFamilyIdx = -1;
- int gfxQueueIdx = 0;
- VkQueue gfxQueue = VK_NULL_HANDLE;
- // and optionally, the mem allocator
- void *vmemAllocator = nullptr;
+ QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, Flags flags,
+ QRhiTexture::Format backingFormatHint);
+ ~QVkRenderBuffer();
+ void destroy() override;
+ bool create() 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;
+ uint generation = 0;
+ friend class QRhiVulkan;
+};
+
+struct QVkTexture : public QRhiTexture
+{
+ QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
+ int arraySize, int sampleCount, Flags flags);
+ ~QVkTexture();
+ void destroy() override;
+ bool create() override;
+ bool createFrom(NativeTexture src) override;
+ NativeTexture nativeTexture() override;
+ void setNativeLayout(int layout) override;
+
+ bool prepareCreate(QSize *adjustedSize = nullptr);
+ bool finishCreate();
+ VkImageView perLevelImageViewForLoadStore(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_MIP_LEVELS];
+ bool owns = true;
+ 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;
+ VkFormat viewFormat;
+ VkFormat viewFormatForSampling;
+ 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, AddressMode w);
+ ~QVkSampler();
+ void destroy() override;
+ bool create() override;
+
+ VkSampler sampler = VK_NULL_HANDLE;
+ int lastActiveFrameSlot = -1;
+ uint generation = 0;
+ friend class QRhiVulkan;
+};
+
+struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
+{
+ QVkRenderPassDescriptor(QRhiImplementation *rhi);
+ ~QVkRenderPassDescriptor();
+ void destroy() override;
+ bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
+ QVector<quint32> serializedFormat() const override;
+ const QRhiNativeHandles *nativeHandles() override;
+
+ void updateSerializedFormat();
+
+ VkRenderPass rp = VK_NULL_HANDLE;
+ bool ownsRp = false;
+ QVarLengthArray<VkAttachmentDescription, 8> attDescs;
+ QVarLengthArray<VkAttachmentReference, 8> colorRefs;
+ QVarLengthArray<VkAttachmentReference, 8> resolveRefs;
+ QVarLengthArray<VkSubpassDependency, 2> subpassDeps;
+ bool hasDepthStencil = false;
+ bool hasDepthStencilResolve = false;
+ uint32_t multiViewCount = 0;
+ VkAttachmentReference dsRef;
+ VkAttachmentReference dsResolveRef;
+ QVector<quint32> serializedFormatData;
+ QRhiVulkanRenderPassNativeHandles nativeHandlesStruct;
+ 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;
+ int dsResolveAttCount = 0;
+ int multiViewCount = 0;
+ QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList;
+ static const int MAX_COLOR_ATTACHMENTS = 8;
+};
+
+struct QVkSwapChainRenderTarget : public QRhiSwapChainRenderTarget
+{
+ QVkSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain);
+ ~QVkSwapChainRenderTarget();
+ void destroy() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QVkRenderTargetData d;
};
-struct Q_GUI_EXPORT QRhiVulkanCommandBufferNativeHandles : public QRhiNativeHandles
+struct QVkTextureRenderTarget : public QRhiTextureRenderTarget
{
- VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
+ QVkTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
+ ~QVkTextureRenderTarget();
+ void destroy() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool create() override;
+
+ QVkRenderTargetData d;
+ VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
+ VkImageView dsv = VK_NULL_HANDLE;
+ VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
+ VkImageView resdsv = VK_NULL_HANDLE;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiVulkan;
};
-struct Q_GUI_EXPORT QRhiVulkanRenderPassNativeHandles : public QRhiNativeHandles
+struct QVkShaderResourceBindings : public QRhiShaderResourceBindings
{
- VkRenderPass renderPass = VK_NULL_HANDLE;
+ QVkShaderResourceBindings(QRhiImplementation *rhi);
+ ~QVkShaderResourceBindings();
+ void destroy() override;
+ bool create() override;
+ void updateResources(UpdateFlags flags) override;
+
+ QVarLengthArray<QRhiShaderResourceBinding, 8> sortedBindings;
+ bool hasSlottedResource = false;
+ bool hasDynamicOffset = false;
+ 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 {
+ int count;
+ struct {
+ quint64 texId;
+ uint texGeneration;
+ quint64 samplerId;
+ uint samplerGeneration;
+ } d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE];
+ };
+ struct BoundStorageImageData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundStorageBufferData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundResourceData {
+ union {
+ BoundUniformBufferData ubuf;
+ BoundSampledTextureData stex;
+ BoundStorageImageData simage;
+ BoundStorageBufferData sbuf;
+ };
+ };
+ QVarLengthArray<BoundResourceData, 8> boundResourceData[QVK_FRAMES_IN_FLIGHT];
+
+ friend class QRhiVulkan;
+};
+
+Q_DECLARE_TYPEINFO(QVkShaderResourceBindings::BoundResourceData, Q_RELOCATABLE_TYPE);
+
+struct QVkGraphicsPipeline : public QRhiGraphicsPipeline
+{
+ QVkGraphicsPipeline(QRhiImplementation *rhi);
+ ~QVkGraphicsPipeline();
+ void destroy() override;
+ bool create() 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 destroy() override;
+ bool create() 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 destroy() override;
+
+ const QRhiNativeHandles *nativeHandles();
+
+ VkCommandBuffer cb = VK_NULL_HANDLE; // primary
+ QRhiVulkanCommandBufferNativeHandles nativeHandlesStruct;
+
+ enum PassType {
+ NoPass,
+ RenderPass,
+ ComputePass
+ };
+
+ void resetState() {
+ recordingPass = NoPass;
+ passUsesSecondaryCb = false;
+ lastGpuTime = 0;
+ currentTarget = nullptr;
+ activeSecondaryCbStack.clear();
+ resetCommands();
+ 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));
+ inExternal = false;
+ }
+
+ PassType recordingPass;
+ bool passUsesSecondaryCb;
+ double lastGpuTime = 0;
+ 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];
+ QVarLengthArray<VkCommandBuffer, 4> activeSecondaryCbStack;
+ bool inExternal;
+
+ struct {
+ QHash<QRhiResource *, QPair<VkAccessFlags, bool> > writtenResources;
+ void reset() {
+ writtenResources.clear();
+ }
+ } computePassState;
+
+ 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,
+ ExecuteSecondary
+ };
+ 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;
+ int count;
+ int index;
+ } imageBarrier;
+ struct {
+ VkPipelineStageFlags srcStageMask;
+ VkPipelineStageFlags dstStageMask;
+ int count;
+ int index;
+ } bufferBarrier;
+ struct {
+ VkImage src;
+ VkImageLayout srcLayout;
+ VkImage dst;
+ VkImageLayout dstLayout;
+ VkFilter filter;
+ VkImageBlit desc;
+ } blitImage;
+ struct {
+ VkRenderPassBeginInfo desc;
+ int clearValueIndex;
+ bool useSecondaryCb;
+ } 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 {
+#ifdef VK_EXT_debug_utils
+ VkDebugUtilsLabelEXT label;
+ int labelNameIndex;
+#endif
+ } debugMarkerBegin;
+ struct {
+ } debugMarkerEnd;
+ struct {
+#ifdef VK_EXT_debug_utils
+ VkDebugUtilsLabelEXT label;
+ int labelNameIndex;
+#endif
+ } debugMarkerInsert;
+ struct {
+ int trackerIndex;
+ } transitionResources;
+ struct {
+ int x, y, z;
+ } dispatch;
+ struct {
+ VkCommandBuffer cb;
+ } executeSecondary;
+ } args;
+ };
+
+ QRhiBackendCommandList<Command> commands;
+ QVarLengthArray<QRhiPassResourceTracker, 8> passResTrackers;
+ int currentPassResTrackerIndex;
+
+ void resetCommands() {
+ commands.reset();
+ resetPools();
+
+ passResTrackers.clear();
+ currentPassResTrackerIndex = -1;
+ }
+
+ void resetPools() {
+ pools.clearValue.clear();
+ pools.bufferImageCopy.clear();
+ pools.dynamicOffset.clear();
+ pools.vertexBuffer.clear();
+ pools.vertexBufferOffset.clear();
+ pools.debugMarkerData.clear();
+ pools.imageBarrier.clear();
+ pools.bufferBarrier.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> debugMarkerData;
+ QVarLengthArray<VkImageMemoryBarrier, 8> imageBarrier;
+ QVarLengthArray<VkBufferMemoryBarrier, 8> bufferBarrier;
+ } pools;
+
+ friend class QRhiVulkan;
+};
+
+struct QVkSwapChain : public QRhiSwapChain
+{
+ QVkSwapChain(QRhiImplementation *rhi);
+ ~QVkSwapChain();
+ void destroy() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+ QRhiRenderTarget *currentFrameRenderTarget(StereoTargetBuffer targetBuffer) override;
+
+ QSize surfacePixelSize() override;
+ bool isFormatSupported(Format f) override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool createOrResize() override;
+
+ bool ensureSurface();
+
+ static const quint32 EXPECTED_MAX_BUFFER_COUNT = 4;
+
+ QWindow *window = nullptr;
+ QSize pixelSize;
+ bool supportsReadback = false;
+ bool stereo = 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;
+ QVarLengthArray<VkPresentModeKHR, 8> supportedPresentationModes;
+ VkDeviceMemory msaaImageMem = VK_NULL_HANDLE;
+ QVkSwapChainRenderTarget rtWrapper;
+ QVkSwapChainRenderTarget rtWrapperRight;
+ 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;
+ };
+ QVarLengthArray<ImageResources, EXPECTED_MAX_BUFFER_COUNT> imageRes;
+
+ 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;
+ VkFence cmdFence = VK_NULL_HANDLE;
+ bool cmdFenceWaitable = false;
+ VkCommandBuffer cmdBuf = VK_NULL_HANDLE; // primary
+ 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 *importParams = 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,
+ quint32 size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags,
+ QRhiTexture::Format backingFormatHint) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int depth,
+ int arraySize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
+ QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u,
+ QRhiSampler::AddressMode v,
+ QRhiSampler::AddressMode w) 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, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) 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,
+ QRhiCommandBuffer::BeginPassFlags flags) 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,
+ QRhiCommandBuffer::BeginPassFlags flags) 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;
+ double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
+
+ QList<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;
+ QRhiDriverInfo driverInfo() const override;
+ QRhiStats statistics() override;
+ bool makeThreadLocalNativeContextCurrent() override;
+ void releaseCachedResources() override;
+ bool isDeviceLost() const override;
+
+ QByteArray pipelineCacheData() override;
+ void setPipelineCacheData(const QByteArray &data) 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 effectiveSampleCountBits(int sampleCount);
+ bool createDefaultRenderPass(QVkRenderPassDescriptor *rpD,
+ bool hasDepthStencil,
+ VkSampleCountFlagBits samples,
+ VkFormat colorFormat);
+ bool createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
+ const QRhiColorAttachment *firstColorAttachment,
+ const QRhiColorAttachment *lastColorAttachment,
+ bool preserveColor,
+ bool preserveDs,
+ bool storeDs,
+ QRhiRenderBuffer *depthStencilBuffer,
+ QRhiTexture *depthTexture,
+ QRhiTexture *depthResolveTexture);
+ bool ensurePipelineCache(const void *initialData = nullptr, size_t initialDataSize = 0);
+ VkShaderModule createShader(const QByteArray &spirv);
+
+ void prepareNewFrame(QRhiCommandBuffer *cb);
+ VkCommandBuffer startSecondaryCommandBuffer(QVkRenderTargetData *rtD = nullptr);
+ void endAndEnqueueSecondaryCommandBuffer(VkCommandBuffer cb, QVkCommandBuffer *cbD);
+ QRhi::FrameOpResult startPrimaryCommandBuffer(VkCommandBuffer *cb);
+ QRhi::FrameOpResult endAndSubmitPrimaryCommandBuffer(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 executeBufferHostWritesForSlot(QVkBuffer *bufD, int slot);
+ void enqueueTransitionPassResources(QVkCommandBuffer *cbD);
+ void recordPrimaryCommandBuffer(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, VkObjectType 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 depthStencilExplicitBarrier(QVkCommandBuffer *cbD, QVkRenderBuffer *rbD);
+ 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);
+ void ensureCommandPoolForNewFrame();
+ double elapsedSecondsFromTimestamp(quint64 timestamp[2], bool *ok);
+ void printExtraErrorInfo(VkResult err);
+
+ QVulkanInstance *inst = nullptr;
+ QWindow *maybeWindow = nullptr;
+ QByteArrayList requestedDeviceExtensions;
+ bool importedDevice = false;
+ VkPhysicalDevice physDev = VK_NULL_HANDLE;
+ VkDevice dev = VK_NULL_HANDLE;
+ VkCommandPool cmdPool[QVK_FRAMES_IN_FLIGHT] = {};
+ quint32 gfxQueueFamilyIdx = 0;
+ quint32 gfxQueueIdx = 0;
+ VkQueue gfxQueue = VK_NULL_HANDLE;
+ quint32 timestampValidBits = 0;
+ bool importedAllocator = false;
+ QVkAllocator allocator = nullptr;
+ QVulkanFunctions *f = nullptr;
+ QVulkanDeviceFunctions *df = nullptr;
+ QRhi::Flags rhiFlags;
+ VkPhysicalDeviceFeatures physDevFeatures;
+#ifdef VK_VERSION_1_1
+ VkPhysicalDeviceMultiviewFeatures multiviewFeaturesIfApi11;
+#endif
+#ifdef VK_VERSION_1_2
+ VkPhysicalDeviceVulkan11Features physDevFeatures11IfApi12OrNewer;
+ VkPhysicalDeviceVulkan12Features physDevFeatures12;
+#endif
+#ifdef VK_VERSION_1_3
+ VkPhysicalDeviceVulkan13Features physDevFeatures13;
+#endif
+ VkPhysicalDeviceProperties physDevProperties;
+ VkDeviceSize ubufAlign;
+ VkDeviceSize texbufAlign;
+ bool deviceLost = false;
+ bool releaseCachedResourcesCalledBeforeFrameStart = false;
+
+#ifdef VK_EXT_debug_utils
+ PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT = nullptr;
+ PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT = nullptr;
+ PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT = nullptr;
+ PFN_vkCmdInsertDebugUtilsLabelEXT vkCmdInsertDebugUtilsLabelEXT = nullptr;
+#endif
+
+ PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR = nullptr;
+ PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR;
+ PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
+ PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
+ PFN_vkQueuePresentKHR vkQueuePresentKHR;
+ PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
+ PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
+ PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
+
+#ifdef VK_KHR_create_renderpass2
+ PFN_vkCreateRenderPass2KHR vkCreateRenderPass2KHR = nullptr;
+#endif
+
+ struct {
+ bool compute = false;
+ bool wideLines = false;
+ bool debugUtils = false;
+ bool vertexAttribDivisor = false;
+ bool texture3DSliceAs2D = false;
+ bool tessellation = false;
+ bool geometryShader = false;
+ bool nonFillPolygonMode = false;
+ bool multiView = false;
+ bool renderPass2KHR = false;
+ bool depthStencilResolveKHR = false;
+ QVersionNumber apiVersion;
+ } caps;
+
+ VkPipelineCache pipelineCache = VK_NULL_HANDLE;
+ struct DescriptorPoolData {
+ DescriptorPoolData() { }
+ DescriptorPoolData(VkDescriptorPool pool_)
+ : pool(pool_)
+ { }
+ VkDescriptorPool pool = VK_NULL_HANDLE;
+ int refCount = 0;
+ int allocedDescSets = 0;
+ };
+ QVarLengthArray<DescriptorPoolData, 8> descriptorPools;
+ QVarLengthArray<VkCommandBuffer, 4> freeSecondaryCbs[QVK_FRAMES_IN_FLIGHT];
+
+ VkQueryPool timestampQueryPool = VK_NULL_HANDLE;
+ QBitArray timestampQueryPoolMap;
+
+ VkFormat optimalDsFormat = VK_FORMAT_UNDEFINED;
+ QMatrix4x4 clipCorrectMatrix;
+
+ QVkSwapChain *currentSwapChain = nullptr;
+ QSet<QVkSwapChain *> swapchains;
+ QRhiVulkanNativeHandles nativeHandlesStruct;
+ QRhiDriverInfo driverInfoStruct;
+
+ struct OffscreenFrame {
+ OffscreenFrame(QRhiImplementation *rhi)
+ {
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
+ cbWrapper[i] = new QVkCommandBuffer(rhi);
+ }
+ ~OffscreenFrame()
+ {
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
+ delete cbWrapper[i];
+ }
+ bool active = false;
+ QVkCommandBuffer *cbWrapper[QVK_FRAMES_IN_FLIGHT];
+ VkFence cmdFence = VK_NULL_HANDLE;
+ int timestampQueryIndex = -1;
+ } ofr;
+
+ struct TextureReadback {
+ int activeFrameSlot = -1;
+ QRhiReadbackDescription desc;
+ QRhiReadbackResult *result;
+ VkBuffer stagingBuf;
+ QVkAlloc stagingAlloc;
+ quint32 byteSize;
+ QSize pixelSize;
+ QRhiTexture::Format format;
+ };
+ QVarLengthArray<TextureReadback, 2> activeTextureReadbacks;
+ struct BufferReadback {
+ int activeFrameSlot = -1;
+ QRhiReadbackResult *result;
+ quint32 byteSize;
+ VkBuffer stagingBuf;
+ QVkAlloc stagingAlloc;
+ };
+ QVarLengthArray<BufferReadback, 2> activeBufferReadbacks;
+
+ struct DeferredReleaseEntry {
+ enum Type {
+ Pipeline,
+ ShaderResourceBindings,
+ Buffer,
+ RenderBuffer,
+ Texture,
+ Sampler,
+ TextureRenderTarget,
+ RenderPass,
+ StagingBuffer,
+ SecondaryCommandBuffer
+ };
+ 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_MIP_LEVELS];
+ } texture;
+ struct {
+ VkSampler sampler;
+ } sampler;
+ struct {
+ VkFramebuffer fb;
+ VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
+ VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
+ VkImageView dsv;
+ VkImageView resdsv;
+ } textureRenderTarget;
+ struct {
+ VkRenderPass rp;
+ } renderPass;
+ struct {
+ VkBuffer stagingBuffer;
+ QVkAlloc stagingAllocation;
+ } stagingBuffer;
+ struct {
+ VkCommandBuffer cb;
+ } secondaryCommandBuffer;
+ };
+ };
+ QList<DeferredReleaseEntry> releaseQueue;
+};
+
+Q_DECLARE_TYPEINFO(QRhiVulkan::DescriptorPoolData, Q_RELOCATABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiVulkan::DeferredReleaseEntry, Q_RELOCATABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiVulkan::TextureReadback, Q_RELOCATABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiVulkan::BufferReadback, Q_RELOCATABLE_TYPE);
+
QT_END_NAMESPACE
#endif
diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h
deleted file mode 100644
index 0a035e3f34..0000000000
--- a/src/gui/rhi/qrhivulkan_p_p.h
+++ /dev/null
@@ -1,995 +0,0 @@
-// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#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 destroy() override;
- bool create() override;
- QRhiBuffer::NativeBuffer nativeBuffer() override;
- char *beginFullDynamicBufferUpdateForCurrentFrame() override;
- void endFullDynamicBufferUpdateForCurrentFrame() override;
-
- VkBuffer buffers[QVK_FRAMES_IN_FLIGHT];
- QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT];
- struct DynamicUpdate {
- int offset;
- QRhiBufferData data;
- };
- QVarLengthArray<DynamicUpdate, 16> 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;
-};
-
-Q_DECLARE_TYPEINFO(QVkBuffer::DynamicUpdate, Q_RELOCATABLE_TYPE);
-
-struct QVkTexture;
-
-struct QVkRenderBuffer : public QRhiRenderBuffer
-{
- QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
- int sampleCount, Flags flags,
- QRhiTexture::Format backingFormatHint);
- ~QVkRenderBuffer();
- void destroy() override;
- bool create() 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;
- uint generation = 0;
- friend class QRhiVulkan;
-};
-
-struct QVkTexture : public QRhiTexture
-{
- QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
- int arraySize, int sampleCount, Flags flags);
- ~QVkTexture();
- void destroy() override;
- bool create() override;
- bool createFrom(NativeTexture src) override;
- NativeTexture nativeTexture() override;
- void setNativeLayout(int layout) override;
-
- bool prepareCreate(QSize *adjustedSize = nullptr);
- bool finishCreate();
- 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_MIP_LEVELS];
- bool owns = true;
- 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, AddressMode w);
- ~QVkSampler();
- void destroy() override;
- bool create() override;
-
- VkSampler sampler = VK_NULL_HANDLE;
- int lastActiveFrameSlot = -1;
- uint generation = 0;
- friend class QRhiVulkan;
-};
-
-struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
-{
- QVkRenderPassDescriptor(QRhiImplementation *rhi);
- ~QVkRenderPassDescriptor();
- void destroy() override;
- bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
- QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
- QVector<quint32> serializedFormat() const override;
- const QRhiNativeHandles *nativeHandles() override;
-
- void updateSerializedFormat();
-
- VkRenderPass rp = VK_NULL_HANDLE;
- bool ownsRp = false;
- QVarLengthArray<VkAttachmentDescription, 8> attDescs;
- QVarLengthArray<VkAttachmentReference, 8> colorRefs;
- QVarLengthArray<VkAttachmentReference, 8> resolveRefs;
- QVarLengthArray<VkSubpassDependency, 2> subpassDeps;
- bool hasDepthStencil = false;
- VkAttachmentReference dsRef;
- QVector<quint32> serializedFormatData;
- QRhiVulkanRenderPassNativeHandles nativeHandlesStruct;
- 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;
- QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList;
- static const int MAX_COLOR_ATTACHMENTS = 8;
-};
-
-struct QVkSwapChainRenderTarget : public QRhiSwapChainRenderTarget
-{
- QVkSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain);
- ~QVkSwapChainRenderTarget();
- void destroy() 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 destroy() override;
-
- QSize pixelSize() const override;
- float devicePixelRatio() const override;
- int sampleCount() const override;
-
- QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
- bool create() 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 destroy() override;
- bool create() override;
- void updateResources(UpdateFlags flags) override;
-
- QVarLengthArray<QRhiShaderResourceBinding, 8> sortedBindings;
- bool hasSlottedResource = false;
- bool hasDynamicOffset = false;
- 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 {
- int count;
- struct {
- quint64 texId;
- uint texGeneration;
- quint64 samplerId;
- uint samplerGeneration;
- } d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE];
- };
- struct BoundStorageImageData {
- quint64 id;
- uint generation;
- };
- struct BoundStorageBufferData {
- quint64 id;
- uint generation;
- };
- struct BoundResourceData {
- union {
- BoundUniformBufferData ubuf;
- BoundSampledTextureData stex;
- BoundStorageImageData simage;
- BoundStorageBufferData sbuf;
- };
- };
- QVarLengthArray<BoundResourceData, 8> boundResourceData[QVK_FRAMES_IN_FLIGHT];
-
- friend class QRhiVulkan;
-};
-
-Q_DECLARE_TYPEINFO(QVkShaderResourceBindings::BoundResourceData, Q_RELOCATABLE_TYPE);
-
-struct QVkGraphicsPipeline : public QRhiGraphicsPipeline
-{
- QVkGraphicsPipeline(QRhiImplementation *rhi);
- ~QVkGraphicsPipeline();
- void destroy() override;
- bool create() 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 destroy() override;
- bool create() 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 destroy() override;
-
- const QRhiNativeHandles *nativeHandles();
-
- VkCommandBuffer cb = VK_NULL_HANDLE; // primary
- QRhiVulkanCommandBufferNativeHandles nativeHandlesStruct;
-
- enum PassType {
- NoPass,
- RenderPass,
- ComputePass
- };
-
- void resetState() {
- recordingPass = NoPass;
- passUsesSecondaryCb = false;
- currentTarget = nullptr;
- activeSecondaryCbStack.clear();
- resetCommands();
- 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));
- inExternal = false;
- }
-
- PassType recordingPass;
- bool passUsesSecondaryCb;
- 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];
- QVarLengthArray<VkCommandBuffer, 4> activeSecondaryCbStack;
- bool inExternal;
-
- struct {
- QHash<QRhiResource *, QPair<VkAccessFlags, bool> > writtenResources;
- void reset() {
- writtenResources.clear();
- }
- } computePassState;
-
- 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,
- ExecuteSecondary
- };
- 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;
- int count;
- int index;
- } imageBarrier;
- struct {
- VkPipelineStageFlags srcStageMask;
- VkPipelineStageFlags dstStageMask;
- int count;
- int index;
- } bufferBarrier;
- struct {
- VkImage src;
- VkImageLayout srcLayout;
- VkImage dst;
- VkImageLayout dstLayout;
- VkFilter filter;
- VkImageBlit desc;
- } blitImage;
- struct {
- VkRenderPassBeginInfo desc;
- int clearValueIndex;
- bool useSecondaryCb;
- } 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;
- int markerNameIndex;
- } debugMarkerInsert;
- struct {
- int trackerIndex;
- } transitionResources;
- struct {
- int x, y, z;
- } dispatch;
- struct {
- VkCommandBuffer cb;
- } executeSecondary;
- } args;
- };
-
- QRhiBackendCommandList<Command> commands;
- QVarLengthArray<QRhiPassResourceTracker, 8> passResTrackers;
- int currentPassResTrackerIndex;
-
- void resetCommands() {
- commands.reset();
- resetPools();
-
- passResTrackers.clear();
- currentPassResTrackerIndex = -1;
- }
-
- void resetPools() {
- pools.clearValue.clear();
- pools.bufferImageCopy.clear();
- pools.dynamicOffset.clear();
- pools.vertexBuffer.clear();
- pools.vertexBufferOffset.clear();
- pools.debugMarkerData.clear();
- pools.imageBarrier.clear();
- pools.bufferBarrier.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> debugMarkerData;
- QVarLengthArray<VkImageMemoryBarrier, 8> imageBarrier;
- QVarLengthArray<VkBufferMemoryBarrier, 8> bufferBarrier;
- } pools;
-
- friend class QRhiVulkan;
-};
-
-struct QVkSwapChain : public QRhiSwapChain
-{
- QVkSwapChain(QRhiImplementation *rhi);
- ~QVkSwapChain();
- void destroy() override;
-
- QRhiCommandBuffer *currentFrameCommandBuffer() override;
- QRhiRenderTarget *currentFrameRenderTarget() override;
-
- QSize surfacePixelSize() override;
- bool isFormatSupported(Format f) override;
-
- QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
- bool createOrResize() override;
-
- bool ensureSurface();
-
- static const quint32 EXPECTED_MAX_BUFFER_COUNT = 4;
-
- 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;
- QVarLengthArray<VkPresentModeKHR, 8> supportedPresentationModes;
- VkDeviceMemory msaaImageMem = VK_NULL_HANDLE;
- QVkSwapChainRenderTarget 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;
- };
- QVarLengthArray<ImageResources, EXPECTED_MAX_BUFFER_COUNT> imageRes;
-
- 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;
- VkFence cmdFence = VK_NULL_HANDLE;
- bool cmdFenceWaitable = false;
- VkCommandBuffer cmdBuf = VK_NULL_HANDLE; // primary
- 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 *importParams = 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,
- QRhiTexture::Format backingFormatHint) override;
- QRhiTexture *createTexture(QRhiTexture::Format format,
- const QSize &pixelSize,
- int depth,
- int arraySize,
- int sampleCount,
- QRhiTexture::Flags flags) override;
- QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
- QRhiSampler::Filter minFilter,
- QRhiSampler::Filter mipmapMode,
- QRhiSampler:: AddressMode u,
- QRhiSampler::AddressMode v,
- QRhiSampler::AddressMode w) 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, QRhi::BeginFrameFlags flags) override;
- QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) 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,
- QRhiCommandBuffer::BeginPassFlags flags) 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,
- QRhiCommandBuffer::BeginPassFlags flags) 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;
-
- QList<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;
- QRhiDriverInfo driverInfo() const override;
- QRhiMemAllocStats graphicsMemoryAllocationStatistics() override;
- bool makeThreadLocalNativeContextCurrent() override;
- void releaseCachedResources() override;
- bool isDeviceLost() const override;
-
- QByteArray pipelineCacheData() override;
- void setPipelineCacheData(const QByteArray &data) 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(QVkRenderPassDescriptor *rpD,
- bool hasDepthStencil,
- VkSampleCountFlagBits samples,
- VkFormat colorFormat);
- bool createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
- const QRhiColorAttachment *firstColorAttachment,
- const QRhiColorAttachment *lastColorAttachment,
- bool preserveColor,
- bool preserveDs,
- QRhiRenderBuffer *depthStencilBuffer,
- QRhiTexture *depthTexture);
- bool ensurePipelineCache(const void *initialData = nullptr, size_t initialDataSize = 0);
- VkShaderModule createShader(const QByteArray &spirv);
-
- void prepareNewFrame(QRhiCommandBuffer *cb);
- VkCommandBuffer startSecondaryCommandBuffer(QVkRenderTargetData *rtD = nullptr);
- void endAndEnqueueSecondaryCommandBuffer(VkCommandBuffer cb, QVkCommandBuffer *cbD);
- QRhi::FrameOpResult startPrimaryCommandBuffer(VkCommandBuffer *cb);
- QRhi::FrameOpResult endAndSubmitPrimaryCommandBuffer(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 executeBufferHostWritesForSlot(QVkBuffer *bufD, int slot);
- void enqueueTransitionPassResources(QVkCommandBuffer *cbD);
- void recordPrimaryCommandBuffer(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 depthStencilExplicitBarrier(QVkCommandBuffer *cbD, QVkRenderBuffer *rbD);
- 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);
- void ensureCommandPoolForNewFrame();
-
- QVulkanInstance *inst = nullptr;
- QWindow *maybeWindow = nullptr;
- QByteArrayList requestedDeviceExtensions;
- bool importedDevice = false;
- VkPhysicalDevice physDev = VK_NULL_HANDLE;
- VkDevice dev = VK_NULL_HANDLE;
- VkCommandPool cmdPool[QVK_FRAMES_IN_FLIGHT] = {};
- int gfxQueueFamilyIdx = -1;
- int gfxQueueIdx = 0;
- VkQueue gfxQueue = VK_NULL_HANDLE;
- quint32 timestampValidBits = 0;
- bool importedAllocator = false;
- QVkAllocator allocator = nullptr;
- QVulkanFunctions *f = nullptr;
- QVulkanDeviceFunctions *df = nullptr;
- QRhi::Flags rhiFlags;
- VkPhysicalDeviceFeatures physDevFeatures;
- VkPhysicalDeviceProperties physDevProperties;
- VkDeviceSize ubufAlign;
- VkDeviceSize texbufAlign;
- bool deviceLost = false;
- bool releaseCachedResourcesCalledBeforeFrameStart = 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;
- PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
- PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
-
- struct {
- bool compute = false;
- bool wideLines = false;
- bool debugMarkers = false;
- bool vertexAttribDivisor = false;
- bool texture3DSliceAs2D = false;
- bool tessellation = false;
- bool vulkan11OrHigher = false;
- bool geometryShader = false;
- bool nonFillPolygonMode = false;
- } caps;
-
- VkPipelineCache pipelineCache = VK_NULL_HANDLE;
- struct DescriptorPoolData {
- DescriptorPoolData() { }
- DescriptorPoolData(VkDescriptorPool pool_)
- : pool(pool_)
- { }
- VkDescriptorPool pool = VK_NULL_HANDLE;
- int refCount = 0;
- int allocedDescSets = 0;
- };
- QVarLengthArray<DescriptorPoolData, 8> descriptorPools;
- QVarLengthArray<VkCommandBuffer, 4> freeSecondaryCbs[QVK_FRAMES_IN_FLIGHT];
-
- VkQueryPool timestampQueryPool = VK_NULL_HANDLE;
- QBitArray timestampQueryPoolMap;
-
- VkFormat optimalDsFormat = VK_FORMAT_UNDEFINED;
- QMatrix4x4 clipCorrectMatrix;
-
- QVkSwapChain *currentSwapChain = nullptr;
- QSet<QVkSwapChain *> swapchains;
- QRhiVulkanNativeHandles nativeHandlesStruct;
- QRhiDriverInfo driverInfoStruct;
-
- struct OffscreenFrame {
- OffscreenFrame(QRhiImplementation *rhi)
- {
- for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
- cbWrapper[i] = new QVkCommandBuffer(rhi);
- }
- ~OffscreenFrame()
- {
- for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
- delete cbWrapper[i];
- }
- bool active = false;
- QVkCommandBuffer *cbWrapper[QVK_FRAMES_IN_FLIGHT];
- VkFence cmdFence = VK_NULL_HANDLE;
- } ofr;
-
- struct TextureReadback {
- int activeFrameSlot = -1;
- QRhiReadbackDescription desc;
- QRhiReadbackResult *result;
- VkBuffer stagingBuf;
- QVkAlloc stagingAlloc;
- quint32 byteSize;
- QSize pixelSize;
- QRhiTexture::Format format;
- };
- QVarLengthArray<TextureReadback, 2> activeTextureReadbacks;
- struct BufferReadback {
- int activeFrameSlot = -1;
- QRhiBufferReadbackResult *result;
- int byteSize;
- VkBuffer stagingBuf;
- QVkAlloc stagingAlloc;
- };
- QVarLengthArray<BufferReadback, 2> activeBufferReadbacks;
-
- struct DeferredReleaseEntry {
- enum Type {
- Pipeline,
- ShaderResourceBindings,
- Buffer,
- RenderBuffer,
- Texture,
- Sampler,
- TextureRenderTarget,
- RenderPass,
- StagingBuffer,
- SecondaryCommandBuffer
- };
- 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_MIP_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;
- struct {
- VkCommandBuffer cb;
- } secondaryCommandBuffer;
- };
- };
- QList<DeferredReleaseEntry> releaseQueue;
-};
-
-Q_DECLARE_TYPEINFO(QRhiVulkan::DescriptorPoolData, Q_RELOCATABLE_TYPE);
-Q_DECLARE_TYPEINFO(QRhiVulkan::DeferredReleaseEntry, Q_RELOCATABLE_TYPE);
-Q_DECLARE_TYPEINFO(QRhiVulkan::TextureReadback, Q_RELOCATABLE_TYPE);
-Q_DECLARE_TYPEINFO(QRhiVulkan::BufferReadback, Q_RELOCATABLE_TYPE);
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/gui/rhi/qrhivulkanext_p.h b/src/gui/rhi/qrhivulkanext_p.h
deleted file mode 100644
index 02b346948b..0000000000
--- a/src/gui/rhi/qrhivulkanext_p.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#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
index cd4a9f3854..d5fb53e7e6 100644
--- a/src/gui/rhi/qshader.cpp
+++ b/src/gui/rhi/qshader.cpp
@@ -1,7 +1,7 @@
-// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qshader_p_p.h"
+#include "qshader_p.h"
#include <QDataStream>
#include <QBuffer>
@@ -9,8 +9,9 @@ QT_BEGIN_NAMESPACE
/*!
\class QShader
- \internal
+ \ingroup painting-3D
\inmodule QtGui
+ \since 6.6
\brief Contains multiple versions of a shader translated to multiple shading languages,
together with reflection metadata.
@@ -21,6 +22,16 @@ QT_BEGIN_NAMESPACE
as, Vulkan, Metal, Direct3D, and OpenGL, take QShader as their input
whenever a shader needs to be specified.
+ \warning The QRhi family of classes in the Qt Gui module, including QShader
+ and QShaderDescription, offer limited compatibility guarantees. There are
+ no source or binary compatibility guarantees for these classes, meaning the
+ API is only guaranteed to work with the Qt version the application was
+ developed against. Source incompatible changes are however aimed to be kept
+ at a minimum and will only be made in minor releases (6.7, 6.8, and so on).
+ To use these classes in an application, link to
+ \c{Qt::GuiPrivate} (if using CMake), and include the headers with the \c
+ rhi prefix, for example \c{#include <rhi/qshader.h>}.
+
A QShader instance is empty and thus invalid by default. To get a useful
instance, the two typical methods are:
@@ -68,8 +79,9 @@ QT_BEGIN_NAMESPACE
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:
+ For reference, a typical, portable QRhi expects that a QShader suitable for
+ all its backends contains at least the following. (this excludes support
+ for core profile OpenGL contexts, add GLSL 150 or newer for that)
\list
@@ -77,11 +89,11 @@ QT_BEGIN_NAMESPACE
\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 GLSL 120 source code suitable for OpenGL 2.1 or newer
- \li HLSL Shader Model 5.0 source code or the corresponding DXBC bytecode suitable for Direct3D 11
+ \li HLSL Shader Model 5.0 source code or the corresponding DXBC bytecode suitable for Direct3D 11/12
- \li Metal Shading Language 1.2 source code or the corresponding bytecode suitable for Metal
+ \li Metal Shading Language 1.2 source code or the corresponding bytecode suitable for Metal 1.2 or newer
\endlist
@@ -102,8 +114,8 @@ QT_BEGIN_NAMESPACE
/*!
\class QShaderVersion
- \internal
\inmodule QtGui
+ \since 6.6
\brief Specifies the shading language version.
@@ -128,6 +140,9 @@ QT_BEGIN_NAMESPACE
A default constructed QShaderVersion contains a version of 100 and no
flags set.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QShader
+ for details.
*/
/*!
@@ -140,13 +155,16 @@ QT_BEGIN_NAMESPACE
/*!
\class QShaderKey
- \internal
\inmodule QtGui
+ \since 6.6
\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.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QShader
+ for details.
*/
/*!
@@ -160,6 +178,7 @@ QT_BEGIN_NAMESPACE
\value MslShader Metal Shading Language
\value DxilShader Direct3D bytecode (HLSL compiled by \c dxc)
\value MetalLibShader Pre-compiled Metal bytecode
+ \value WgslShader WGSL
*/
/*!
@@ -167,25 +186,74 @@ QT_BEGIN_NAMESPACE
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.
+
+ \value UInt16IndexedVertexAsComputeShader A vertex shader meant to be used
+ in a Metal pipeline with tessellation in combination with indexed draw
+ calls sourcing index data from a uint16 index buffer. To support the Metal
+ tessellation pipeline, the vertex shader is translated to a compute shader
+ that may be dependent on the index buffer usage in the draw calls (e.g. if
+ the shader is using gl_VertexIndex), hence the need for three dedicated
+ variants.
+
+ \value UInt32IndexedVertexAsComputeShader A vertex shader meant to be used
+ in a Metal pipeline with tessellation in combination with indexed draw
+ calls sourcing index data from a uint32 index buffer. To support the Metal
+ tessellation pipeline, the vertex shader is translated to a compute shader
+ that may be dependent on the index buffer usage in the draw calls (e.g. if
+ the shader is using gl_VertexIndex), hence the need for three dedicated
+ variants.
+
+ \value NonIndexedVertexAsComputeShader A vertex shader meant to be used in
+ a Metal pipeline with tessellation in combination with non-indexed draw
+ calls. To support the Metal tessellation pipeline, the vertex shader is
+ translated to a compute shader that may be dependent on the index buffer
+ usage in the draw calls (e.g. if the shader is using gl_VertexIndex), hence
+ the need for three dedicated variants.
+ */
+
+/*!
+ \enum QShader::SerializedFormatVersion
+ Describes the desired output format when serializing the QShader.
+
+ The default value for the \c version argument of serialized() is \c Latest.
+ This is sufficient in the vast majority of cases. Specifying another value
+ is needed only when the intention is to generate serialized data that can
+ be loaded by earlier Qt versions. For example, the \c qsb tool uses these
+ enum values when the \c{--qsbversion} command-line argument is given.
+
+ \note Targeting earlier versions will make certain features disfunctional
+ with the generated asset. This is not an issue when using the asset with
+ the specified, older Qt version, given that that Qt version does not have
+ the newer features in newer Qt versions that rely on additional data
+ generated in the QShader and the serialized data stream, but may become a
+ problem if the generated asset is then used with a newer Qt version.
+
+ \value Latest The current Qt version
+ \value Qt_6_5 Qt 6.5
+ \value Qt_6_4 Qt 6.4
*/
/*!
\class QShaderCode
- \internal
\inmodule QtGui
+ \since 6.6
\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.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QShader
+ for details.
*/
/*!
Constructs a new, empty (and thus invalid) QShader instance.
*/
QShader::QShader()
- : d(new QShaderPrivate)
+ : d(nullptr)
{
}
@@ -194,42 +262,87 @@ QShader::QShader()
*/
void QShader::detach()
{
- qAtomicDetach(d);
+ if (d)
+ qAtomicDetach(d);
+ else
+ d = new QShaderPrivate;
}
/*!
- \internal
+ Constructs a copy of \a other.
*/
QShader::QShader(const QShader &other)
: d(other.d)
{
- d->ref.ref();
+ if (d)
+ d->ref.ref();
}
/*!
- \internal
+ Assigns \a other to this object.
*/
QShader &QShader::operator=(const QShader &other)
{
- qAtomicAssign(d, other.d);
+ if (d) {
+ if (other.d) {
+ qAtomicAssign(d, other.d);
+ } else {
+ if (!d->ref.deref())
+ delete d;
+ d = nullptr;
+ }
+ } else if (other.d) {
+ other.d->ref.ref();
+ d = other.d;
+ }
return *this;
}
/*!
+ \fn QShader::QShader(QShader &&other) noexcept
+ \since 6.7
+
+ Move-constructs a new QShader from \a other.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ \fn QShader &QShader::operator=(QShader &&other)
+ \since 6.7
+
+ Move-assigns \a other to this QShader instance.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
Destructor.
*/
QShader::~QShader()
{
- if (!d->ref.deref())
+ if (d && !d->ref.deref())
delete d;
}
/*!
+ \fn void QShader::swap(QShader &other)
+ \since 6.7
+
+ Swaps shader \a other with this shader. This operation is very fast and
+ never fails.
+*/
+
+/*!
\return true if the QShader contains at least one shader version.
*/
bool QShader::isValid() const
{
- return !d->shaders.isEmpty();
+ return d ? !d->shaders.isEmpty() : false;
}
/*!
@@ -237,7 +350,7 @@ bool QShader::isValid() const
*/
QShader::Stage QShader::stage() const
{
- return d->stage;
+ return d ? d->stage : QShader::VertexStage;
}
/*!
@@ -245,7 +358,7 @@ QShader::Stage QShader::stage() const
*/
void QShader::setStage(Stage stage)
{
- if (stage != d->stage) {
+ if (!d || stage != d->stage) {
detach();
d->stage = stage;
}
@@ -256,7 +369,7 @@ void QShader::setStage(Stage stage)
*/
QShaderDescription QShader::description() const
{
- return d->desc;
+ return d ? d->desc : QShaderDescription();
}
/*!
@@ -273,7 +386,7 @@ void QShader::setDescription(const QShaderDescription &desc)
*/
QList<QShaderKey> QShader::availableShaders() const
{
- return d->shaders.keys().toVector();
+ return d ? d->shaders.keys().toVector() : QList<QShaderKey>();
}
/*!
@@ -281,7 +394,7 @@ QList<QShaderKey> QShader::availableShaders() const
*/
QShaderCode QShader::shader(const QShaderKey &key) const
{
- return d->shaders.value(key);
+ return d ? d->shaders.value(key) : QShaderCode();
}
/*!
@@ -289,7 +402,7 @@ QShaderCode QShader::shader(const QShaderKey &key) const
*/
void QShader::setShader(const QShaderKey &key, const QShaderCode &shader)
{
- if (d->shaders.value(key) == shader)
+ if (d && d->shaders.value(key) == shader)
return;
detach();
@@ -302,6 +415,9 @@ void QShader::setShader(const QShaderKey &key, const QShaderCode &shader)
*/
void QShader::removeShader(const QShaderKey &key)
{
+ if (!d)
+ return;
+
auto it = d->shaders.find(key);
if (it == d->shaders.end())
return;
@@ -322,51 +438,78 @@ static void writeShaderKey(QDataStream *ds, const QShaderKey &k)
\return a serialized binary version of all the data held by the
QShader, suitable for writing to files or other I/O devices.
+ By default the latest serialization format is used. Use \a version
+ parameter to serialize for a compatibility Qt version. Only when it is
+ known that the generated data stream must be made compatible with an older
+ Qt version at the expense of making it incompatible with features
+ introduced since that Qt version, should another value (for example,
+ \l{SerializedFormatVersion}{Qt_6_5} for Qt 6.5) be used.
+
\sa fromSerialized()
*/
-QByteArray QShader::serialized() const
+QByteArray QShader::serialized(SerializedFormatVersion version) const
{
+ static QShaderPrivate sd;
+ QShaderPrivate *dd = d ? d : &sd;
+
QBuffer buf;
QDataStream ds(&buf);
ds.setVersion(QDataStream::Qt_5_10);
if (!buf.open(QIODevice::WriteOnly))
return QByteArray();
- ds << QShaderPrivate::QSB_VERSION;
- ds << int(d->stage);
- d->desc.serialize(&ds);
- ds << int(d->shaders.count());
- for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) {
+ const int qsbVersion = QShaderPrivate::qtQsbVersion(version);
+ ds << qsbVersion;
+
+ ds << int(dd->stage);
+ dd->desc.serialize(&ds, qsbVersion);
+ ds << int(dd->shaders.size());
+ for (auto it = dd->shaders.cbegin(), itEnd = dd->shaders.cend(); it != itEnd; ++it) {
const QShaderKey &k(it.key());
writeShaderKey(&ds, k);
- const QShaderCode &shader(d->shaders.value(k));
+ const QShaderCode &shader(dd->shaders.value(k));
ds << shader.shader();
ds << shader.entryPoint();
}
- ds << int(d->bindings.count());
- for (auto it = d->bindings.cbegin(), itEnd = d->bindings.cend(); it != itEnd; ++it) {
+ ds << int(dd->bindings.size());
+ for (auto it = dd->bindings.cbegin(), itEnd = dd->bindings.cend(); it != itEnd; ++it) {
const QShaderKey &k(it.key());
writeShaderKey(&ds, k);
const NativeResourceBindingMap &map(it.value());
- ds << int(map.count());
+ ds << int(map.size());
for (auto mapIt = map.cbegin(), mapItEnd = map.cend(); mapIt != mapItEnd; ++mapIt) {
ds << mapIt.key();
ds << mapIt.value().first;
ds << mapIt.value().second;
}
}
- ds << int(d->combinedImageMap.count());
- for (auto it = d->combinedImageMap.cbegin(), itEnd = d->combinedImageMap.cend(); it != itEnd; ++it) {
+ ds << int(dd->combinedImageMap.size());
+ for (auto it = dd->combinedImageMap.cbegin(), itEnd = dd->combinedImageMap.cend(); it != itEnd; ++it) {
const QShaderKey &k(it.key());
writeShaderKey(&ds, k);
const SeparateToCombinedImageSamplerMappingList &list(it.value());
- ds << int(list.count());
+ ds << int(list.size());
for (auto listIt = list.cbegin(), listItEnd = list.cend(); listIt != listItEnd; ++listIt) {
ds << listIt->combinedSamplerName;
ds << listIt->textureBinding;
ds << listIt->samplerBinding;
}
}
+ if (qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO) {
+ ds << int(dd->nativeShaderInfoMap.size());
+ for (auto it = dd->nativeShaderInfoMap.cbegin(), itEnd = dd->nativeShaderInfoMap.cend(); it != itEnd; ++it) {
+ const QShaderKey &k(it.key());
+ writeShaderKey(&ds, k);
+ ds << it->flags;
+ ds << int(it->extraBufferBindings.size());
+ for (auto mapIt = it->extraBufferBindings.cbegin(), mapItEnd = it->extraBufferBindings.cend();
+ mapIt != mapItEnd; ++mapIt)
+ {
+ ds << mapIt.key();
+ ds << mapIt.value();
+ }
+ }
+ }
return qCompress(buf.buffer());
}
@@ -389,6 +532,9 @@ static void readShaderKey(QDataStream *ds, QShaderKey *k)
/*!
Creates a new QShader instance from the given \a data.
+ If \a data cannot be deserialized successfully, the result is a default
+ constructed QShader for which isValid() returns \c false.
+
\sa serialized()
*/
QShader QShader::fromSerialized(const QByteArray &data)
@@ -401,12 +547,16 @@ QShader QShader::fromSerialized(const QByteArray &data)
return QShader();
QShader bs;
+ bs.detach(); // to get d created
QShaderPrivate *d = QShaderPrivate::get(&bs);
Q_ASSERT(d->ref.loadRelaxed() == 1); // must be detached
int intVal;
ds >> intVal;
d->qsbVersion = intVal;
if (d->qsbVersion != QShaderPrivate::QSB_VERSION
+ && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_INPUT_OUTPUT_INTERFACE_BLOCKS
+ && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_EXTENDED_STORAGE_BUFFER_INFO
+ && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_VAR_ARRAYDIMS
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_CBOR
@@ -484,19 +634,102 @@ QShader QShader::fromSerialized(const QByteArray &data)
}
}
+ if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO) {
+ ds >> count;
+ for (int i = 0; i < count; ++i) {
+ QShaderKey k;
+ readShaderKey(&ds, &k);
+ int flags;
+ ds >> flags;
+ QMap<int, int> extraBufferBindings;
+ int mapSize;
+ ds >> mapSize;
+ for (int b = 0; b < mapSize; ++b) {
+ int k, v;
+ ds >> k;
+ ds >> v;
+ extraBufferBindings.insert(k, v);
+ }
+ d->nativeShaderInfoMap.insert(k, { flags, extraBufferBindings });
+ }
+ }
+
return bs;
}
+/*!
+ \fn QShaderVersion::QShaderVersion() = default
+ */
+
+/*!
+ Constructs a new QShaderVersion with version \a v and flags \a f.
+ */
QShaderVersion::QShaderVersion(int v, Flags f)
: m_version(v), m_flags(f)
{
}
+/*!
+ \fn int QShaderVersion::version() const
+ \return the version.
+ */
+
+/*!
+ \fn void QShaderVersion::setVersion(int v)
+ Sets the shading language version to \a v.
+ */
+
+/*!
+ \fn QShaderVersion::Flags QShaderVersion::flags() const
+ \return the flags.
+ */
+
+/*!
+ \fn void QShaderVersion::setFlags(Flags f)
+ Sets the flags \a f.
+ */
+
+/*!
+ \fn QShaderCode::QShaderCode() = default
+ */
+
+/*!
+ Constructs a new QShaderCode with the specified shader source \a code and
+ \a entry point name.
+ */
QShaderCode::QShaderCode(const QByteArray &code, const QByteArray &entry)
: m_shader(code), m_entryPoint(entry)
{
}
+/*!
+ \fn QByteArray QShaderCode::shader() const
+ \return the shader source or bytecode.
+ */
+
+/*!
+ \fn void QShaderCode::setShader(const QByteArray &code)
+ Sets the shader source or byte \a code.
+ */
+
+/*!
+ \fn QByteArray QShaderCode::entryPoint() const
+ \return the entry point name.
+ */
+
+/*!
+ \fn void QShaderCode::setEntryPoint(const QByteArray &entry)
+ Sets the \a entry point name.
+ */
+
+/*!
+ \fn QShaderKey::QShaderKey() = default
+ */
+
+/*!
+ Constructs a new QShaderKey with shader type \a s, version \a sver, and
+ variant \a svar.
+ */
QShaderKey::QShaderKey(QShader::Source s,
const QShaderVersion &sver,
QShader::Variant svar)
@@ -507,6 +740,36 @@ QShaderKey::QShaderKey(QShader::Source s,
}
/*!
+ \fn QShader::Source QShaderKey::source() const
+ \return the shader type.
+ */
+
+/*!
+ \fn void QShaderKey::setSource(QShader::Source s)
+ Sets the shader type \a s.
+ */
+
+/*!
+ \fn QShaderVersion QShaderKey::sourceVersion() const
+ \return the shading language version.
+ */
+
+/*!
+ \fn void QShaderKey::setSourceVersion(const QShaderVersion &sver)
+ Sets the shading language version \a sver.
+ */
+
+/*!
+ \fn QShader::Variant QShaderKey::sourceVariant() const
+ \return the type of the variant to use.
+ */
+
+/*!
+ \fn void QShaderKey::setSourceVariant(QShader::Variant svar)
+ Sets the type of variant to use to \a svar.
+ */
+
+/*!
Returns \c true if the two QShader objects \a lhs and \a rhs are equal,
meaning they are for the same stage with matching sets of shader source or
binary code.
@@ -515,16 +778,18 @@ QShaderKey::QShaderKey(QShader::Source s,
*/
bool operator==(const QShader &lhs, const QShader &rhs) noexcept
{
+ if (!lhs.d || !rhs.d)
+ return lhs.d == rhs.d;
+
return lhs.d->stage == rhs.d->stage
- && lhs.d->shaders == rhs.d->shaders;
- // do not bother with desc and bindings, if the shader code is the same, the description must match too
+ && lhs.d->shaders == rhs.d->shaders
+ && lhs.d->bindings == rhs.d->bindings;
}
/*!
- \internal
\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
+ Returns \c false if the values in the two QShader objects \a lhs and \a rhs
are equal; otherwise returns \c true.
\relates QShader
@@ -537,11 +802,14 @@ bool operator==(const QShader &lhs, const QShader &rhs) noexcept
*/
size_t qHash(const QShader &s, size_t seed) noexcept
{
- QtPrivate::QHashCombine hash;
- seed = hash(seed, s.stage());
- seed = qHashRange(s.d->shaders.keyValueBegin(),
- s.d->shaders.keyValueEnd(),
- seed);
+ if (s.d) {
+ QtPrivate::QHashCombine hash;
+ seed = hash(seed, s.stage());
+ if (!s.d->shaders.isEmpty()) {
+ seed = hash(seed, s.d->shaders.firstKey());
+ seed = hash(seed, std::as_const(s.d->shaders).first());
+ }
+ }
return seed;
}
@@ -564,6 +832,8 @@ size_t qHash(const QShaderVersion &s, size_t seed) noexcept
#endif
/*!
+ \return true if \a lhs is smaller than \a rhs.
+
Establishes a sorting order between the two QShaderVersion \a lhs and \a rhs.
\relates QShaderVersion
@@ -580,11 +850,10 @@ bool operator<(const QShaderVersion &lhs, const QShaderVersion &rhs) noexcept
}
/*!
- \internal
\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.
+ Returns \c false if the values in the two QShaderVersion objects \a lhs
+ and \a rhs are equal; otherwise returns \c true.
\relates QShaderVersion
*/
@@ -601,6 +870,8 @@ bool operator==(const QShaderKey &lhs, const QShaderKey &rhs) noexcept
}
/*!
+ \return true if \a lhs is smaller than \a rhs.
+
Establishes a sorting order between the two keys \a lhs and \a rhs.
\relates QShaderKey
@@ -623,11 +894,10 @@ bool operator<(const QShaderKey &lhs, const QShaderKey &rhs) noexcept
}
/*!
- \internal
\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.
+ Returns \c false if the values in the two QShaderKey objects \a lhs
+ and \a rhs are equal; otherwise returns \c true.
\relates QShaderKey
*/
@@ -657,11 +927,10 @@ bool operator==(const QShaderCode &lhs, const QShaderCode &rhs) noexcept
}
/*!
- \internal
\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.
+ Returns \c false if the values in the two QShaderCode objects \a lhs
+ and \a rhs are equal; otherwise returns \c true.
\relates QShaderCode
*/
@@ -682,11 +951,15 @@ 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()
- << ')';
+ if (d) {
+ dbg.nospace() << "QShader("
+ << "stage=" << d->stage
+ << " shaders=" << d->shaders.keys()
+ << " desc.isValid=" << d->desc.isValid()
+ << ')';
+ } else {
+ dbg.nospace() << "QShader()";
+ }
return dbg;
}
@@ -711,7 +984,7 @@ QDebug operator<<(QDebug dbg, const QShaderVersion &v)
/*!
\typedef QShader::NativeResourceBindingMap
- Synonym for QHash<int, QPair<int, int>>.
+ Synonym for QMap<int, QPair<int, int>>.
The resource binding model QRhi assumes is based on SPIR-V. This means that
uniform buffers, storage buffers, combined image samplers, and storage
@@ -750,6 +1023,9 @@ QDebug operator<<(QDebug dbg, const QShaderVersion &v)
*/
QShader::NativeResourceBindingMap QShader::nativeResourceBindingMap(const QShaderKey &key) const
{
+ if (!d)
+ return {};
+
auto it = d->bindings.constFind(key);
if (it == d->bindings.cend())
return {};
@@ -773,6 +1049,9 @@ void QShader::setResourceBindingMap(const QShaderKey &key, const NativeResourceB
*/
void QShader::removeResourceBindingMap(const QShaderKey &key)
{
+ if (!d)
+ return;
+
auto it = d->bindings.find(key);
if (it == d->bindings.end())
return;
@@ -789,6 +1068,8 @@ void QShader::removeResourceBindingMap(const QShaderKey &key)
/*!
\struct QShader::SeparateToCombinedImageSamplerMapping
+ \inmodule QtGui
+ \brief Mapping metadata for sampler uniforms.
Describes a mapping from a traditional combined image sampler uniform to
binding points for a separate texture and sampler.
@@ -798,15 +1079,33 @@ void QShader::removeResourceBindingMap(const QShaderKey &key)
contains a \c sampler2D (or sampler3D, etc.) uniform with the name of
\c{_54} which corresponds to two separate resource bindings (\c 1 and \c 2)
in the original shader.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QShader
+ for details.
*/
/*!
+ \variable QShader::SeparateToCombinedImageSamplerMapping::combinedSamplerName
+*/
+
+/*!
+ \variable QShader::SeparateToCombinedImageSamplerMapping::textureBinding
+*/
+
+/*!
+ \variable QShader::SeparateToCombinedImageSamplerMapping::samplerBinding
+*/
+
+/*!
\return the combined image sampler mapping list for \a key, or an empty
list if there is no data available for \a key, for example because such a
mapping is not applicable for the shading language.
*/
QShader::SeparateToCombinedImageSamplerMappingList QShader::separateToCombinedImageSamplerMappingList(const QShaderKey &key) const
{
+ if (!d)
+ return {};
+
auto it = d->combinedImageMap.constFind(key);
if (it == d->combinedImageMap.cend())
return {};
@@ -831,6 +1130,9 @@ void QShader::setSeparateToCombinedImageSamplerMappingList(const QShaderKey &key
*/
void QShader::removeSeparateToCombinedImageSamplerMappingList(const QShaderKey &key)
{
+ if (!d)
+ return;
+
auto it = d->combinedImageMap.find(key);
if (it == d->combinedImageMap.end())
return;
@@ -839,4 +1141,81 @@ void QShader::removeSeparateToCombinedImageSamplerMappingList(const QShaderKey &
d->combinedImageMap.erase(it);
}
+/*!
+ \struct QShader::NativeShaderInfo
+ \inmodule QtGui
+ \brief Additional metadata about the native shader code.
+
+ Describes information about the native shader code, if applicable. This
+ becomes relevant with certain shader languages for certain shader stages,
+ in case the translation from SPIR-V involves the introduction of
+ additional, "magic" inputs, outputs, or resources in the generated shader.
+ Such additions may be dependent on the original source code (i.e. the usage
+ of various GLSL language constructs or built-ins), and therefore it needs
+ to be indicated in a dynamic manner if certain features got added to the
+ generated shader code.
+
+ As an example, consider a tessellation control shader with a per-patch (not
+ per-vertex) output variable. This is translated to a Metal compute shader
+ outputting (among others) into an spvPatchOut buffer. But this buffer would
+ not be present at all if per-patch output variables were not used. The fact
+ that the shader code relies on such a buffer present can be indicated by
+ the data in this struct.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QShader
+ for details.
+ */
+
+/*!
+ \variable QShader::NativeShaderInfo::flags
+*/
+
+/*!
+ \variable QShader::NativeShaderInfo::extraBufferBindings
+*/
+
+/*!
+ \return the native shader info struct for \a key, or an empty object if
+ there is no data available for \a key, for example because such a mapping
+ is not applicable for the shading language or the shader stage.
+ */
+QShader::NativeShaderInfo QShader::nativeShaderInfo(const QShaderKey &key) const
+{
+ if (!d)
+ return {};
+
+ auto it = d->nativeShaderInfoMap.constFind(key);
+ if (it == d->nativeShaderInfoMap.cend())
+ return {};
+
+ return it.value();
+}
+
+/*!
+ Stores the given native shader \a info associated with \a key.
+
+ \sa nativeShaderInfo()
+ */
+void QShader::setNativeShaderInfo(const QShaderKey &key, const NativeShaderInfo &info)
+{
+ detach();
+ d->nativeShaderInfoMap[key] = info;
+}
+
+/*!
+ Removes the native shader information for \a key.
+ */
+void QShader::removeNativeShaderInfo(const QShaderKey &key)
+{
+ if (!d)
+ return;
+
+ auto it = d->nativeShaderInfoMap.find(key);
+ if (it == d->nativeShaderInfoMap.end())
+ return;
+
+ detach();
+ d->nativeShaderInfoMap.erase(it);
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qshader.h b/src/gui/rhi/qshader.h
new file mode 100644
index 0000000000..2465081366
--- /dev/null
+++ b/src/gui/rhi/qshader.h
@@ -0,0 +1,241 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSHADER_H
+#define QSHADER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is part of the RHI API, with limited compatibility guarantees.
+// Usage of this API may make your code source and binary incompatible with
+// future versions of Qt.
+//
+
+#include <QtGui/qtguiglobal.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qmap.h>
+#include <rhi/qshaderdescription.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QShaderPrivate;
+class QShaderKey;
+
+#ifdef Q_OS_INTEGRITY
+ class QShaderVersion;
+ size_t qHash(const QShaderVersion &, size_t = 0) noexcept;
+#endif
+
+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_RELOCATABLE_TYPE);
+
+class QShaderCode;
+Q_GUI_EXPORT size_t qHash(const QShaderCode &, size_t = 0) noexcept;
+
+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:
+ friend Q_GUI_EXPORT size_t qHash(const QShaderCode &, size_t) noexcept;
+
+ QByteArray m_shader;
+ QByteArray m_entryPoint;
+};
+
+Q_DECLARE_TYPEINFO(QShaderCode, Q_RELOCATABLE_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
+ WgslShader
+ };
+
+ enum Variant {
+ StandardShader = 0,
+ BatchableVertexShader,
+ UInt16IndexedVertexAsComputeShader,
+ UInt32IndexedVertexAsComputeShader,
+ NonIndexedVertexAsComputeShader
+ };
+
+ enum class SerializedFormatVersion {
+ Latest = 0,
+ Qt_6_5,
+ Qt_6_4
+ };
+
+ QShader();
+ QShader(const QShader &other);
+ QShader &operator=(const QShader &other);
+ QShader(QShader &&other) noexcept : d(std::exchange(other.d, nullptr)) {}
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QShader)
+ ~QShader();
+
+ void swap(QShader &other) noexcept { qt_ptr_swap(d, other.d); }
+ void detach();
+
+ bool isValid() const;
+
+ Stage stage() const;
+ void setStage(Stage stage);
+
+ QShaderDescription description() const;
+ void setDescription(const QShaderDescription &desc);
+
+ QList<QShaderKey> availableShaders() const;
+ QShaderCode shader(const QShaderKey &key) const;
+ void setShader(const QShaderKey &key, const QShaderCode &shader);
+ void removeShader(const QShaderKey &key);
+
+ QByteArray serialized(SerializedFormatVersion version = SerializedFormatVersion::Latest) const;
+ static QShader fromSerialized(const QByteArray &data);
+
+ using NativeResourceBindingMap = QMap<int, QPair<int, int> >; // binding -> native_binding[, native_binding]
+ NativeResourceBindingMap nativeResourceBindingMap(const QShaderKey &key) const;
+ void setResourceBindingMap(const QShaderKey &key, const NativeResourceBindingMap &map);
+ void removeResourceBindingMap(const QShaderKey &key);
+
+ struct SeparateToCombinedImageSamplerMapping {
+ QByteArray combinedSamplerName;
+ int textureBinding;
+ int samplerBinding;
+ };
+ using SeparateToCombinedImageSamplerMappingList = QList<SeparateToCombinedImageSamplerMapping>;
+ SeparateToCombinedImageSamplerMappingList separateToCombinedImageSamplerMappingList(const QShaderKey &key) const;
+ void setSeparateToCombinedImageSamplerMappingList(const QShaderKey &key,
+ const SeparateToCombinedImageSamplerMappingList &list);
+ void removeSeparateToCombinedImageSamplerMappingList(const QShaderKey &key);
+
+ struct NativeShaderInfo {
+ int flags = 0;
+ QMap<int, int> extraBufferBindings;
+ };
+ NativeShaderInfo nativeShaderInfo(const QShaderKey &key) const;
+ void setNativeShaderInfo(const QShaderKey &key, const NativeShaderInfo &info);
+ void removeNativeShaderInfo(const QShaderKey &key);
+
+private:
+ QShaderPrivate *d;
+ friend struct QShaderPrivate;
+ friend Q_GUI_EXPORT bool operator==(const QShader &, const QShader &) noexcept;
+ friend Q_GUI_EXPORT size_t qHash(const QShader &, size_t) noexcept;
+#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_RELOCATABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QShader &lhs, const QShader &rhs) noexcept;
+Q_GUI_EXPORT size_t qHash(const QShader &s, size_t seed = 0) noexcept;
+
+inline bool operator!=(const QShader &lhs, const QShader &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+Q_GUI_EXPORT bool operator==(const QShaderVersion &lhs, const QShaderVersion &rhs) noexcept;
+Q_GUI_EXPORT bool operator<(const QShaderVersion &lhs, const QShaderVersion &rhs) noexcept;
+Q_GUI_EXPORT bool operator==(const QShaderKey &lhs, const QShaderKey &rhs) noexcept;
+Q_GUI_EXPORT bool operator<(const QShaderKey &lhs, const QShaderKey &rhs) noexcept;
+Q_GUI_EXPORT bool operator==(const QShaderCode &lhs, const QShaderCode &rhs) noexcept;
+
+inline bool operator!=(const QShaderVersion &lhs, const QShaderVersion &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderKey &lhs, const QShaderKey &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderCode &lhs, const QShaderCode &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+Q_GUI_EXPORT size_t qHash(const QShaderKey &k, size_t seed = 0) noexcept;
+
+#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.h b/src/gui/rhi/qshader_p.h
index c6ef338bfa..f77bcb1259 100644
--- a/src/gui/rhi/qshader_p.h
+++ b/src/gui/rhi/qshader_p.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSHADER_P_H
@@ -15,206 +15,77 @@
// We mean it.
//
-#include <QtGui/qtguiglobal.h>
-#include <QtCore/qhash.h>
-#include <private/qshaderdescription_p.h>
+#include <rhi/qshader.h>
+#include <QtCore/QAtomicInt>
+#include <QtCore/QMap>
+#include <QtCore/QDebug>
QT_BEGIN_NAMESPACE
-struct QShaderPrivate;
-class QShaderKey;
-
-#ifdef Q_OS_INTEGRITY
- class QShaderVersion;
- size_t qHash(const QShaderVersion &, size_t = 0) noexcept;
-#endif
-
-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_RELOCATABLE_TYPE);
-
-class QShaderCode;
-Q_GUI_EXPORT size_t qHash(const QShaderCode &, size_t = 0) noexcept;
-
-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:
- friend Q_GUI_EXPORT size_t qHash(const QShaderCode &, size_t) noexcept;
-
- QByteArray m_shader;
- QByteArray m_entryPoint;
-};
-
-Q_DECLARE_TYPEINFO(QShaderCode, Q_RELOCATABLE_TYPE);
-
-class Q_GUI_EXPORT QShader
+struct Q_GUI_EXPORT QShaderPrivate
{
-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);
-
- QList<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);
-
- using NativeResourceBindingMap = QHash<int, QPair<int, int> >; // binding -> native_binding[, native_binding]
- NativeResourceBindingMap nativeResourceBindingMap(const QShaderKey &key) const;
- void setResourceBindingMap(const QShaderKey &key, const NativeResourceBindingMap &map);
- void removeResourceBindingMap(const QShaderKey &key);
-
- struct SeparateToCombinedImageSamplerMapping {
- QByteArray combinedSamplerName;
- int textureBinding;
- int samplerBinding;
+ static const int QSB_VERSION = 9;
+ static const int QSB_VERSION_WITHOUT_INPUT_OUTPUT_INTERFACE_BLOCKS = 8;
+ static const int QSB_VERSION_WITHOUT_EXTENDED_STORAGE_BUFFER_INFO = 7;
+ static const int QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO = 6;
+ static const int QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS = 5;
+ static const int QSB_VERSION_WITHOUT_VAR_ARRAYDIMS = 4;
+ static const int QSB_VERSION_WITH_CBOR = 3;
+ static const int QSB_VERSION_WITH_BINARY_JSON = 2;
+ static const int QSB_VERSION_WITHOUT_BINDINGS = 1;
+
+ enum MslNativeShaderInfoExtraBufferBindings {
+ MslTessVertIndicesBufferBinding = 0,
+ MslTessVertTescOutputBufferBinding,
+ MslTessTescTessLevelBufferBinding,
+ MslTessTescPatchOutputBufferBinding,
+ MslTessTescParamsBufferBinding,
+ MslTessTescInputBufferBinding,
+ MslBufferSizeBufferBinding,
+ MslMultiViewMaskBufferBinding
};
- using SeparateToCombinedImageSamplerMappingList = QList<SeparateToCombinedImageSamplerMapping>;
- SeparateToCombinedImageSamplerMappingList separateToCombinedImageSamplerMappingList(const QShaderKey &key) const;
- void setSeparateToCombinedImageSamplerMappingList(const QShaderKey &key,
- const SeparateToCombinedImageSamplerMappingList &list);
- void removeSeparateToCombinedImageSamplerMappingList(const QShaderKey &key);
-
-private:
- QShaderPrivate *d;
- friend struct QShaderPrivate;
- friend Q_GUI_EXPORT bool operator==(const QShader &, const QShader &) noexcept;
- friend Q_GUI_EXPORT size_t qHash(const QShader &, size_t) noexcept;
-#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;
+ QShaderPrivate()
+ : ref(1)
+ {
+ }
+
+ QShaderPrivate(const QShaderPrivate &other)
+ : ref(1),
+ qsbVersion(other.qsbVersion),
+ stage(other.stage),
+ desc(other.desc),
+ shaders(other.shaders),
+ bindings(other.bindings),
+ combinedImageMap(other.combinedImageMap),
+ nativeShaderInfoMap(other.nativeShaderInfoMap)
+ {
+ }
+
+ static QShaderPrivate *get(QShader *s) { return s->d; }
+ static const QShaderPrivate *get(const QShader *s) { return s->d; }
+ static int qtQsbVersion(QShader::SerializedFormatVersion qtVersion) {
+ switch (qtVersion) {
+ case QShader::SerializedFormatVersion::Qt_6_4:
+ return (QShaderPrivate::QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS + 1);
+ case QShader::SerializedFormatVersion::Qt_6_5:
+ return (QShaderPrivate::QSB_VERSION_WITHOUT_EXTENDED_STORAGE_BUFFER_INFO + 1);
+ default:
+ return QShaderPrivate::QSB_VERSION;
+ }
+ }
+
+ QAtomicInt ref;
+ int qsbVersion = QSB_VERSION;
+ QShader::Stage stage = QShader::VertexStage;
+ QShaderDescription desc;
+ // QMap not QHash because we need to be able to iterate based on sorted keys
+ QMap<QShaderKey, QShaderCode> shaders;
+ QMap<QShaderKey, QShader::NativeResourceBindingMap> bindings;
+ QMap<QShaderKey, QShader::SeparateToCombinedImageSamplerMappingList> combinedImageMap;
+ QMap<QShaderKey, QShader::NativeShaderInfo> nativeShaderInfoMap;
};
-Q_DECLARE_TYPEINFO(QShaderKey, Q_RELOCATABLE_TYPE);
-
-Q_GUI_EXPORT bool operator==(const QShader &lhs, const QShader &rhs) noexcept;
-Q_GUI_EXPORT size_t qHash(const QShader &s, size_t seed = 0) noexcept;
-
-inline bool operator!=(const QShader &lhs, const QShader &rhs) noexcept
-{
- return !(lhs == rhs);
-}
-
-Q_GUI_EXPORT bool operator==(const QShaderVersion &lhs, const QShaderVersion &rhs) noexcept;
-Q_GUI_EXPORT bool operator<(const QShaderVersion &lhs, const QShaderVersion &rhs) noexcept;
-Q_GUI_EXPORT bool operator==(const QShaderKey &lhs, const QShaderKey &rhs) noexcept;
-Q_GUI_EXPORT bool operator<(const QShaderKey &lhs, const QShaderKey &rhs) noexcept;
-Q_GUI_EXPORT bool operator==(const QShaderCode &lhs, const QShaderCode &rhs) noexcept;
-
-inline bool operator!=(const QShaderVersion &lhs, const QShaderVersion &rhs) noexcept
-{
- return !(lhs == rhs);
-}
-
-inline bool operator!=(const QShaderKey &lhs, const QShaderKey &rhs) noexcept
-{
- return !(lhs == rhs);
-}
-
-inline bool operator!=(const QShaderCode &lhs, const QShaderCode &rhs) noexcept
-{
- return !(lhs == rhs);
-}
-
-Q_GUI_EXPORT size_t qHash(const QShaderKey &k, size_t seed = 0) noexcept;
-
-#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
deleted file mode 100644
index e9d1e31aaf..0000000000
--- a/src/gui/rhi/qshader_p_p.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#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/QMap>
-#include <QtCore/QDebug>
-
-QT_BEGIN_NAMESPACE
-
-struct Q_GUI_EXPORT QShaderPrivate
-{
- static const int QSB_VERSION = 6;
- static const int QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS = 5;
- static const int QSB_VERSION_WITHOUT_VAR_ARRAYDIMS = 4;
- static const int QSB_VERSION_WITH_CBOR = 3;
- static const int QSB_VERSION_WITH_BINARY_JSON = 2;
- static const int QSB_VERSION_WITHOUT_BINDINGS = 1;
-
- QShaderPrivate()
- : ref(1)
- {
- }
-
- QShaderPrivate(const QShaderPrivate &other)
- : ref(1),
- qsbVersion(other.qsbVersion),
- stage(other.stage),
- desc(other.desc),
- shaders(other.shaders),
- bindings(other.bindings),
- combinedImageMap(other.combinedImageMap)
- {
- }
-
- static QShaderPrivate *get(QShader *s) { return s->d; }
- static const QShaderPrivate *get(const QShader *s) { return s->d; }
-
- QAtomicInt ref;
- int qsbVersion = QSB_VERSION;
- QShader::Stage stage = QShader::VertexStage;
- QShaderDescription desc;
- // QMap not QHash because we need to be able to iterate based on sorted keys
- QMap<QShaderKey, QShaderCode> shaders;
- QMap<QShaderKey, QShader::NativeResourceBindingMap> bindings;
- QMap<QShaderKey, QShader::SeparateToCombinedImageSamplerMappingList> combinedImageMap;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/gui/rhi/qshaderdescription.cpp b/src/gui/rhi/qshaderdescription.cpp
index d55caed210..f64daf02ef 100644
--- a/src/gui/rhi/qshaderdescription.cpp
+++ b/src/gui/rhi/qshaderdescription.cpp
@@ -1,8 +1,8 @@
-// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qshaderdescription_p_p.h"
-#include "qshader_p_p.h"
+#include "qshaderdescription_p.h"
+#include "qshader_p.h"
#include <QDebug>
#include <QDataStream>
#include <QJsonObject>
@@ -12,11 +12,22 @@ QT_BEGIN_NAMESPACE
/*!
\class QShaderDescription
- \internal
+ \ingroup painting-3D
\inmodule QtGui
+ \since 6.6
\brief Describes the interface of a shader.
+ \warning The QRhi family of classes in the Qt Gui module, including QShader
+ and QShaderDescription, offer limited compatibility guarantees. There are
+ no source or binary compatibility guarantees for these classes, meaning the
+ API is only guaranteed to work with the Qt version the application was
+ developed against. Source incompatible changes are however aimed to be kept
+ at a minimum and will only be made in minor releases (6.7, 6.8, and so on).
+ To use these classes in an application, link to
+ \c{Qt::GuiPrivate} (if using CMake), and include the headers with the \c
+ rhi prefix, for example \c{#include <rhi/qshaderdescription.h>}.
+
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
@@ -51,8 +62,6 @@ QT_BEGIN_NAMESPACE
float opacity;
} ubuf;
- out gl_PerVertex { vec4 gl_Position; };
-
void main()
{
v_color = color;
@@ -200,28 +209,179 @@ QT_BEGIN_NAMESPACE
\value ImageRect
\value ImageBuffer
\value Struct
+ \value Half
+ \value Half2
+ \value Half3
+ \value Half4
*/
/*!
- \class QShaderDescription::InOutVariable
- \internal
+ \enum QShaderDescription::ImageFormat
+ Image format.
+
+ \value ImageFormatUnknown
+ \value ImageFormatRgba32f
+ \value ImageFormatRgba16f
+ \value ImageFormatR32f
+ \value ImageFormatRgba8
+ \value ImageFormatRgba8Snorm
+ \value ImageFormatRg32f
+ \value ImageFormatRg16f
+ \value ImageFormatR11fG11fB10f
+ \value ImageFormatR16f
+ \value ImageFormatRgba16
+ \value ImageFormatRgb10A2
+ \value ImageFormatRg16
+ \value ImageFormatRg8
+ \value ImageFormatR16
+ \value ImageFormatR8
+ \value ImageFormatRgba16Snorm
+ \value ImageFormatRg16Snorm
+ \value ImageFormatRg8Snorm
+ \value ImageFormatR16Snorm
+ \value ImageFormatR8Snorm
+ \value ImageFormatRgba32i
+ \value ImageFormatRgba16i
+ \value ImageFormatRgba8i
+ \value ImageFormatR32i
+ \value ImageFormatRg32i
+ \value ImageFormatRg16i
+ \value ImageFormatRg8i
+ \value ImageFormatR16i
+ \value ImageFormatR8i
+ \value ImageFormatRgba32ui
+ \value ImageFormatRgba16ui
+ \value ImageFormatRgba8ui
+ \value ImageFormatR32ui
+ \value ImageFormatRgb10a2ui
+ \value ImageFormatRg32ui
+ \value ImageFormatRg16ui
+ \value ImageFormatRg8ui
+ \value ImageFormatR16ui
+ \value ImageFormatR8ui
+ */
+
+/*!
+ \enum QShaderDescription::ImageFlag
+ Image flags.
+
+ \value ReadOnlyImage
+ \value WriteOnlyImage
+ */
+
+/*!
+ \enum QShaderDescription::QualifierFlag
+ Qualifier flags.
+
+ \value QualifierReadOnly
+ \value QualifierWriteOnly
+ \value QualifierCoherent
+ \value QualifierVolatile
+ \value QualifierRestrict
+ */
+
+/*!
+ \struct QShaderDescription::InOutVariable
\inmodule QtGui
+ \since 6.6
\brief Describes an input or output variable in the shader.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QShaderDescription
+ for details.
*/
/*!
- \class QShaderDescription::BlockVariable
- \internal
+ \variable QShaderDescription::InOutVariable::name
+ */
+
+/*!
+ \variable QShaderDescription::InOutVariable::type
+ */
+
+/*!
+ \variable QShaderDescription::InOutVariable::location
+ */
+
+/*!
+ \variable QShaderDescription::InOutVariable::binding
+ */
+
+/*!
+ \variable QShaderDescription::InOutVariable::descriptorSet
+ */
+
+/*!
+ \variable QShaderDescription::InOutVariable::imageFormat
+ */
+
+/*!
+ \variable QShaderDescription::InOutVariable::imageFlags
+ */
+
+/*!
+ \variable QShaderDescription::InOutVariable::arrayDims
+ */
+
+/*!
+ \variable QShaderDescription::InOutVariable::perPatch
+ */
+
+/*!
+ \variable QShaderDescription::InOutVariable::structMembers
+ */
+
+/*!
+ \struct QShaderDescription::BlockVariable
\inmodule QtGui
+ \since 6.6
\brief Describes a member of a uniform or push constant block.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QShaderDescription
+ for details.
*/
/*!
- \class QShaderDescription::UniformBlock
- \internal
+ \variable QShaderDescription::BlockVariable::name
+ */
+
+/*!
+ \variable QShaderDescription::BlockVariable::type
+ */
+
+/*!
+ \variable QShaderDescription::BlockVariable::offset
+ */
+
+/*!
+ \variable QShaderDescription::BlockVariable::size
+ */
+
+/*!
+ \variable QShaderDescription::BlockVariable::arrayDims
+ */
+
+/*!
+ \variable QShaderDescription::BlockVariable::arrayStride
+ */
+
+/*!
+ \variable QShaderDescription::BlockVariable::matrixStride
+ */
+
+/*!
+ \variable QShaderDescription::BlockVariable::matrixIsRowMajor
+ */
+
+/*!
+ \variable QShaderDescription::BlockVariable::structMembers
+ */
+
+/*!
+ \struct QShaderDescription::UniformBlock
\inmodule QtGui
+ \since 6.6
\brief Describes a uniform block.
@@ -229,22 +389,157 @@ QT_BEGIN_NAMESPACE
(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.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QShaderDescription
+ for details.
*/
/*!
- \class QShaderDescription::PushConstantBlock
- \internal
+ \variable QShaderDescription::UniformBlock::blockName
+ */
+
+/*!
+ \variable QShaderDescription::UniformBlock::structName
+ */
+
+/*!
+ \variable QShaderDescription::UniformBlock::size
+ */
+
+/*!
+ \variable QShaderDescription::UniformBlock::binding
+ */
+
+/*!
+ \variable QShaderDescription::UniformBlock::descriptorSet
+ */
+
+/*!
+ \variable QShaderDescription::UniformBlock::members
+ */
+
+/*!
+ \struct QShaderDescription::PushConstantBlock
\inmodule QtGui
+ \since 6.6
\brief Describes a push constant block.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QShaderDescription
+ for details.
*/
/*!
- \class QShaderDescription::StorageBlock
- \internal
+ \variable QShaderDescription::PushConstantBlock::name
+ */
+
+/*!
+ \variable QShaderDescription::PushConstantBlock::size
+ */
+
+/*!
+ \variable QShaderDescription::PushConstantBlock::members
+ */
+
+/*!
+ \struct QShaderDescription::StorageBlock
\inmodule QtGui
+ \since 6.6
\brief Describes a shader storage block.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QShaderDescription
+ for details.
+ */
+
+/*!
+ \variable QShaderDescription::StorageBlock::blockName
+ */
+
+/*!
+ \variable QShaderDescription::StorageBlock::instanceName
+ */
+
+/*!
+ \variable QShaderDescription::StorageBlock::knownSize
+ */
+
+/*!
+ \variable QShaderDescription::StorageBlock::binding
+ */
+
+/*!
+ \variable QShaderDescription::StorageBlock::descriptorSet
+ */
+
+/*!
+ \variable QShaderDescription::StorageBlock::members
+ */
+
+/*!
+ \variable QShaderDescription::StorageBlock::runtimeArrayStride
+ */
+
+/*!
+ \variable QShaderDescription::StorageBlock::qualifierFlags
+ */
+
+/*!
+ \struct QShaderDescription::BuiltinVariable
+ \inmodule QtGui
+ \since 6.6
+
+ \brief Describes a built-in variable.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QShaderDescription
+ for details.
+ */
+
+/*!
+ \variable QShaderDescription::BuiltinVariable::type
+ */
+
+/*!
+ \variable QShaderDescription::BuiltinVariable::varType
+ */
+
+/*!
+ \variable QShaderDescription::BuiltinVariable::arrayDims
+ */
+
+/*!
+ \enum QShaderDescription::BuiltinType
+ Built-in variable type.
+
+ \value PositionBuiltin
+ \value PointSizeBuiltin
+ \value ClipDistanceBuiltin
+ \value CullDistanceBuiltin
+ \value VertexIdBuiltin
+ \value InstanceIdBuiltin
+ \value PrimitiveIdBuiltin
+ \value InvocationIdBuiltin
+ \value LayerBuiltin
+ \value ViewportIndexBuiltin
+ \value TessLevelOuterBuiltin
+ \value TessLevelInnerBuiltin
+ \value TessCoordBuiltin
+ \value PatchVerticesBuiltin
+ \value FragCoordBuiltin
+ \value PointCoordBuiltin
+ \value FrontFacingBuiltin
+ \value SampleIdBuiltin
+ \value SamplePositionBuiltin
+ \value SampleMaskBuiltin
+ \value FragDepthBuiltin
+ \value NumWorkGroupsBuiltin
+ \value WorkgroupSizeBuiltin
+ \value WorkgroupIdBuiltin
+ \value LocalInvocationIdBuiltin
+ \value GlobalInvocationIdBuiltin
+ \value LocalInvocationIndexBuiltin
+ \value VertexIndexBuiltin
+ \value InstanceIndexBuiltin
*/
/*!
@@ -267,7 +562,7 @@ void QShaderDescription::detach()
}
/*!
- \internal
+ Constructs a copy of \a other.
*/
QShaderDescription::QShaderDescription(const QShaderDescription &other)
: d(other.d)
@@ -276,7 +571,7 @@ QShaderDescription::QShaderDescription(const QShaderDescription &other)
}
/*!
- \internal
+ Assigns \a other to this object.
*/
QShaderDescription &QShaderDescription::operator=(const QShaderDescription &other)
{
@@ -302,7 +597,8 @@ 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()
- || !d->separateImages.isEmpty() || !d->separateSamplers.isEmpty();
+ || !d->separateImages.isEmpty() || !d->separateSamplers.isEmpty()
+ || !d->inBuiltins.isEmpty() || !d->outBuiltins.isEmpty();
}
/*!
@@ -318,13 +614,14 @@ QByteArray QShaderDescription::toJson() const
}
/*!
- Serializes this QShaderDescription to \a stream.
+ Serializes this QShaderDescription to \a stream. \a version specifies
+ the qsb version.
\sa deserialize(), toJson()
*/
-void QShaderDescription::serialize(QDataStream *stream) const
+void QShaderDescription::serialize(QDataStream *stream, int version) const
{
- d->writeToStream(stream);
+ d->writeToStream(stream, version);
}
/*!
@@ -402,6 +699,7 @@ QList<QShaderDescription::PushConstantBlock> QShaderDescription::pushConstantBlo
"blockName": "StuffSsbo",
"instanceName": "buf",
"knownSize": 16,
+ "runtimeArrayStride": 16
"members": [
{
"name": "whatever",
@@ -439,7 +737,10 @@ QList<QShaderDescription::PushConstantBlock> QShaderDescription::pushConstantBlo
\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.
+ excludes the size of the last member since that will only be known at run time. The
+ stride in bytes between array items for a last member with undefined array size is
+ \c runtimeArrayStride. This value is determined according to the specified buffer
+ memory layout standard (std140, std430) rules.
\note SSBOs are not available with some graphics APIs, such as, OpenGL 2.x or
OpenGL ES older than 3.1.
@@ -514,7 +815,26 @@ QList<QShaderDescription::InOutVariable> QShaderDescription::storageImages() con
}
/*!
- Returns the local size of a compute shader.
+ \return the list of active builtins used as input. For example, a
+ tessellation evaluation shader reading the value of gl_TessCoord and
+ gl_Position will have TessCoordBuiltin and PositionBuiltin listed here.
+ */
+QVector<QShaderDescription::BuiltinVariable> QShaderDescription::inputBuiltinVariables() const
+{
+ return d->inBuiltins;
+}
+
+/*!
+ \return the list of active built-in variables used as input. For example, a
+ vertex shader will very often have PositionBuiltin as an output built-in.
+ */
+QVector<QShaderDescription::BuiltinVariable> QShaderDescription::outputBuiltinVariables() const
+{
+ return d->outBuiltins;
+}
+
+/*!
+ \return the local size of a compute shader.
For example, for a compute shader with the following declaration the
function returns { 256, 16, 1}.
@@ -528,6 +848,101 @@ std::array<uint, 3> QShaderDescription::computeShaderLocalSize() const
return d->localSize;
}
+/*!
+ \return the number of output vertices.
+
+ For example, for a tessellation control shader with the following
+ declaration the function returns 3.
+
+ \badcode
+ layout(vertices = 3) out;
+ \endcode
+ */
+uint QShaderDescription::tessellationOutputVertexCount() const
+{
+ return d->tessOutVertCount;
+}
+
+/*!
+ \enum QShaderDescription::TessellationMode
+
+ \value UnknownTessellationMode
+ \value TrianglesTessellationMode
+ \value QuadTessellationMode
+ \value IsolineTessellationMode
+ */
+
+/*!
+ \return the tessellation execution mode for a tessellation control or
+ evaluation shader.
+
+ When not set, the returned value is UnknownTessellationMode.
+
+ For example, for a tessellation evaluation shader with the following
+ declaration the function returns TrianglesTessellationMode.
+
+ \badcode
+ layout(triangles) in;
+ \endcode
+ */
+QShaderDescription::TessellationMode QShaderDescription::tessellationMode() const
+{
+ return d->tessMode;
+}
+
+/*!
+ \enum QShaderDescription::TessellationWindingOrder
+
+ \value UnknownTessellationWindingOrder
+ \value CwTessellationWindingOrder
+ \value CcwTessellationWindingOrder
+ */
+
+/*!
+ \return the tessellation winding order for a tessellation control or
+ evaluation shader.
+
+ When not set, the returned value is UnknownTessellationWindingOrder.
+
+ For example, for a tessellation evaluation shader with the following
+ declaration the function returns CcwTessellationWindingOrder.
+
+ \badcode
+ layout(triangles, fractional_odd_spacing, ccw) in;
+ \endcode
+ */
+QShaderDescription::TessellationWindingOrder QShaderDescription::tessellationWindingOrder() const
+{
+ return d->tessWind;
+}
+
+/*!
+ \enum QShaderDescription::TessellationPartitioning
+
+ \value UnknownTessellationPartitioning
+ \value EqualTessellationPartitioning
+ \value FractionalEvenTessellationPartitioning
+ \value FractionalOddTessellationPartitioning
+ */
+
+/*!
+ \return the tessellation partitioning mode for a tessellation control or
+ evaluation shader.
+
+ When not set, the returned value is UnknownTessellationPartitioning.
+
+ For example, for a tessellation evaluation shader with the following
+ declaration the function returns FractionalOddTessellationPartitioning.
+
+ \badcode
+ layout(triangles, fractional_odd_spacing, ccw) in;
+ \endcode
+ */
+QShaderDescription::TessellationPartitioning QShaderDescription::tessellationPartitioning() const
+{
+ return d->tessPart;
+}
+
static const struct TypeTab {
const char k[20];
QShaderDescription::VariableType v;
@@ -604,10 +1019,14 @@ static const struct TypeTab {
{ "image3DArray", QShaderDescription::Image3DArray },
{ "imageCubeArray", QShaderDescription::ImageCubeArray },
{ "imageRect", QShaderDescription::ImageRect },
- { "imageBuffer", QShaderDescription::ImageBuffer }
-};
+ { "imageBuffer", QShaderDescription::ImageBuffer },
-static QLatin1StringView typeStr(const QShaderDescription::VariableType &t)
+ { "half", QShaderDescription::Half },
+ { "half2", QShaderDescription::Half2 },
+ { "half3", QShaderDescription::Half3 },
+ { "half4", QShaderDescription::Half4 } };
+
+static QLatin1StringView typeStr(QShaderDescription::VariableType t)
{
for (size_t i = 0; i < sizeof(typeTab) / sizeof(TypeTab); ++i) {
if (typeTab[i].v == t)
@@ -662,7 +1081,7 @@ static const struct ImageFormatTab {
{ "r8ui", QShaderDescription::ImageFormatR8ui }
};
-static QLatin1StringView imageFormatStr(const QShaderDescription::ImageFormat &f)
+static QLatin1StringView imageFormatStr(QShaderDescription::ImageFormat f)
{
for (size_t i = 0; i < sizeof(imageFormatTab) / sizeof(ImageFormatTab); ++i) {
if (imageFormatTab[i].v == f)
@@ -671,6 +1090,106 @@ static QLatin1StringView imageFormatStr(const QShaderDescription::ImageFormat &f
return {};
}
+static const struct BuiltinTypeTab {
+ const char k[21];
+ QShaderDescription::BuiltinType v;
+} builtinTypeTab[] = {
+ { "Position", QShaderDescription::PositionBuiltin },
+ { "PointSize", QShaderDescription::PointSizeBuiltin },
+ { "ClipDistance", QShaderDescription::ClipDistanceBuiltin },
+ { "CullDistance", QShaderDescription::CullDistanceBuiltin },
+ { "VertexId", QShaderDescription::VertexIdBuiltin },
+ { "InstanceId", QShaderDescription::InstanceIdBuiltin },
+ { "PrimitiveId", QShaderDescription::PrimitiveIdBuiltin },
+ { "InvocationId", QShaderDescription::InvocationIdBuiltin },
+ { "Layer", QShaderDescription::LayerBuiltin },
+ { "ViewportIndex", QShaderDescription::ViewportIndexBuiltin },
+ { "TessLevelOuter", QShaderDescription::TessLevelOuterBuiltin },
+ { "TessLevelInner", QShaderDescription::TessLevelInnerBuiltin },
+ { "TessCoord", QShaderDescription::TessCoordBuiltin },
+ { "PatchVertices", QShaderDescription::PatchVerticesBuiltin },
+ { "FragCoord", QShaderDescription::FragCoordBuiltin },
+ { "PointCoord", QShaderDescription::PointCoordBuiltin },
+ { "FrontFacing", QShaderDescription::FrontFacingBuiltin },
+ { "SampleId", QShaderDescription::SampleIdBuiltin },
+ { "SamplePosition", QShaderDescription::SamplePositionBuiltin },
+ { "SampleMask", QShaderDescription::SampleMaskBuiltin },
+ { "FragDepth", QShaderDescription::FragDepthBuiltin },
+ { "NumWorkGroups", QShaderDescription::NumWorkGroupsBuiltin },
+ { "WorkgroupSize", QShaderDescription::WorkgroupSizeBuiltin },
+ { "WorkgroupId", QShaderDescription::WorkgroupIdBuiltin },
+ { "LocalInvocationId", QShaderDescription::LocalInvocationIdBuiltin },
+ { "GlobalInvocationId", QShaderDescription::GlobalInvocationIdBuiltin },
+ { "LocalInvocationIndex", QShaderDescription::LocalInvocationIndexBuiltin },
+ { "VertexIndex", QShaderDescription::VertexIndexBuiltin },
+ { "InstanceIndex", QShaderDescription::InstanceIndexBuiltin }
+};
+
+static QLatin1StringView builtinTypeStr(QShaderDescription::BuiltinType t)
+{
+ for (size_t i = 0; i < sizeof(builtinTypeTab) / sizeof(BuiltinTypeTab); ++i) {
+ if (builtinTypeTab[i].v == t)
+ return QLatin1StringView(builtinTypeTab[i].k);
+ }
+ return {};
+}
+
+static const struct TessellationModeTab {
+ const char k[10];
+ QShaderDescription::TessellationMode v;
+} tessellationModeTab[] {
+ { "unknown", QShaderDescription::UnknownTessellationMode },
+ { "triangles", QShaderDescription::TrianglesTessellationMode },
+ { "quad", QShaderDescription::QuadTessellationMode },
+ { "isoline", QShaderDescription::IsolineTessellationMode }
+};
+
+static QLatin1StringView tessModeStr(QShaderDescription::TessellationMode mode)
+{
+ for (size_t i = 0; i < sizeof(tessellationModeTab) / sizeof(TessellationModeTab); ++i) {
+ if (tessellationModeTab[i].v == mode)
+ return QLatin1StringView(tessellationModeTab[i].k);
+ }
+ return {};
+}
+
+static const struct TessellationWindingOrderTab {
+ const char k[8];
+ QShaderDescription::TessellationWindingOrder v;
+} tessellationWindingOrderTab[] {
+ { "unknown", QShaderDescription::UnknownTessellationWindingOrder },
+ { "cw", QShaderDescription::CwTessellationWindingOrder },
+ { "ccw", QShaderDescription::CcwTessellationWindingOrder }
+};
+
+static QLatin1StringView tessWindStr(QShaderDescription::TessellationWindingOrder w)
+{
+ for (size_t i = 0; i < sizeof(tessellationWindingOrderTab) / sizeof(TessellationWindingOrderTab); ++i) {
+ if (tessellationWindingOrderTab[i].v == w)
+ return QLatin1StringView(tessellationWindingOrderTab[i].k);
+ }
+ return {};
+}
+
+static const struct TessellationPartitioningTab {
+ const char k[24];
+ QShaderDescription::TessellationPartitioning v;
+} tessellationPartitioningTab[] {
+ { "unknown", QShaderDescription::UnknownTessellationPartitioning },
+ { "equal_spacing", QShaderDescription::EqualTessellationPartitioning },
+ { "fractional_even_spacing", QShaderDescription::FractionalEvenTessellationPartitioning },
+ { "fractional_odd_spacing", QShaderDescription::FractionalOddTessellationPartitioning }
+};
+
+static QLatin1StringView tessPartStr(QShaderDescription::TessellationPartitioning p)
+{
+ for (size_t i = 0; i < sizeof(tessellationPartitioningTab) / sizeof(TessellationPartitioningTab); ++i) {
+ if (tessellationPartitioningTab[i].v == p)
+ return QLatin1StringView(tessellationPartitioningTab[i].k);
+ }
+ return {};
+}
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QShaderDescription &sd)
{
@@ -688,6 +1207,8 @@ QDebug operator<<(QDebug dbg, const QShaderDescription &sd)
<< " storageImages " << d->storageImages
<< " separateImages " << d->separateImages
<< " separateSamplers " << d->separateSamplers
+ << " inBuiltins " << d->inBuiltins
+ << " outBuiltins " << d->outBuiltins
<< ')';
} else {
dbg.nospace() << "QShaderDescription(null)";
@@ -700,6 +1221,8 @@ QDebug operator<<(QDebug dbg, const QShaderDescription::InOutVariable &var)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "InOutVariable(" << typeStr(var.type) << ' ' << var.name;
+ if (var.perPatch)
+ dbg.nospace() << " per-patch";
if (var.location >= 0)
dbg.nospace() << " location=" << var.location;
if (var.binding >= 0)
@@ -712,6 +1235,8 @@ QDebug operator<<(QDebug dbg, const QShaderDescription::InOutVariable &var)
dbg.nospace() << " imageFlags=" << var.imageFlags;
if (!var.arrayDims.isEmpty())
dbg.nospace() << " array=" << var.arrayDims;
+ if (!var.structMembers.isEmpty())
+ dbg.nospace() << " structMembers=" << var.structMembers;
dbg.nospace() << ')';
return dbg;
}
@@ -719,8 +1244,10 @@ QDebug operator<<(QDebug dbg, const QShaderDescription::InOutVariable &var)
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;
+ dbg.nospace() << "BlockVariable(" << typeStr(var.type) << ' ' << var.name;
+ if (var.offset != -1)
+ dbg.nospace() << " offset=" << var.offset;
+ dbg.nospace() << " size=" << var.size;
if (!var.arrayDims.isEmpty())
dbg.nospace() << " array=" << var.arrayDims;
if (var.arrayStride)
@@ -765,9 +1292,24 @@ QDebug operator<<(QDebug dbg, const QShaderDescription::StorageBlock &blk)
dbg.nospace() << " binding=" << blk.binding;
if (blk.descriptorSet >= 0)
dbg.nospace() << " set=" << blk.descriptorSet;
+ if (blk.runtimeArrayStride)
+ dbg.nospace() << " runtimeArrayStride=" << blk.runtimeArrayStride;
+ if (blk.qualifierFlags)
+ dbg.nospace() << " qualifierFlags=" << blk.qualifierFlags;
dbg.nospace() << ' ' << blk.members << ')';
return dbg;
}
+
+QDebug operator<<(QDebug dbg, const QShaderDescription::BuiltinVariable &builtin)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "BuiltinVariable(type=" << builtinTypeStr(builtin.type);
+ dbg.nospace() << " varType=" << typeStr(builtin.varType);
+ if (!builtin.arrayDims.isEmpty())
+ dbg.nospace() << " array=" << builtin.arrayDims;
+ dbg.nospace() << ")";
+ return dbg;
+}
#endif
#define JSON_KEY(key) static constexpr QLatin1StringView key ## Key() noexcept { return QLatin1StringView( #key ); }
@@ -776,6 +1318,7 @@ JSON_KEY(type)
JSON_KEY(location)
JSON_KEY(binding)
JSON_KEY(set)
+JSON_KEY(perPatch)
JSON_KEY(imageFormat)
JSON_KEY(imageFlags)
JSON_KEY(offset)
@@ -797,9 +1340,17 @@ JSON_KEY(pushConstantBlocks)
JSON_KEY(storageBlocks)
JSON_KEY(combinedImageSamplers)
JSON_KEY(storageImages)
-JSON_KEY(localSize)
+JSON_KEY(inBuiltins)
+JSON_KEY(outBuiltins)
+JSON_KEY(computeLocalSize)
+JSON_KEY(tessellationOutputVertexCount)
+JSON_KEY(tessellationMode)
+JSON_KEY(tessellationWindingOrder)
+JSON_KEY(tessellationPartitioning)
JSON_KEY(separateImages)
JSON_KEY(separateSamplers)
+JSON_KEY(runtimeArrayStride)
+JSON_KEY(qualifierFlags)
#undef JSON_KEY
static void addDeco(QJsonObject *obj, const QShaderDescription::InOutVariable &v)
@@ -810,6 +1361,8 @@ static void addDeco(QJsonObject *obj, const QShaderDescription::InOutVariable &v
(*obj)[bindingKey()] = v.binding;
if (v.descriptorSet >= 0)
(*obj)[setKey()] = v.descriptorSet;
+ if (v.perPatch)
+ (*obj)[perPatchKey()] = v.perPatch;
if (v.imageFormat != QShaderDescription::ImageFormatUnknown)
(*obj)[imageFormatKey()] = imageFormatStr(v.imageFormat);
if (v.imageFlags)
@@ -822,32 +1375,29 @@ static void addDeco(QJsonObject *obj, const QShaderDescription::InOutVariable &v
}
}
-static void serializeDecorations(QDataStream *stream, const QShaderDescription::InOutVariable &v)
+static void serializeDecorations(QDataStream *stream, const QShaderDescription::InOutVariable &v, int version)
{
(*stream) << v.location;
(*stream) << v.binding;
(*stream) << v.descriptorSet;
(*stream) << int(v.imageFormat);
(*stream) << int(v.imageFlags);
- (*stream) << int(v.arrayDims.count());
+ (*stream) << int(v.arrayDims.size());
for (int dim : v.arrayDims)
(*stream) << dim;
+ if (version > QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO)
+ (*stream) << quint8(v.perPatch);
}
-static QJsonObject inOutObject(const QShaderDescription::InOutVariable &v)
-{
- QJsonObject obj;
- obj[nameKey()] = QString::fromUtf8(v.name);
- obj[typeKey()] = typeStr(v.type);
- addDeco(&obj, v);
- return obj;
-}
-
-static void serializeInOutVar(QDataStream *stream, const QShaderDescription::InOutVariable &v)
+static void serializeBuiltinVar(QDataStream *stream, const QShaderDescription::BuiltinVariable &v, int version)
{
- (*stream) << QString::fromUtf8(v.name);
(*stream) << int(v.type);
- serializeDecorations(stream, v);
+ if (version > QShaderPrivate::QSB_VERSION_WITHOUT_INPUT_OUTPUT_INTERFACE_BLOCKS) {
+ (*stream) << int(v.varType);
+ (*stream) << int(v.arrayDims.size());
+ for (int dim : v.arrayDims)
+ (*stream) << dim;
+ }
}
static QJsonObject blockMemberObject(const QShaderDescription::BlockVariable &v)
@@ -855,7 +1405,8 @@ static QJsonObject blockMemberObject(const QShaderDescription::BlockVariable &v)
QJsonObject obj;
obj[nameKey()] = QString::fromUtf8(v.name);
obj[typeKey()] = typeStr(v.type);
- obj[offsetKey()] = v.offset;
+ if (v.offset != -1)
+ obj[offsetKey()] = v.offset;
obj[sizeKey()] = v.size;
if (!v.arrayDims.isEmpty()) {
QJsonArray dimArr;
@@ -878,35 +1429,78 @@ static QJsonObject blockMemberObject(const QShaderDescription::BlockVariable &v)
return obj;
}
+static QJsonObject inOutObject(const QShaderDescription::InOutVariable &v)
+{
+ QJsonObject obj;
+ obj[nameKey()] = QString::fromUtf8(v.name);
+ obj[typeKey()] = typeStr(v.type);
+ addDeco(&obj, v);
+ if (!v.structMembers.isEmpty()) {
+ QJsonArray arr;
+ for (const QShaderDescription::BlockVariable &sv : v.structMembers)
+ arr.append(blockMemberObject(sv));
+ obj[structMembersKey()] = arr;
+ }
+ return obj;
+}
+
+static QJsonObject builtinObject(const QShaderDescription::BuiltinVariable &v)
+{
+ QJsonObject obj;
+
+ obj[nameKey()] = builtinTypeStr(v.type);
+ obj[typeKey()] = typeStr(v.varType);
+ if (!v.arrayDims.isEmpty()) {
+ QJsonArray dimArr;
+ for (int dim : v.arrayDims)
+ dimArr.append(dim);
+ obj[arrayDimsKey()] = dimArr;
+ }
+ return obj;
+}
+
static void serializeBlockMemberVar(QDataStream *stream, const QShaderDescription::BlockVariable &v)
{
(*stream) << QString::fromUtf8(v.name);
(*stream) << int(v.type);
(*stream) << v.offset;
(*stream) << v.size;
- (*stream) << int(v.arrayDims.count());
+ (*stream) << int(v.arrayDims.size());
for (int dim : v.arrayDims)
(*stream) << dim;
(*stream) << v.arrayStride;
(*stream) << v.matrixStride;
(*stream) << v.matrixIsRowMajor;
- (*stream) << int(v.structMembers.count());
+ (*stream) << int(v.structMembers.size());
for (const QShaderDescription::BlockVariable &sv : v.structMembers)
serializeBlockMemberVar(stream, sv);
}
+static void serializeInOutVar(QDataStream *stream, const QShaderDescription::InOutVariable &v,
+ int version)
+{
+ (*stream) << QString::fromUtf8(v.name);
+ (*stream) << int(v.type);
+ serializeDecorations(stream, v, version);
+ if (version > QShaderPrivate::QSB_VERSION_WITHOUT_INPUT_OUTPUT_INTERFACE_BLOCKS) {
+ (*stream) << int(v.structMembers.size());
+ for (const QShaderDescription::BlockVariable &sv : v.structMembers)
+ serializeBlockMemberVar(stream, sv);
+ }
+}
+
QJsonDocument QShaderDescriptionPrivate::makeDoc()
{
QJsonObject root;
QJsonArray jinputs;
- for (const QShaderDescription::InOutVariable &v : qAsConst(inVars))
+ for (const QShaderDescription::InOutVariable &v : std::as_const(inVars))
jinputs.append(inOutObject(v));
if (!jinputs.isEmpty())
root[inputsKey()] = jinputs;
QJsonArray joutputs;
- for (const QShaderDescription::InOutVariable &v : qAsConst(outVars))
+ for (const QShaderDescription::InOutVariable &v : std::as_const(outVars))
joutputs.append(inOutObject(v));
if (!joutputs.isEmpty())
root[outputsKey()] = joutputs;
@@ -954,6 +1548,10 @@ QJsonDocument QShaderDescriptionPrivate::makeDoc()
jstorageBlock[bindingKey()] = b.binding;
if (b.descriptorSet >= 0)
jstorageBlock[setKey()] = b.descriptorSet;
+ if (b.runtimeArrayStride)
+ jstorageBlock[runtimeArrayStrideKey()] = b.runtimeArrayStride;
+ if (b.qualifierFlags)
+ jstorageBlock[qualifierFlagsKey()] = int(b.qualifierFlags);
QJsonArray members;
for (const QShaderDescription::BlockVariable &v : b.members)
members.append(blockMemberObject(v));
@@ -964,7 +1562,7 @@ QJsonDocument QShaderDescriptionPrivate::makeDoc()
root[storageBlocksKey()] = jstorageBlocks;
QJsonArray jcombinedSamplers;
- for (const QShaderDescription::InOutVariable &v : qAsConst(combinedImageSamplers)) {
+ for (const QShaderDescription::InOutVariable &v : std::as_const(combinedImageSamplers)) {
QJsonObject sampler;
sampler[nameKey()] = QString::fromUtf8(v.name);
sampler[typeKey()] = typeStr(v.type);
@@ -975,7 +1573,7 @@ QJsonDocument QShaderDescriptionPrivate::makeDoc()
root[combinedImageSamplersKey()] = jcombinedSamplers;
QJsonArray jstorageImages;
- for (const QShaderDescription::InOutVariable &v : qAsConst(storageImages)) {
+ for (const QShaderDescription::InOutVariable &v : std::as_const(storageImages)) {
QJsonObject image;
image[nameKey()] = QString::fromUtf8(v.name);
image[typeKey()] = typeStr(v.type);
@@ -985,13 +1583,39 @@ QJsonDocument QShaderDescriptionPrivate::makeDoc()
if (!jstorageImages.isEmpty())
root[storageImagesKey()] = jstorageImages;
- QJsonArray jlocalSize;
- for (int i = 0; i < 3; ++i)
- jlocalSize.append(QJsonValue(int(localSize[i])));
- root[localSizeKey()] = jlocalSize;
+ QJsonArray jinBuiltins;
+ for (const QShaderDescription::BuiltinVariable &v : std::as_const(inBuiltins))
+ jinBuiltins.append(builtinObject(v));
+ if (!jinBuiltins.isEmpty())
+ root[inBuiltinsKey()] = jinBuiltins;
+
+ QJsonArray joutBuiltins;
+ for (const QShaderDescription::BuiltinVariable &v : std::as_const(outBuiltins))
+ joutBuiltins.append(builtinObject(v));
+ if (!joutBuiltins.isEmpty())
+ root[outBuiltinsKey()] = joutBuiltins;
+
+ if (localSize[0] || localSize[1] || localSize[2]) {
+ QJsonArray jlocalSize;
+ for (size_t i = 0; i < 3; ++i)
+ jlocalSize.append(QJsonValue(int(localSize[i])));
+ root[computeLocalSizeKey()] = jlocalSize;
+ }
+
+ if (tessOutVertCount)
+ root[tessellationOutputVertexCountKey()] = int(tessOutVertCount);
+
+ if (tessMode != QShaderDescription::UnknownTessellationMode)
+ root[tessellationModeKey()] = tessModeStr(tessMode);
+
+ if (tessWind != QShaderDescription::UnknownTessellationWindingOrder)
+ root[tessellationWindingOrderKey()] = tessWindStr(tessWind);
+
+ if (tessPart != QShaderDescription::UnknownTessellationPartitioning)
+ root[tessellationPartitioningKey()] = tessPartStr(tessPart);
QJsonArray jseparateImages;
- for (const QShaderDescription::InOutVariable &v : qAsConst(separateImages)) {
+ for (const QShaderDescription::InOutVariable &v : std::as_const(separateImages)) {
QJsonObject image;
image[nameKey()] = QString::fromUtf8(v.name);
image[typeKey()] = typeStr(v.type);
@@ -1002,7 +1626,7 @@ QJsonDocument QShaderDescriptionPrivate::makeDoc()
root[separateImagesKey()] = jseparateImages;
QJsonArray jseparateSamplers;
- for (const QShaderDescription::InOutVariable &v : qAsConst(separateSamplers)) {
+ for (const QShaderDescription::InOutVariable &v : std::as_const(separateSamplers)) {
QJsonObject sampler;
sampler[nameKey()] = QString::fromUtf8(v.name);
sampler[typeKey()] = typeStr(v.type);
@@ -1015,78 +1639,97 @@ QJsonDocument QShaderDescriptionPrivate::makeDoc()
return QJsonDocument(root);
}
-void QShaderDescriptionPrivate::writeToStream(QDataStream *stream)
+void QShaderDescriptionPrivate::writeToStream(QDataStream *stream, int version)
{
- (*stream) << int(inVars.count());
- for (const QShaderDescription::InOutVariable &v : qAsConst(inVars))
- serializeInOutVar(stream, v);
+ (*stream) << int(inVars.size());
+ for (const QShaderDescription::InOutVariable &v : std::as_const(inVars))
+ serializeInOutVar(stream, v, version);
- (*stream) << int(outVars.count());
- for (const QShaderDescription::InOutVariable &v : qAsConst(outVars))
- serializeInOutVar(stream, v);
+ (*stream) << int(outVars.size());
+ for (const QShaderDescription::InOutVariable &v : std::as_const(outVars))
+ serializeInOutVar(stream, v, version);
- (*stream) << int(uniformBlocks.count());
+ (*stream) << int(uniformBlocks.size());
for (const QShaderDescription::UniformBlock &b : uniformBlocks) {
(*stream) << QString::fromUtf8(b.blockName);
(*stream) << QString::fromUtf8(b.structName);
(*stream) << b.size;
(*stream) << b.binding;
(*stream) << b.descriptorSet;
- (*stream) << int(b.members.count());
+ (*stream) << int(b.members.size());
for (const QShaderDescription::BlockVariable &v : b.members)
serializeBlockMemberVar(stream, v);
}
- (*stream) << int(pushConstantBlocks.count());
+ (*stream) << int(pushConstantBlocks.size());
for (const QShaderDescription::PushConstantBlock &b : pushConstantBlocks) {
(*stream) << QString::fromUtf8(b.name);
(*stream) << b.size;
- (*stream) << int(b.members.count());
+ (*stream) << int(b.members.size());
for (const QShaderDescription::BlockVariable &v : b.members)
serializeBlockMemberVar(stream, v);
}
- (*stream) << int(storageBlocks.count());
+ (*stream) << int(storageBlocks.size());
for (const QShaderDescription::StorageBlock &b : storageBlocks) {
(*stream) << QString::fromUtf8(b.blockName);
(*stream) << QString::fromUtf8(b.instanceName);
(*stream) << b.knownSize;
(*stream) << b.binding;
(*stream) << b.descriptorSet;
- (*stream) << int(b.members.count());
+ (*stream) << int(b.members.size());
for (const QShaderDescription::BlockVariable &v : b.members)
serializeBlockMemberVar(stream, v);
+ if (version > QShaderPrivate::QSB_VERSION_WITHOUT_EXTENDED_STORAGE_BUFFER_INFO) {
+ (*stream) << b.runtimeArrayStride;
+ (*stream) << b.qualifierFlags;
+ }
}
- (*stream) << int(combinedImageSamplers.count());
- for (const QShaderDescription::InOutVariable &v : qAsConst(combinedImageSamplers)) {
+ (*stream) << int(combinedImageSamplers.size());
+ for (const QShaderDescription::InOutVariable &v : std::as_const(combinedImageSamplers)) {
(*stream) << QString::fromUtf8(v.name);
(*stream) << int(v.type);
- serializeDecorations(stream, v);
+ serializeDecorations(stream, v, version);
}
- (*stream) << int(storageImages.count());
- for (const QShaderDescription::InOutVariable &v : qAsConst(storageImages)) {
+ (*stream) << int(storageImages.size());
+ for (const QShaderDescription::InOutVariable &v : std::as_const(storageImages)) {
(*stream) << QString::fromUtf8(v.name);
(*stream) << int(v.type);
- serializeDecorations(stream, v);
+ serializeDecorations(stream, v, version);
}
for (size_t i = 0; i < 3; ++i)
- (*stream) << localSize[i];
+ (*stream) << quint32(localSize[i]);
- (*stream) << int(separateImages.count());
- for (const QShaderDescription::InOutVariable &v : qAsConst(separateImages)) {
+ (*stream) << int(separateImages.size());
+ for (const QShaderDescription::InOutVariable &v : std::as_const(separateImages)) {
(*stream) << QString::fromUtf8(v.name);
(*stream) << int(v.type);
- serializeDecorations(stream, v);
+ serializeDecorations(stream, v, version);
}
- (*stream) << int(separateSamplers.count());
- for (const QShaderDescription::InOutVariable &v : qAsConst(separateSamplers)) {
+ (*stream) << int(separateSamplers.size());
+ for (const QShaderDescription::InOutVariable &v : std::as_const(separateSamplers)) {
(*stream) << QString::fromUtf8(v.name);
(*stream) << int(v.type);
- serializeDecorations(stream, v);
+ serializeDecorations(stream, v, version);
+ }
+
+ if (version > QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO) {
+ (*stream) << quint32(tessOutVertCount);
+ (*stream) << quint32(tessMode);
+ (*stream) << quint32(tessWind);
+ (*stream) << quint32(tessPart);
+
+ (*stream) << int(inBuiltins.size());
+ for (const QShaderDescription::BuiltinVariable &v : std::as_const(inBuiltins))
+ serializeBuiltinVar(stream, v, version);
+
+ (*stream) << int(outBuiltins.size());
+ for (const QShaderDescription::BuiltinVariable &v : std::as_const(outBuiltins))
+ serializeBuiltinVar(stream, v, version);
}
}
@@ -1107,18 +1750,29 @@ static void deserializeDecorations(QDataStream *stream, int version, QShaderDesc
for (int i = 0; i < f; ++i)
(*stream) >> v->arrayDims[i];
}
+
+ if (version > QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO) {
+ quint8 b;
+ (*stream) >> b;
+ v->perPatch = b;
+ }
}
-static QShaderDescription::InOutVariable deserializeInOutVar(QDataStream *stream, int version)
+static QShaderDescription::BuiltinVariable deserializeBuiltinVar(QDataStream *stream, int version)
{
- QShaderDescription::InOutVariable var;
- QString tmp;
- (*stream) >> tmp;
- var.name = tmp.toUtf8();
+ QShaderDescription::BuiltinVariable var;
int t;
(*stream) >> t;
- var.type = QShaderDescription::VariableType(t);
- deserializeDecorations(stream, version, &var);
+ var.type = QShaderDescription::BuiltinType(t);
+ if (version > QShaderPrivate::QSB_VERSION_WITHOUT_INPUT_OUTPUT_INTERFACE_BLOCKS) {
+ (*stream) >> t;
+ var.varType = QShaderDescription::VariableType(t);
+ int count;
+ (*stream) >> count;
+ var.arrayDims.resize(count);
+ for (int i = 0; i < count; ++i)
+ (*stream) >> var.arrayDims[i];
+ }
return var;
}
@@ -1148,6 +1802,26 @@ static QShaderDescription::BlockVariable deserializeBlockMemberVar(QDataStream *
return var;
}
+static QShaderDescription::InOutVariable deserializeInOutVar(QDataStream *stream, int version)
+{
+ QShaderDescription::InOutVariable var;
+ QString tmp;
+ (*stream) >> tmp;
+ var.name = tmp.toUtf8();
+ int t;
+ (*stream) >> t;
+ var.type = QShaderDescription::VariableType(t);
+ deserializeDecorations(stream, version, &var);
+ if (version > QShaderPrivate::QSB_VERSION_WITHOUT_INPUT_OUTPUT_INTERFACE_BLOCKS) {
+ int count;
+ (*stream) >> count;
+ var.structMembers.resize(count);
+ for (int i = 0; i < count; ++i)
+ var.structMembers[i] = deserializeBlockMemberVar(stream, version);
+ }
+ return var;
+}
+
void QShaderDescriptionPrivate::loadFromStream(QDataStream *stream, int version)
{
Q_ASSERT(ref.loadRelaxed() == 1); // must be detached
@@ -1211,6 +1885,11 @@ void QShaderDescriptionPrivate::loadFromStream(QDataStream *stream, int version)
storageBlocks[i].members.resize(memberCount);
for (int memberIdx = 0; memberIdx < memberCount; ++memberIdx)
storageBlocks[i].members[memberIdx] = deserializeBlockMemberVar(stream, version);
+
+ if (version > QShaderPrivate::QSB_VERSION_WITHOUT_EXTENDED_STORAGE_BUFFER_INFO) {
+ (*stream) >> storageBlocks[i].runtimeArrayStride;
+ (*stream) >> storageBlocks[i].qualifierFlags;
+ }
}
(*stream) >> count;
@@ -1237,8 +1916,11 @@ void QShaderDescriptionPrivate::loadFromStream(QDataStream *stream, int version)
deserializeDecorations(stream, version, &storageImages[i]);
}
- for (size_t i = 0; i < 3; ++i)
- (*stream) >> localSize[i];
+ for (size_t i = 0; i < 3; ++i) {
+ quint32 v;
+ (*stream) >> v;
+ localSize[i] = v;
+ }
if (version > QShaderPrivate::QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS) {
(*stream) >> count;
@@ -1265,6 +1947,28 @@ void QShaderDescriptionPrivate::loadFromStream(QDataStream *stream, int version)
deserializeDecorations(stream, version, &separateSamplers[i]);
}
}
+
+ if (version > QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO) {
+ quint32 v;
+ (*stream) >> v;
+ tessOutVertCount = v;
+ (*stream) >> v;
+ tessMode = QShaderDescription::TessellationMode(v);
+ (*stream) >> v;
+ tessWind = QShaderDescription::TessellationWindingOrder(v);
+ (*stream) >> v;
+ tessPart = QShaderDescription::TessellationPartitioning(v);
+
+ (*stream) >> count;
+ inBuiltins.resize(count);
+ for (int i = 0; i < count; ++i)
+ inBuiltins[i] = deserializeBuiltinVar(stream, version);
+
+ (*stream) >> count;
+ outBuiltins.resize(count);
+ for (int i = 0; i < count; ++i)
+ outBuiltins[i] = deserializeBuiltinVar(stream, version);
+ }
}
/*!
@@ -1287,7 +1991,13 @@ bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) no
&& lhs.d->separateImages == rhs.d->separateImages
&& lhs.d->separateSamplers == rhs.d->separateSamplers
&& lhs.d->storageImages == rhs.d->storageImages
- && lhs.d->localSize == rhs.d->localSize;
+ && lhs.d->inBuiltins == rhs.d->inBuiltins
+ && lhs.d->outBuiltins == rhs.d->outBuiltins
+ && lhs.d->localSize == rhs.d->localSize
+ && lhs.d->tessOutVertCount == rhs.d->tessOutVertCount
+ && lhs.d->tessMode == rhs.d->tessMode
+ && lhs.d->tessWind == rhs.d->tessWind
+ && lhs.d->tessPart == rhs.d->tessPart;
}
/*!
@@ -1305,7 +2015,9 @@ bool operator==(const QShaderDescription::InOutVariable &lhs, const QShaderDescr
&& lhs.descriptorSet == rhs.descriptorSet
&& lhs.imageFormat == rhs.imageFormat
&& lhs.imageFlags == rhs.imageFlags
- && lhs.arrayDims == rhs.arrayDims;
+ && lhs.arrayDims == rhs.arrayDims
+ && lhs.perPatch == rhs.perPatch
+ && lhs.structMembers == rhs.structMembers;
}
/*!
@@ -1369,7 +2081,22 @@ bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescri
&& lhs.knownSize == rhs.knownSize
&& lhs.binding == rhs.binding
&& lhs.descriptorSet == rhs.descriptorSet
+ && lhs.runtimeArrayStride == rhs.runtimeArrayStride
+ && lhs.qualifierFlags == rhs.qualifierFlags
&& lhs.members == rhs.members;
}
+/*!
+ Returns \c true if the two BuiltinVariable objects \a lhs and \a rhs are
+ equal.
+
+ \relates QShaderDescription::BuiltinVariable
+ */
+bool operator==(const QShaderDescription::BuiltinVariable &lhs, const QShaderDescription::BuiltinVariable &rhs) noexcept
+{
+ return lhs.type == rhs.type
+ && lhs.varType == rhs.varType
+ && lhs.arrayDims == rhs.arrayDims;
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qshaderdescription.h b/src/gui/rhi/qshaderdescription.h
new file mode 100644
index 0000000000..02492a1598
--- /dev/null
+++ b/src/gui/rhi/qshaderdescription.h
@@ -0,0 +1,386 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSHADERDESCRIPTION_H
+#define QSHADERDESCRIPTION_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is part of the RHI API, with limited compatibility guarantees.
+// Usage of this API may make your code source and binary incompatible with
+// future versions of Qt.
+//
+
+#include <QtGui/qtguiglobal.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qlist.h>
+#include <array>
+
+QT_BEGIN_NAMESPACE
+
+struct QShaderDescriptionPrivate;
+class QDataStream;
+
+class Q_GUI_EXPORT QShaderDescription
+{
+public:
+ QShaderDescription();
+ QShaderDescription(const QShaderDescription &other);
+ QShaderDescription &operator=(const QShaderDescription &other);
+ ~QShaderDescription();
+ void detach();
+
+ bool isValid() const;
+
+ void serialize(QDataStream *stream, int version) const;
+ QByteArray toJson() const;
+
+ static QShaderDescription deserialize(QDataStream *stream, int version);
+
+ 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,
+ SamplerExternalOES,
+ Sampler,
+
+ Image1D,
+ Image2D,
+ Image2DMS,
+ Image3D,
+ ImageCube,
+ Image1DArray,
+ Image2DArray,
+ Image2DMSArray,
+ Image3DArray,
+ ImageCubeArray,
+ ImageRect,
+ ImageBuffer,
+
+ Struct,
+
+ Half,
+ Half2,
+ Half3,
+ Half4
+ };
+
+ 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)
+
+ enum QualifierFlag {
+ QualifierReadOnly = 1 << 0,
+ QualifierWriteOnly = 1 << 1,
+ QualifierCoherent = 1 << 2,
+ QualifierVolatile = 1 << 3,
+ QualifierRestrict = 1 << 4,
+ };
+ Q_DECLARE_FLAGS(QualifierFlags, QualifierFlag)
+
+ // Optional data (like decorations) usually default to an otherwise invalid value (-1 or 0). This is intentional.
+
+ struct BlockVariable {
+ QByteArray name;
+ VariableType type = Unknown;
+ int offset = 0;
+ int size = 0;
+ QList<int> arrayDims;
+ int arrayStride = 0;
+ int matrixStride = 0;
+ bool matrixIsRowMajor = false;
+ QList<BlockVariable> structMembers;
+ };
+
+ struct InOutVariable {
+ QByteArray name;
+ VariableType type = Unknown;
+ int location = -1;
+ int binding = -1;
+ int descriptorSet = -1;
+ ImageFormat imageFormat = ImageFormatUnknown;
+ ImageFlags imageFlags;
+ QList<int> arrayDims;
+ bool perPatch = false;
+ QList<BlockVariable> structMembers;
+ };
+
+ struct UniformBlock {
+ QByteArray blockName;
+ QByteArray structName; // instanceName
+ int size = 0;
+ int binding = -1;
+ int descriptorSet = -1;
+ QList<BlockVariable> members;
+ };
+
+ struct PushConstantBlock {
+ QByteArray name;
+ int size = 0;
+ QList<BlockVariable> members;
+ };
+
+ struct StorageBlock {
+ QByteArray blockName;
+ QByteArray instanceName;
+ int knownSize = 0;
+ int binding = -1;
+ int descriptorSet = -1;
+ QList<BlockVariable> members;
+ int runtimeArrayStride = 0;
+ QualifierFlags qualifierFlags;
+ };
+
+ QList<InOutVariable> inputVariables() const;
+ QList<InOutVariable> outputVariables() const;
+ QList<UniformBlock> uniformBlocks() const;
+ QList<PushConstantBlock> pushConstantBlocks() const;
+ QList<StorageBlock> storageBlocks() const;
+ QList<InOutVariable> combinedImageSamplers() const;
+ QList<InOutVariable> separateImages() const;
+ QList<InOutVariable> separateSamplers() const;
+ QList<InOutVariable> storageImages() const;
+
+ enum BuiltinType {
+ // must match SpvBuiltIn
+ PositionBuiltin = 0,
+ PointSizeBuiltin = 1,
+ ClipDistanceBuiltin = 3,
+ CullDistanceBuiltin = 4,
+ VertexIdBuiltin = 5,
+ InstanceIdBuiltin = 6,
+ PrimitiveIdBuiltin = 7,
+ InvocationIdBuiltin = 8,
+ LayerBuiltin = 9,
+ ViewportIndexBuiltin = 10,
+ TessLevelOuterBuiltin = 11,
+ TessLevelInnerBuiltin = 12,
+ TessCoordBuiltin = 13,
+ PatchVerticesBuiltin = 14,
+ FragCoordBuiltin = 15,
+ PointCoordBuiltin = 16,
+ FrontFacingBuiltin = 17,
+ SampleIdBuiltin = 18,
+ SamplePositionBuiltin = 19,
+ SampleMaskBuiltin = 20,
+ FragDepthBuiltin = 22,
+ NumWorkGroupsBuiltin = 24,
+ WorkgroupSizeBuiltin = 25,
+ WorkgroupIdBuiltin = 26,
+ LocalInvocationIdBuiltin = 27,
+ GlobalInvocationIdBuiltin = 28,
+ LocalInvocationIndexBuiltin = 29,
+ VertexIndexBuiltin = 42,
+ InstanceIndexBuiltin = 43
+ };
+
+ struct BuiltinVariable {
+ BuiltinType type;
+ VariableType varType;
+ QList<int> arrayDims;
+ };
+
+ QList<BuiltinVariable> inputBuiltinVariables() const;
+ QList<BuiltinVariable> outputBuiltinVariables() const;
+
+ std::array<uint, 3> computeShaderLocalSize() const;
+
+ uint tessellationOutputVertexCount() const;
+
+ enum TessellationMode {
+ UnknownTessellationMode,
+ TrianglesTessellationMode,
+ QuadTessellationMode,
+ IsolineTessellationMode
+ };
+
+ TessellationMode tessellationMode() const;
+
+ enum TessellationWindingOrder {
+ UnknownTessellationWindingOrder,
+ CwTessellationWindingOrder,
+ CcwTessellationWindingOrder
+ };
+
+ TessellationWindingOrder tessellationWindingOrder() const;
+
+ enum TessellationPartitioning {
+ UnknownTessellationPartitioning,
+ EqualTessellationPartitioning,
+ FractionalEvenTessellationPartitioning,
+ FractionalOddTessellationPartitioning
+ };
+
+ TessellationPartitioning tessellationPartitioning() const;
+
+private:
+ QShaderDescriptionPrivate *d;
+ friend struct QShaderDescriptionPrivate;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription &);
+#endif
+ friend Q_GUI_EXPORT bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) noexcept;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderDescription::ImageFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderDescription::QualifierFlags)
+
+#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 &);
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::BuiltinVariable &);
+#endif
+
+Q_GUI_EXPORT bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) noexcept;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) noexcept;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) noexcept;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) noexcept;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) noexcept;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) noexcept;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::BuiltinVariable &lhs, const QShaderDescription::BuiltinVariable &rhs) noexcept;
+
+inline bool operator!=(const QShaderDescription &lhs, const QShaderDescription &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::BuiltinVariable &lhs, const QShaderDescription::BuiltinVariable &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qshaderdescription_p.h b/src/gui/rhi/qshaderdescription_p.h
index a64496ead7..df694bbf40 100644
--- a/src/gui/rhi/qshaderdescription_p.h
+++ b/src/gui/rhi/qshaderdescription_p.h
@@ -1,8 +1,8 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QSHADERDESCRIPTION_H
-#define QSHADERDESCRIPTION_H
+#ifndef QSHADERDESCRIPTION_P_H
+#define QSHADERDESCRIPTION_P_H
//
// W A R N I N G
@@ -15,279 +15,67 @@
// We mean it.
//
-#include <QtGui/qtguiglobal.h>
-#include <QtCore/QString>
+#include <rhi/qshaderdescription.h>
#include <QtCore/QList>
-#include <QtCore/private/qglobal_p.h>
-#include <array>
+#include <QtCore/QAtomicInt>
+#include <QtCore/QJsonDocument>
QT_BEGIN_NAMESPACE
-struct QShaderDescriptionPrivate;
-class QDataStream;
-
-class Q_GUI_EXPORT QShaderDescription
+struct Q_GUI_EXPORT QShaderDescriptionPrivate
{
-public:
- QShaderDescription();
- QShaderDescription(const QShaderDescription &other);
- QShaderDescription &operator=(const QShaderDescription &other);
- ~QShaderDescription();
- void detach();
-
- bool isValid() const;
-
- void serialize(QDataStream *stream) const;
- QByteArray toJson() const;
-
- static QShaderDescription deserialize(QDataStream *stream, int version);
-
- 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,
- SamplerExternalOES,
- Sampler,
-
- 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 {
- QByteArray name;
- VariableType type = Unknown;
- int location = -1;
- int binding = -1;
- int descriptorSet = -1;
- ImageFormat imageFormat = ImageFormatUnknown;
- ImageFlags imageFlags;
- QList<int> arrayDims;
- };
-
- struct BlockVariable {
- QByteArray name;
- VariableType type = Unknown;
- int offset = 0;
- int size = 0;
- QList<int> arrayDims;
- int arrayStride = 0;
- int matrixStride = 0;
- bool matrixIsRowMajor = false;
- QList<BlockVariable> structMembers;
- };
-
- struct UniformBlock {
- QByteArray blockName;
- QByteArray structName; // instanceName
- int size = 0;
- int binding = -1;
- int descriptorSet = -1;
- QList<BlockVariable> members;
- };
-
- struct PushConstantBlock {
- QByteArray name;
- int size = 0;
- QList<BlockVariable> members;
- };
-
- struct StorageBlock {
- QByteArray blockName;
- QByteArray instanceName;
- int knownSize = 0;
- int binding = -1;
- int descriptorSet = -1;
- QList<BlockVariable> members;
- };
-
- QList<InOutVariable> inputVariables() const;
- QList<InOutVariable> outputVariables() const;
- QList<UniformBlock> uniformBlocks() const;
- QList<PushConstantBlock> pushConstantBlocks() const;
- QList<StorageBlock> storageBlocks() const;
- QList<InOutVariable> combinedImageSamplers() const;
- QList<InOutVariable> separateImages() const;
- QList<InOutVariable> separateSamplers() const;
- QList<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
- friend Q_GUI_EXPORT bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) noexcept;
+ QShaderDescriptionPrivate()
+ : ref(1)
+ {
+ }
+
+ QShaderDescriptionPrivate(const QShaderDescriptionPrivate &other)
+ : ref(1),
+ inVars(other.inVars),
+ outVars(other.outVars),
+ uniformBlocks(other.uniformBlocks),
+ pushConstantBlocks(other.pushConstantBlocks),
+ storageBlocks(other.storageBlocks),
+ combinedImageSamplers(other.combinedImageSamplers),
+ separateImages(other.separateImages),
+ separateSamplers(other.separateSamplers),
+ storageImages(other.storageImages),
+ inBuiltins(other.inBuiltins),
+ outBuiltins(other.outBuiltins),
+ localSize(other.localSize),
+ tessOutVertCount(other.tessOutVertCount),
+ tessMode(other.tessMode),
+ tessWind(other.tessWind),
+ tessPart(other.tessPart)
+ {
+ }
+
+ static QShaderDescriptionPrivate *get(QShaderDescription *desc) { return desc->d; }
+ static const QShaderDescriptionPrivate *get(const QShaderDescription *desc) { return desc->d; }
+
+ QJsonDocument makeDoc();
+ void writeToStream(QDataStream *stream, int version);
+ void loadFromStream(QDataStream *stream, int version);
+
+ QAtomicInt ref;
+ QList<QShaderDescription::InOutVariable> inVars;
+ QList<QShaderDescription::InOutVariable> outVars;
+ QList<QShaderDescription::UniformBlock> uniformBlocks;
+ QList<QShaderDescription::PushConstantBlock> pushConstantBlocks;
+ QList<QShaderDescription::StorageBlock> storageBlocks;
+ QList<QShaderDescription::InOutVariable> combinedImageSamplers;
+ QList<QShaderDescription::InOutVariable> separateImages;
+ QList<QShaderDescription::InOutVariable> separateSamplers;
+ QList<QShaderDescription::InOutVariable> storageImages;
+ QList<QShaderDescription::BuiltinVariable> inBuiltins;
+ QList<QShaderDescription::BuiltinVariable> outBuiltins;
+ std::array<uint, 3> localSize = {};
+ uint tessOutVertCount = 0;
+ QShaderDescription::TessellationMode tessMode = QShaderDescription::UnknownTessellationMode;
+ QShaderDescription::TessellationWindingOrder tessWind = QShaderDescription::UnknownTessellationWindingOrder;
+ QShaderDescription::TessellationPartitioning tessPart = QShaderDescription::UnknownTessellationPartitioning;
};
-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
-
-Q_GUI_EXPORT bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) noexcept;
-Q_GUI_EXPORT bool operator==(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) noexcept;
-Q_GUI_EXPORT bool operator==(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) noexcept;
-Q_GUI_EXPORT bool operator==(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) noexcept;
-Q_GUI_EXPORT bool operator==(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) noexcept;
-Q_GUI_EXPORT bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) noexcept;
-
-inline bool operator!=(const QShaderDescription &lhs, const QShaderDescription &rhs) noexcept
-{
- return !(lhs == rhs);
-}
-
-inline bool operator!=(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) noexcept
-{
- return !(lhs == rhs);
-}
-
-inline bool operator!=(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) noexcept
-{
- return !(lhs == rhs);
-}
-
-inline bool operator!=(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) noexcept
-{
- return !(lhs == rhs);
-}
-
-inline bool operator!=(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) noexcept
-{
- return !(lhs == rhs);
-}
-
-inline bool operator!=(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) noexcept
-{
- return !(lhs == rhs);
-}
-
QT_END_NAMESPACE
#endif
diff --git a/src/gui/rhi/qshaderdescription_p_p.h b/src/gui/rhi/qshaderdescription_p_p.h
deleted file mode 100644
index 6a981213b1..0000000000
--- a/src/gui/rhi/qshaderdescription_p_p.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#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/QList>
-#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),
- separateImages(other.separateImages),
- separateSamplers(other.separateSamplers),
- 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 writeToStream(QDataStream *stream);
- void loadFromStream(QDataStream *stream, int version);
-
- QAtomicInt ref;
- QList<QShaderDescription::InOutVariable> inVars;
- QList<QShaderDescription::InOutVariable> outVars;
- QList<QShaderDescription::UniformBlock> uniformBlocks;
- QList<QShaderDescription::PushConstantBlock> pushConstantBlocks;
- QList<QShaderDescription::StorageBlock> storageBlocks;
- QList<QShaderDescription::InOutVariable> combinedImageSamplers;
- QList<QShaderDescription::InOutVariable> separateImages;
- QList<QShaderDescription::InOutVariable> separateSamplers;
- QList<QShaderDescription::InOutVariable> storageImages;
- std::array<uint, 3> localSize;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/gui/rhi/qt_attribution.json b/src/gui/rhi/qt_attribution.json
new file mode 100644
index 0000000000..c356f5f087
--- /dev/null
+++ b/src/gui/rhi/qt_attribution.json
@@ -0,0 +1,16 @@
+[
+ {
+ "Id": "rhi-miniengine-d3d12-mipmap",
+ "Name": "Mipmap generator for D3D12",
+ "QDocModule": "qtgui",
+ "Description": "Compute shader for mipmap generation from MiniEngine in DirectX-Graphics-Samples",
+ "QtUsage": "Compute shader for mipmap generation with Direct 3D 12",
+
+ "Homepage": "https://github.com/microsoft/DirectX-Graphics-Samples",
+ "Version": "0aa79bad78992da0b6a8279ddb9002c1753cb849",
+ "License": "MIT License",
+ "LicenseId": "MIT",
+ "LicenseFile": "MiniEngine_LICENSE.txt",
+ "Copyright": "Copyright (c) 2015 Microsoft"
+ }
+]
diff --git a/src/gui/rhi/tdr.hlsl b/src/gui/rhi/tdr.hlsl
deleted file mode 100644
index f79de91c4a..0000000000
--- a/src/gui/rhi/tdr.hlsl
+++ /dev/null
@@ -1,9 +0,0 @@
-RWBuffer<uint> uav;
-cbuffer ConstantBuffer { uint zero; }
-
-[numthreads(256, 1, 1)]
-void killDeviceByTimingOut(uint3 id: SV_DispatchThreadID)
-{
- while (zero == 0)
- uav[id.x] = zero;
-}
diff --git a/src/gui/rhi/vs_test_p.h b/src/gui/rhi/vs_test_p.h
index e24472cd95..5feaef7d38 100644
--- a/src/gui/rhi/vs_test_p.h
+++ b/src/gui/rhi/vs_test_p.h
@@ -76,7 +76,7 @@ ret
// Approximately 6 instruction slots used
#endif
-const BYTE g_testVertexShader[] =
+inline constexpr BYTE g_testVertexShader[] =
{
68, 88, 66, 67, 75, 198,
18, 149, 172, 244, 247, 123,
diff --git a/src/gui/text/coretext/qcoretextfontdatabase.mm b/src/gui/text/coretext/qcoretextfontdatabase.mm
index d17d1bb701..19f3a2b335 100644
--- a/src/gui/text/coretext/qcoretextfontdatabase.mm
+++ b/src/gui/text/coretext/qcoretextfontdatabase.mm
@@ -13,6 +13,7 @@
#endif
#include <QtCore/qelapsedtimer.h>
+#include <QtCore/private/qcore_mac_p.h>
#include "qcoretextfontdatabase_p.h"
#include "qfontengine_coretext_p.h"
@@ -79,7 +80,7 @@ QCoreTextFontDatabase::QCoreTextFontDatabase()
#if defined(Q_OS_MACOS)
m_fontSetObserver = QMacNotificationObserver(nil, NSFontSetChangedNotification, [] {
qCDebug(lcQpaFonts) << "Fonts have changed";
- handleAvailableFontsChanged();
+ QPlatformFontDatabase::repopulateFontDatabase();
});
#endif
}
@@ -89,6 +90,12 @@ QCoreTextFontDatabase::~QCoreTextFontDatabase()
qDeleteAll(m_themeFonts);
}
+CTFontDescriptorRef descriptorForFamily(const QString &familyName)
+{
+ return CTFontDescriptorCreateWithAttributes(CFDictionaryRef(@{
+ (id)kCTFontFamilyNameAttribute: familyName.toNSString()
+ }));
+}
void QCoreTextFontDatabase::populateFontDatabase()
{
qCDebug(lcQpaFonts) << "Populating font database...";
@@ -100,6 +107,110 @@ void QCoreTextFontDatabase::populateFontDatabase()
for (NSString *familyName in familyNames.as<const NSArray *>())
QPlatformFontDatabase::registerFontFamily(QString::fromNSString(familyName));
+ // Some fonts has special handling since macOS Catalina: It is available
+ // on the platform, so that it may be used by applications directly, but does not
+ // get enumerated. Since there are no alternatives, we hardcode it.
+ if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSCatalina
+ && !qEnvironmentVariableIsSet("QT_NO_HARDCODED_FALLBACK_FONTS")) {
+ m_hardcodedFallbackFonts[QChar::Script_Adlam] = QStringLiteral("Noto Sans Adlam");
+ m_hardcodedFallbackFonts[QChar::Script_Ahom] = QStringLiteral("Noto Serif Ahom");
+ m_hardcodedFallbackFonts[QChar::Script_Avestan] = QStringLiteral("Noto Sans Avestan");
+ m_hardcodedFallbackFonts[QChar::Script_Balinese] = QStringLiteral("Noto Serif Balinese");
+ m_hardcodedFallbackFonts[QChar::Script_Bamum] = QStringLiteral("Noto Sans Bamum");
+ m_hardcodedFallbackFonts[QChar::Script_BassaVah] = QStringLiteral("Noto Sans Bassa Vah");
+ m_hardcodedFallbackFonts[QChar::Script_Batak] = QStringLiteral("Noto Sans Batak");
+ m_hardcodedFallbackFonts[QChar::Script_Bhaiksuki] = QStringLiteral("Noto Sans Bhaiksuki");
+ m_hardcodedFallbackFonts[QChar::Script_Brahmi] = QStringLiteral("Noto Sans Brahmi");
+ m_hardcodedFallbackFonts[QChar::Script_Buginese] = QStringLiteral("Noto Sans Buginese");
+ m_hardcodedFallbackFonts[QChar::Script_Buhid] = QStringLiteral("Noto Sans Buhid");
+ m_hardcodedFallbackFonts[QChar::Script_Carian] = QStringLiteral("Noto Sans Carian");
+ m_hardcodedFallbackFonts[QChar::Script_CaucasianAlbanian] = QStringLiteral("Noto Sans Caucasian Albanian");
+ m_hardcodedFallbackFonts[QChar::Script_Chakma] = QStringLiteral("Noto Sans Chakma");
+ m_hardcodedFallbackFonts[QChar::Script_Cham] = QStringLiteral("Noto Sans Cham");
+ m_hardcodedFallbackFonts[QChar::Script_Coptic] = QStringLiteral("Noto Sans Coptic");
+ m_hardcodedFallbackFonts[QChar::Script_Cuneiform] = QStringLiteral("Noto Sans Cuneiform");
+ m_hardcodedFallbackFonts[QChar::Script_Cypriot] = QStringLiteral("Noto Sans Cypriot");
+ m_hardcodedFallbackFonts[QChar::Script_Duployan] = QStringLiteral("Noto Sans Duployan");
+ m_hardcodedFallbackFonts[QChar::Script_EgyptianHieroglyphs] = QStringLiteral("Noto Sans Egyptian Hieroglyphs");
+ m_hardcodedFallbackFonts[QChar::Script_Elbasan] = QStringLiteral("Noto Sans Elbasan");
+ m_hardcodedFallbackFonts[QChar::Script_Glagolitic] = QStringLiteral("Noto Sans Glagolitic");
+ m_hardcodedFallbackFonts[QChar::Script_Gothic] = QStringLiteral("Noto Sans Gothic");
+ m_hardcodedFallbackFonts[QChar::Script_HanifiRohingya] = QStringLiteral("Noto Sans Hanifi Rohingya");
+ m_hardcodedFallbackFonts[QChar::Script_Hanunoo] = QStringLiteral("Noto Sans Hanunoo");
+ m_hardcodedFallbackFonts[QChar::Script_Hatran] = QStringLiteral("Noto Sans Hatran");
+ m_hardcodedFallbackFonts[QChar::Script_ImperialAramaic] = QStringLiteral("Noto Sans Imperial Aramaic");
+ m_hardcodedFallbackFonts[QChar::Script_InscriptionalPahlavi] = QStringLiteral("Noto Sans Inscriptional Pahlavi");
+ m_hardcodedFallbackFonts[QChar::Script_InscriptionalParthian] = QStringLiteral("Noto Sans Inscriptional Parthian");
+ m_hardcodedFallbackFonts[QChar::Script_Javanese] = QStringLiteral("Noto Sans Javanese");
+ m_hardcodedFallbackFonts[QChar::Script_Kaithi] = QStringLiteral("Noto Sans Kaithi");
+ m_hardcodedFallbackFonts[QChar::Script_KayahLi] = QStringLiteral("Noto Sans Kayah Li");
+ m_hardcodedFallbackFonts[QChar::Script_Kharoshthi] = QStringLiteral("Noto Sans Kharoshthi");
+ m_hardcodedFallbackFonts[QChar::Script_Khojki] = QStringLiteral("Noto Sans Khojki");
+ m_hardcodedFallbackFonts[QChar::Script_Khudawadi] = QStringLiteral("Noto Sans Khudawadi");
+ m_hardcodedFallbackFonts[QChar::Script_Lepcha] = QStringLiteral("Noto Sans Lepcha");
+ m_hardcodedFallbackFonts[QChar::Script_Limbu] = QStringLiteral("Noto Sans Limbu");
+ m_hardcodedFallbackFonts[QChar::Script_LinearA] = QStringLiteral("Noto Sans Linear A");
+ m_hardcodedFallbackFonts[QChar::Script_LinearB] = QStringLiteral("Noto Sans Linear B");
+ m_hardcodedFallbackFonts[QChar::Script_Lisu] = QStringLiteral("Noto Sans Lisu");
+ m_hardcodedFallbackFonts[QChar::Script_Lycian] = QStringLiteral("Noto Sans Lycian");
+ m_hardcodedFallbackFonts[QChar::Script_Lydian] = QStringLiteral("Noto Sans Lydian");
+ m_hardcodedFallbackFonts[QChar::Script_Mahajani] = QStringLiteral("Noto Sans Mahajani");
+ m_hardcodedFallbackFonts[QChar::Script_Mandaic] = QStringLiteral("Noto Sans Mandaic");
+ m_hardcodedFallbackFonts[QChar::Script_Manichaean] = QStringLiteral("Noto Sans Manichaean");
+ m_hardcodedFallbackFonts[QChar::Script_Marchen] = QStringLiteral("Noto Sans Marchen");
+ m_hardcodedFallbackFonts[QChar::Script_MendeKikakui] = QStringLiteral("Noto Sans Mende Kikakui");
+ m_hardcodedFallbackFonts[QChar::Script_MeroiticCursive] = QStringLiteral("Noto Sans Meroitic");
+ m_hardcodedFallbackFonts[QChar::Script_MeroiticHieroglyphs] = QStringLiteral("Noto Sans Meroitic");
+ m_hardcodedFallbackFonts[QChar::Script_Miao] = QStringLiteral("Noto Sans Miao");
+ m_hardcodedFallbackFonts[QChar::Script_Modi] = QStringLiteral("Noto Sans Modi");
+ m_hardcodedFallbackFonts[QChar::Script_Mongolian] = QStringLiteral("Noto Sans Mongolian");
+ m_hardcodedFallbackFonts[QChar::Script_Mro] = QStringLiteral("Noto Sans Mro");
+ m_hardcodedFallbackFonts[QChar::Script_MeeteiMayek] = QStringLiteral("Noto Sans Meetei Mayek");
+ m_hardcodedFallbackFonts[QChar::Script_Multani] = QStringLiteral("Noto Sans Multani");
+ m_hardcodedFallbackFonts[QChar::Script_Nabataean] = QStringLiteral("Noto Sans Nabataean");
+ m_hardcodedFallbackFonts[QChar::Script_Newa] = QStringLiteral("Noto Sans Newa");
+ m_hardcodedFallbackFonts[QChar::Script_NewTaiLue] = QStringLiteral("Noto Sans New Tai Lue");
+ m_hardcodedFallbackFonts[QChar::Script_Nko] = QStringLiteral("Noto Sans Nko");
+ m_hardcodedFallbackFonts[QChar::Script_OlChiki] = QStringLiteral("Noto Sans Ol Chiki");
+ m_hardcodedFallbackFonts[QChar::Script_OldHungarian] = QStringLiteral("Noto Sans Old Hungarian");
+ m_hardcodedFallbackFonts[QChar::Script_OldItalic] = QStringLiteral("Noto Sans Old Italic");
+ m_hardcodedFallbackFonts[QChar::Script_OldNorthArabian] = QStringLiteral("Noto Sans Old North Arabian");
+ m_hardcodedFallbackFonts[QChar::Script_OldPermic] = QStringLiteral("Noto Sans Old Permic");
+ m_hardcodedFallbackFonts[QChar::Script_OldPersian] = QStringLiteral("Noto Sans Old Persian");
+ m_hardcodedFallbackFonts[QChar::Script_OldSouthArabian] = QStringLiteral("Noto Sans Old South Arabian");
+ m_hardcodedFallbackFonts[QChar::Script_OldTurkic] = QStringLiteral("Noto Sans Old Turkic");
+ m_hardcodedFallbackFonts[QChar::Script_Osage] = QStringLiteral("Noto Sans Osage");
+ m_hardcodedFallbackFonts[QChar::Script_Osmanya] = QStringLiteral("Noto Sans Osmanya");
+ m_hardcodedFallbackFonts[QChar::Script_PahawhHmong] = QStringLiteral("Noto Sans Pahawh Hmong");
+ m_hardcodedFallbackFonts[QChar::Script_Palmyrene] = QStringLiteral("Noto Sans Palmyrene");
+ m_hardcodedFallbackFonts[QChar::Script_PauCinHau] = QStringLiteral("Noto Sans Pau Cin Hau");
+ m_hardcodedFallbackFonts[QChar::Script_PhagsPa] = QStringLiteral("Noto Sans PhagsPa");
+ m_hardcodedFallbackFonts[QChar::Script_Phoenician] = QStringLiteral("Noto Sans Phoenician");
+ m_hardcodedFallbackFonts[QChar::Script_PsalterPahlavi] = QStringLiteral("Noto Sans Psalter Pahlavi");
+ m_hardcodedFallbackFonts[QChar::Script_Rejang] = QStringLiteral("Noto Sans Rejang");
+ m_hardcodedFallbackFonts[QChar::Script_Samaritan] = QStringLiteral("Noto Sans Samaritan");
+ m_hardcodedFallbackFonts[QChar::Script_Saurashtra] = QStringLiteral("Noto Sans Saurashtra");
+ m_hardcodedFallbackFonts[QChar::Script_Sharada] = QStringLiteral("Noto Sans Sharada");
+ m_hardcodedFallbackFonts[QChar::Script_Siddham] = QStringLiteral("Noto Sans Siddham");
+ m_hardcodedFallbackFonts[QChar::Script_SoraSompeng] = QStringLiteral("Noto Sans Sora Sompeng");
+ m_hardcodedFallbackFonts[QChar::Script_Sundanese] = QStringLiteral("Noto Sans Sundanese");
+ m_hardcodedFallbackFonts[QChar::Script_SylotiNagri] = QStringLiteral("Noto Sans Syloti Nagri");
+ m_hardcodedFallbackFonts[QChar::Script_Tagalog] = QStringLiteral("Noto Sans Tagalog");
+ m_hardcodedFallbackFonts[QChar::Script_Tagbanwa] = QStringLiteral("Noto Sans Tagbanwa");
+ m_hardcodedFallbackFonts[QChar::Script_Takri] = QStringLiteral("Noto Sans Takri");
+ m_hardcodedFallbackFonts[QChar::Script_TaiLe] = QStringLiteral("Noto Sans Tai Le");
+ m_hardcodedFallbackFonts[QChar::Script_TaiTham] = QStringLiteral("Noto Sans Tai Tham");
+ m_hardcodedFallbackFonts[QChar::Script_TaiViet] = QStringLiteral("Noto Sans Tai Viet");
+ m_hardcodedFallbackFonts[QChar::Script_Thaana] = QStringLiteral("Noto Sans Thaana");
+ m_hardcodedFallbackFonts[QChar::Script_Tifinagh] = QStringLiteral("Noto Sans Tifinagh");
+ m_hardcodedFallbackFonts[QChar::Script_Tirhuta] = QStringLiteral("Noto Sans Tirhuta");
+ m_hardcodedFallbackFonts[QChar::Script_Ugaritic] = QStringLiteral("Noto Sans Ugaritic");
+ m_hardcodedFallbackFonts[QChar::Script_Vai] = QStringLiteral("Noto Sans Vai");
+ m_hardcodedFallbackFonts[QChar::Script_WarangCiti] = QStringLiteral("Noto Sans Warang Citi");
+ m_hardcodedFallbackFonts[QChar::Script_Wancho] = QStringLiteral("Noto Sans Wancho");
+ m_hardcodedFallbackFonts[QChar::Script_Yi] = QStringLiteral("Noto Sans Yi");
+ }
+
qCDebug(lcQpaFonts) << "Populating available families took" << elapsed.restart() << "ms";
populateThemeFonts();
@@ -173,13 +284,6 @@ bool QCoreTextFontDatabase::populateFamilyAliases(const QString &missingFamily)
#endif
}
-CTFontDescriptorRef descriptorForFamily(const QString &familyName)
-{
- return CTFontDescriptorCreateWithAttributes(CFDictionaryRef(@{
- (id)kCTFontFamilyNameAttribute: familyName.toNSString()
- }));
-}
-
CTFontDescriptorRef descriptorForFamily(const char *familyName)
{
return descriptorForFamily(QString::fromLatin1(familyName));
@@ -257,7 +361,7 @@ static void getFontDescription(CTFontDescriptorRef font, FontDescription *fd)
fd->fixedPitch = false;
if (QCFType<CTFontRef> tempFont = CTFontCreateWithFontDescriptor(font, 0.0, 0)) {
- uint tag = MAKE_TAG('O', 'S', '/', '2');
+ uint tag = QFont::Tag("OS/2").value();
CTFontRef tempFontRef = tempFont;
void *userData = reinterpret_cast<void *>(&tempFontRef);
uint length = 128;
@@ -385,6 +489,31 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory<QCoreTextFontEngine>::fontEngine
qreal scaledPointSize = fontDef.pixelSize;
CGAffineTransform matrix = qt_transform_from_fontdef(fontDef);
+
+ if (!fontDef.variableAxisValues.isEmpty()) {
+ QCFType<CFMutableDictionaryRef> variations = CFDictionaryCreateMutable(nullptr,
+ fontDef.variableAxisValues.size(),
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ for (auto it = fontDef.variableAxisValues.constBegin();
+ it != fontDef.variableAxisValues.constEnd();
+ ++it) {
+ const quint32 tag = it.key().value();
+ const float value = it.value();
+ QCFType<CFNumberRef> tagRef = CFNumberCreate(nullptr, kCFNumberIntType, &tag);
+ QCFType<CFNumberRef> valueRef = CFNumberCreate(nullptr, kCFNumberFloatType, &value);
+
+ CFDictionarySetValue(variations, tagRef, valueRef);
+ }
+ QCFType<CFDictionaryRef> attributes = CFDictionaryCreate(nullptr,
+ (const void **) &kCTFontVariationAttribute,
+ (const void **) &variations,
+ 1,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ descriptor = CTFontDescriptorCreateCopyWithAttributes(descriptor, attributes);
+ }
+
if (QCFType<CTFontRef> font = CTFontCreateWithFontDescriptor(descriptor, scaledPointSize, &matrix))
return new QCoreTextFontEngine(font, fontDef);
@@ -400,7 +529,7 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory<QFontEngineFT>::fontEngine(const
if (NSValue *fontDataValue = descriptorAttribute<NSValue>(descriptor, (CFStringRef)kQtFontDataAttribute)) {
QByteArray *fontData = static_cast<QByteArray *>(fontDataValue.pointerValue);
return QFontEngineFT::create(*fontData, fontDef.pixelSize,
- static_cast<QFont::HintingPreference>(fontDef.hintingPreference));
+ static_cast<QFont::HintingPreference>(fontDef.hintingPreference), fontDef.variableAxisValues);
} else if (NSURL *url = descriptorAttribute<NSURL>(descriptor, kCTFontURLAttribute)) {
QFontEngine::FaceId faceId;
@@ -411,6 +540,8 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory<QFontEngineFT>::fontEngine(const
QString styleName = QCFString(CTFontDescriptorCopyAttribute(descriptor, kCTFontStyleNameAttribute));
faceId.index = QFreetypeFace::getFaceIndexByStyleName(faceFileName, styleName);
+ faceId.variableAxes = fontDef.variableAxisValues;
+
return QFontEngineFT::create(fontDef, faceId);
}
// We end up here with a descriptor does not contain Qt font data or kCTFontURLAttribute.
@@ -423,7 +554,7 @@ QFontEngine *QCoreTextFontDatabaseEngineFactory<QFontEngineFT>::fontEngine(const
template <class T>
QFontEngine *QCoreTextFontDatabaseEngineFactory<T>::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
{
- return T::create(fontData, pixelSize, hintingPreference);
+ return T::create(fontData, pixelSize, hintingPreference, {});
}
// Explicitly instantiate so that we don't need the plugin to involve FreeType
@@ -441,7 +572,7 @@ CFArrayRef fallbacksForDescriptor(CTFontDescriptorRef descriptor)
}
CFArrayRef cascadeList = CFArrayRef(CTFontCopyDefaultCascadeListForLanguages(font,
- (CFArrayRef)[NSUserDefaults.standardUserDefaults stringArrayForKey:@"AppleLanguages"]));
+ (CFArrayRef)NSLocale.preferredLanguages));
if (!cascadeList) {
qCWarning(lcQpaFonts) << "Failed to create fallback cascade list for" << descriptor;
@@ -569,6 +700,31 @@ QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFo
// add Apple Symbols to cover those too.
if (!fallbackList.contains(QStringLiteral("Apple Symbols")))
fallbackList.append(QStringLiteral("Apple Symbols"));
+ // Some Noto* fonts are not automatically enumerated by system, despite being the main
+ // fonts for their writing system.
+ QString hardcodedFont = m_hardcodedFallbackFonts.value(script);
+ if (!hardcodedFont.isEmpty() && !fallbackList.contains(hardcodedFont)) {
+ if (!isFamilyPopulated(hardcodedFont)) {
+ if (!m_privateFamilies.contains(hardcodedFont)) {
+ QCFType<CTFontDescriptorRef> familyDescriptor = descriptorForFamily(hardcodedFont);
+ QCFType<CFArrayRef> matchingFonts = CTFontDescriptorCreateMatchingFontDescriptors(familyDescriptor, nullptr);
+ if (matchingFonts) {
+ const int numFonts = CFArrayGetCount(matchingFonts);
+ for (int i = 0; i < numFonts; ++i)
+ const_cast<QCoreTextFontDatabase *>(this)->populateFromDescriptor(CTFontDescriptorRef(CFArrayGetValueAtIndex(matchingFonts, i)),
+ hardcodedFont);
+
+ fallbackList.append(hardcodedFont);
+ }
+
+ // Register as private family even if the font is not found, in order to avoid
+ // redoing the check later. In later calls, the font will then just be ignored.
+ m_privateFamilies.insert(hardcodedFont);
+ }
+ } else {
+ fallbackList.append(hardcodedFont);
+ }
+ }
#endif
extern QStringList qt_sort_families_by_writing_system(QChar::Script, const QStringList &);
@@ -585,13 +741,20 @@ QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData
if (!fontData.isEmpty()) {
QCFType<CFDataRef> fontDataReference = fontData.toRawCFData();
- if (QCFType<CTFontDescriptorRef> descriptor = CTFontManagerCreateFontDescriptorFromData(fontDataReference)) {
- // There's no way to get the data back out of a font descriptor created with
- // CTFontManagerCreateFontDescriptorFromData, so we attach the data manually.
- NSDictionary *attributes = @{ kQtFontDataAttribute : [NSValue valueWithPointer:new QByteArray(fontData)] };
- descriptor = CTFontDescriptorCreateCopyWithAttributes(descriptor, (CFDictionaryRef)attributes);
+ if (QCFType<CFArrayRef> descriptors = CTFontManagerCreateFontDescriptorsFromData(fontDataReference)) {
CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- CFArrayAppendValue(array, descriptor);
+ const int count = CFArrayGetCount(descriptors);
+
+ for (int i = 0; i < count; ++i) {
+ CTFontDescriptorRef descriptor = CTFontDescriptorRef(CFArrayGetValueAtIndex(descriptors, i));
+
+ // There's no way to get the data back out of a font descriptor created with
+ // CTFontManagerCreateFontDescriptorFromData, so we attach the data manually.
+ NSDictionary *attributes = @{ kQtFontDataAttribute : [NSValue valueWithPointer:new QByteArray(fontData)] };
+ descriptor = CTFontDescriptorCreateCopyWithAttributes(descriptor, (CFDictionaryRef)attributes);
+ CFArrayAppendValue(array, descriptor);
+ }
+
fonts = array;
}
} else {
@@ -619,7 +782,7 @@ QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData
bool QCoreTextFontDatabase::isPrivateFontFamily(const QString &family) const
{
- if (family.startsWith(u'.') || family == "LastResort"_L1)
+ if (family.startsWith(u'.') || family == "LastResort"_L1 || m_privateFamilies.contains(family))
return true;
return QPlatformFontDatabase::isPrivateFontFamily(family);
@@ -733,7 +896,7 @@ static CTFontDescriptorRef fontDescriptorFromTheme(QPlatformTheme::Font f)
UIFontDescriptor *desc = [UIFontDescriptor preferredFontDescriptorWithTextStyle:textStyle];
return static_cast<CTFontDescriptorRef>(CFBridgingRetain(desc));
}
-#endif // Q_OS_IOS, Q_OS_TVOS, Q_OS_WATCHOS
+#endif // QT_PLATFORM_UIKIT
// macOS default case and iOS fallback case
return descriptorForFontType(fontTypeFromTheme(f));
@@ -776,7 +939,7 @@ void QCoreTextFontDatabase::populateThemeFonts()
auto addFontVariants = [&](CTFontDescriptorRef descriptor) {
QCFType<CFArrayRef> matchingDescriptors = CTFontDescriptorCreateMatchingFontDescriptors(descriptor, nullptr);
- const int matchingDescriptorsCount = CFArrayGetCount(matchingDescriptors);
+ const int matchingDescriptorsCount = matchingDescriptors ? CFArrayGetCount(matchingDescriptors) : 0;
qCDebug(lcQpaFonts) << "Enumerating font variants based on" << id(descriptor)
<< "resulted in" << matchingDescriptorsCount << "matching descriptors"
<< matchingDescriptors.as<NSArray*>();
@@ -851,5 +1014,10 @@ QList<int> QCoreTextFontDatabase::standardSizes() const
return ret;
}
+bool QCoreTextFontDatabase::supportsVariableApplicationFonts() const
+{
+ return true;
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/text/coretext/qcoretextfontdatabase_p.h b/src/gui/text/coretext/qcoretextfontdatabase_p.h
index ea3a094247..eeea9ad640 100644
--- a/src/gui/text/coretext/qcoretextfontdatabase_p.h
+++ b/src/gui/text/coretext/qcoretextfontdatabase_p.h
@@ -46,6 +46,7 @@ public:
QFont defaultFont() const override;
bool fontsAlwaysScalable() const override;
QList<int> standardSizes() const override;
+ bool supportsVariableApplicationFonts() const override;
// For iOS and macOS platform themes
QFont *themeFont(QPlatformTheme::Font) const;
@@ -57,6 +58,8 @@ private:
QHash<QPlatformTheme::Font, QFont *> m_themeFonts;
QHash<QString, QList<QCFType<CTFontDescriptorRef>>> m_systemFontDescriptors;
+ QHash<QChar::Script, QString> m_hardcodedFallbackFonts;
+ mutable QSet<QString> m_privateFamilies;
bool m_hasPopulatedAliases;
diff --git a/src/gui/text/coretext/qfontengine_coretext.mm b/src/gui/text/coretext/qfontengine_coretext.mm
index 8916648992..1050c03d75 100644
--- a/src/gui/text/coretext/qfontengine_coretext.mm
+++ b/src/gui/text/coretext/qfontengine_coretext.mm
@@ -12,6 +12,9 @@
#include <QtGui/qpainterpath.h>
#include <private/qcoregraphics_p.h>
#include <private/qimage_p.h>
+#include <private/qguiapplication_p.h>
+#include <private/qstringiterator_p.h>
+#include <qpa/qplatformtheme.h>
#include <cmath>
@@ -125,9 +128,13 @@ public:
QByteArray m_fontData;
};
-QCoreTextFontEngine *QCoreTextFontEngine::create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
+QCoreTextFontEngine *QCoreTextFontEngine::create(const QByteArray &fontData,
+ qreal pixelSize,
+ QFont::HintingPreference hintingPreference,
+ const QMap<QFont::Tag, float> &variableAxisValues)
{
Q_UNUSED(hintingPreference);
+ Q_UNUSED(variableAxisValues);
QCFType<CFDataRef> fontDataReference = fontData.toRawCFData();
QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithCFData(fontDataReference);
@@ -186,6 +193,7 @@ void QCoreTextFontEngine::init()
face_id.index = 0;
QCFString name = CTFontCopyName(ctfont, kCTFontUniqueNameKey);
face_id.filename = QString::fromCFString(name).toUtf8();
+ face_id.variableAxes = fontDef.variableAxisValues;
QCFString family = CTFontCopyFamilyName(ctfont);
fontDef.families = QStringList(family);
@@ -230,7 +238,7 @@ void QCoreTextFontEngine::init()
synthesisFlags |= SynthesizedItalic;
avgCharWidth = 0;
- QByteArray os2Table = getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
+ QByteArray os2Table = getSfntTable(QFont::Tag("OS/2").value());
unsigned emSize = CTFontGetUnitsPerEm(ctfont);
if (os2Table.size() >= 10) {
fsType = qFromBigEndian<quint16>(os2Table.constData() + 8);
@@ -267,29 +275,30 @@ glyph_t QCoreTextFontEngine::glyphIndex(uint ucs4) const
return glyphIndices[0];
}
-bool QCoreTextFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
- int *nglyphs, QFontEngine::ShaperFlags flags) const
+int QCoreTextFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
+ int *nglyphs, QFontEngine::ShaperFlags flags) const
{
Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
if (*nglyphs < len) {
*nglyphs = len;
- return false;
+ return -1;
}
QVarLengthArray<CGGlyph> cgGlyphs(len);
CTFontGetGlyphsForCharacters(ctfont, (const UniChar*)str, cgGlyphs.data(), len);
int glyph_pos = 0;
- for (int i = 0; i < len; ++i) {
- glyphs->glyphs[glyph_pos] = cgGlyphs[i];
- if (glyph_pos < i)
- cgGlyphs[glyph_pos] = cgGlyphs[i];
- glyph_pos++;
-
- // If it's a non-BMP char, skip the lower part of surrogate pair and go
- // directly to the next char without increasing glyph_pos
- if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate())
- ++i;
+ int mappedGlyphs = 0;
+ QStringIterator it(str, str + len);
+ while (it.hasNext()) {
+ qsizetype idx = it.index();
+ char32_t ucs4 = it.next();
+ glyphs->glyphs[glyph_pos] = cgGlyphs[idx];
+ if (glyph_pos < idx)
+ cgGlyphs[glyph_pos] = cgGlyphs[idx];
+ if (glyphs->glyphs[glyph_pos] != 0 || isIgnorableChar(ucs4))
+ mappedGlyphs++;
+ glyph_pos++;
}
*nglyphs = glyph_pos;
@@ -298,15 +307,7 @@ bool QCoreTextFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *
if (!(flags & GlyphIndicesOnly))
loadAdvancesForGlyphs(cgGlyphs, glyphs);
- return true;
-}
-
-glyph_metrics_t QCoreTextFontEngine::boundingBox(const QGlyphLayout &glyphs)
-{
- QFixed w;
- for (int i = 0; i < glyphs.numGlyphs; ++i)
- w += glyphs.effectiveAdvance(i);
- return glyph_metrics_t(0, -(ascent()), w - lastRightBearing(glyphs), ascent()+descent(), w, 0);
+ return mappedGlyphs;
}
glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph)
@@ -370,6 +371,7 @@ bool QCoreTextFontEngine::hasColorGlyphs() const
return glyphFormat == QFontEngine::Format_ARGB;
}
+Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight)
{
QVarLengthArray<QFixedPoint> positions;
@@ -386,7 +388,8 @@ void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextIt
CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight);
- CGAffineTransformConcat(cgMatrix, oldTextMatrix);
+ // FIXME: Should we include the old matrix here? If so we need to assign it.
+ Q_UNUSED(CGAffineTransformConcat(cgMatrix, oldTextMatrix));
if (synthesisFlags & QFontEngine::SynthesizedItalic)
cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -SYNTHETIC_ITALIC_SKEW, 1, 0, 0));
@@ -413,7 +416,15 @@ void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextIt
CTFontDrawGlyphs(ctfont, cgGlyphs.data(), cgPositions.data(), glyphs.size(), ctx);
if (synthesisFlags & QFontEngine::SynthesizedBold) {
- CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(),
+ QTransform matrix(cgMatrix.a, cgMatrix.b, cgMatrix.c, cgMatrix.d, cgMatrix.tx, cgMatrix.ty);
+
+ qreal boldOffset = 0.5 * lineThickness().toReal();
+ qreal scale;
+ qt_scaleForTransform(matrix, &scale);
+ boldOffset *= scale;
+
+ CGContextSetTextPosition(ctx,
+ positions[0].x.toReal() + boldOffset,
positions[0].y.toReal());
CTFontDrawGlyphs(ctfont, cgGlyphs.data(), cgPositions.data(), glyphs.size(), ctx);
}
@@ -714,10 +725,12 @@ QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, const QFixedPoint &subP
// draw with white or black fill, and then invert the glyph image in the latter case,
// producing an alpha map. This covers the most common use-cases, but longer term we
// should propagate the fill color all the way from the paint engine, and include it
- //in the key for the glyph cache.
+ // in the key for the glyph cache.
- if (!qt_mac_applicationIsInDarkMode())
- return kCGColorBlack;
+ if (auto *platformTheme = QGuiApplicationPrivate::platformTheme()) {
+ if (platformTheme->colorScheme() != Qt::ColorScheme::Dark)
+ return kCGColorBlack;
+ }
}
return kCGColorWhite;
}();
@@ -760,7 +773,13 @@ QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, const QFixedPoint &subP
CTFontDrawGlyphs(ctfont, &cgGlyph, &CGPointZero, 1, ctx);
if (synthesisFlags & QFontEngine::SynthesizedBold) {
- CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y);
+ qreal boldOffset = 0.5 * lineThickness().toReal();
+
+ qreal scale;
+ qt_scaleForTransform(matrix, &scale);
+ boldOffset *= scale;
+
+ CGContextSetTextPosition(ctx, pos_x + boldOffset, pos_y);
CTFontDrawGlyphs(ctfont, &cgGlyph, &CGPointZero, 1, ctx);
}
} else {
diff --git a/src/gui/text/coretext/qfontengine_coretext_p.h b/src/gui/text/coretext/qfontengine_coretext_p.h
index f81e84fbc0..2f388c32bc 100644
--- a/src/gui/text/coretext/qfontengine_coretext_p.h
+++ b/src/gui/text/coretext/qfontengine_coretext_p.h
@@ -38,10 +38,9 @@ public:
~QCoreTextFontEngine();
glyph_t glyphIndex(uint ucs4) const override;
- bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override;
+ int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override;
void recalcAdvances(QGlyphLayout *, ShaperFlags) const override;
- glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) override;
glyph_metrics_t boundingBox(glyph_t glyph) override;
QFixed capHeight() const override;
@@ -92,7 +91,7 @@ public:
static bool ct_getSfntTable(void *user_data, uint tag, uchar *buffer, uint *length);
static QFont::Weight qtWeightFromCFWeight(float value);
- static QCoreTextFontEngine *create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference);
+ static QCoreTextFontEngine *create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference, const QMap<QFont::Tag, float> &variableAxisValue);
protected:
QCoreTextFontEngine(const QFontDef &def);
diff --git a/src/gui/text/freetype/qfontengine_ft.cpp b/src/gui/text/freetype/qfontengine_ft.cpp
index a7892f95b6..d3791f1f6e 100644
--- a/src/gui/text/freetype/qfontengine_ft.cpp
+++ b/src/gui/text/freetype/qfontengine_ft.cpp
@@ -12,6 +12,7 @@
#include <qscreen.h>
#include <qpa/qplatformscreen.h>
#include <QtCore/QUuid>
+#include <QtCore/QLoggingCategory>
#include <QtGui/QPainterPath>
#ifndef QT_NO_FREETYPE
@@ -34,6 +35,7 @@
#include FT_GLYPH_H
#include FT_MODULE_H
#include FT_LCD_FILTER_H
+#include FT_MULTIPLE_MASTERS_H
#if defined(FT_CONFIG_OPTIONS_H)
#include FT_CONFIG_OPTIONS_H
@@ -53,6 +55,8 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcFontMatch)
+
using namespace Qt::StringLiterals;
#define FLOOR(x) ((x) & -64)
@@ -111,8 +115,11 @@ public:
QtFreetypeData::~QtFreetypeData()
{
- for (QHash<QFontEngine::FaceId, QFreetypeFace *>::ConstIterator iter = faces.cbegin(); iter != faces.cend(); ++iter)
+ for (auto iter = faces.cbegin(); iter != faces.cend(); ++iter) {
iter.value()->cleanup();
+ if (!iter.value()->ref.deref())
+ delete iter.value();
+ }
faces.clear();
FT_Done_FreeType(library);
library = nullptr;
@@ -183,10 +190,15 @@ int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QF
return Err_Ok;
}
+bool QFreetypeFace::isScalable() const
+{
+ return FT_IS_SCALABLE(face);
+}
+
bool QFreetypeFace::isScalableBitmap() const
{
#ifdef FT_HAS_COLOR
- return !FT_IS_SCALABLE(face) && FT_HAS_COLOR(face);
+ return !isScalable() && FT_HAS_COLOR(face);
#else
return false;
#endif
@@ -209,19 +221,37 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
QtFreetypeData *freetypeData = qt_getFreetypeData();
- QFreetypeFace *freetype = freetypeData->faces.value(face_id, nullptr);
- if (freetype) {
- freetype->ref.ref();
- } else {
+ QFreetypeFace *freetype = nullptr;
+ auto it = freetypeData->faces.find(face_id);
+ if (it != freetypeData->faces.end()) {
+ freetype = *it;
+
+ Q_ASSERT(freetype->ref.loadRelaxed() > 0);
+ if (freetype->ref.loadRelaxed() == 1) {
+ // If there is only one reference left to the face, it means it is only referenced by
+ // the cache itself, and thus it is in cleanup state (but the final outside reference
+ // was removed on a different thread so it could not be deleted right away). We then
+ // complete the cleanup and pretend we didn't find it, so that it can be re-created with
+ // the present state.
+ freetype->cleanup();
+ freetypeData->faces.erase(it);
+ delete freetype;
+ freetype = nullptr;
+ } else {
+ freetype->ref.ref();
+ }
+ }
+
+ if (!freetype) {
const auto deleter = [](QFreetypeFace *f) { delete f; };
std::unique_ptr<QFreetypeFace, decltype(deleter)> newFreetype(new QFreetypeFace, deleter);
FT_Face face;
if (!face_id.filename.isEmpty()) {
QString fileName = QFile::decodeName(face_id.filename);
- if (face_id.filename.startsWith(":qmemoryfonts/")) {
+ if (const char *prefix = ":qmemoryfonts/"; face_id.filename.startsWith(prefix)) {
// from qfontdatabase.cpp
QByteArray idx = face_id.filename;
- idx.remove(0, 14); // remove ':qmemoryfonts/'
+ idx.remove(0, strlen(prefix)); // remove ':qmemoryfonts/'
bool ok = false;
newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok));
if (!ok)
@@ -243,7 +273,23 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
} else if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face)) {
return nullptr;
}
+
+#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
+ if (face_id.instanceIndex >= 0) {
+ qCDebug(lcFontMatch)
+ << "Selecting named instance" << (face_id.instanceIndex)
+ << "in" << face_id.filename;
+ FT_Set_Named_Instance(face, face_id.instanceIndex + 1);
+ }
+#endif
newFreetype->face = face;
+ newFreetype->mm_var = nullptr;
+ if (FT_IS_NAMED_INSTANCE(newFreetype->face)) {
+ FT_Error ftresult;
+ ftresult = FT_Get_MM_Var(face, &newFreetype->mm_var);
+ if (ftresult != FT_Err_Ok)
+ newFreetype->mm_var = nullptr;
+ }
newFreetype->ref.storeRelaxed(1);
newFreetype->xsize = 0;
@@ -282,6 +328,25 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
FT_Set_Char_Size(face, newFreetype->face->available_sizes[0].x_ppem, newFreetype->face->available_sizes[0].y_ppem, 0, 0);
FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map);
+
+ if (!face_id.variableAxes.isEmpty()) {
+ FT_MM_Var *var = nullptr;
+ FT_Get_MM_Var(newFreetype->face, &var);
+ if (var != nullptr) {
+ QVarLengthArray<FT_Fixed, 16> coords(var->num_axis);
+ FT_Get_Var_Design_Coordinates(face, var->num_axis, coords.data());
+ for (FT_UInt i = 0; i < var->num_axis; ++i) {
+ if (const auto tag = QFont::Tag::fromValue(var->axis[i].tag)) {
+ const auto it = face_id.variableAxes.constFind(*tag);
+ if (it != face_id.variableAxes.constEnd())
+ coords[i] = FT_Fixed(*it * 65536);
+ }
+ }
+ FT_Set_Var_Design_Coordinates(face, var->num_axis, coords.data());
+ FT_Done_MM_Var(qt_getFreetype(), var);
+ }
+ }
+
QT_TRY {
freetypeData->faces.insert(face_id, newFreetype.get());
} QT_CATCH(...) {
@@ -290,6 +355,7 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
QT_RETHROW;
}
freetype = newFreetype.release();
+ freetype->ref.ref();
}
return freetype;
}
@@ -297,30 +363,45 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
void QFreetypeFace::cleanup()
{
hbFace.reset();
+ if (mm_var && face && face->glyph)
+ FT_Done_MM_Var(face->glyph->library, mm_var);
+ mm_var = nullptr;
FT_Done_Face(face);
face = nullptr;
}
void QFreetypeFace::release(const QFontEngine::FaceId &face_id)
{
- if (!ref.deref()) {
- if (face) {
- QtFreetypeData *freetypeData = qt_getFreetypeData();
-
- cleanup();
-
- auto it = freetypeData->faces.constFind(face_id);
- if (it != freetypeData->faces.constEnd())
- freetypeData->faces.erase(it);
-
- if (freetypeData->faces.isEmpty()) {
- FT_Done_FreeType(freetypeData->library);
- freetypeData->library = nullptr;
+ Q_UNUSED(face_id);
+ bool deleteThis = !ref.deref();
+
+ // If the only reference left over is the cache's reference, we remove it from the cache,
+ // granted that we are on the correct thread. If not, we leave it there to be cleaned out
+ // later. While we are at it, we also purge all left over faces which are only referenced
+ // from the cache.
+ if (face && ref.loadRelaxed() == 1) {
+ QtFreetypeData *freetypeData = qt_getFreetypeData();
+ for (auto it = freetypeData->faces.constBegin(); it != freetypeData->faces.constEnd(); ) {
+ if (it.value()->ref.loadRelaxed() == 1) {
+ it.value()->cleanup();
+ if (it.value() == this)
+ deleteThis = true; // This face, delete at end of function for safety
+ else
+ delete it.value();
+ it = freetypeData->faces.erase(it);
+ } else {
+ ++it;
}
}
- delete this;
+ if (freetypeData->faces.isEmpty()) {
+ FT_Done_FreeType(freetypeData->library);
+ freetypeData->library = nullptr;
+ }
}
+
+ if (deleteThis)
+ delete this;
}
static int computeFaceIndex(const QString &faceFileName, const QString &styleName)
@@ -339,12 +420,12 @@ static int computeFaceIndex(const QString &faceFileName, const QString &styleNam
break;
}
- QString faceStyleName = QString::fromLatin1(face->style_name);
+ const bool found = QLatin1StringView(face->style_name) == styleName;
numFaces = face->num_faces;
FT_Done_Face(face);
- if (faceStyleName == styleName)
+ if (found)
return faceIndex;
} while (++faceIndex < numFaces);
@@ -674,27 +755,32 @@ namespace {
fontDef.weight = QFont::Bold;
}
- bool initFromData(const QByteArray &fontData)
+ bool initFromData(const QByteArray &fontData, const QMap<QFont::Tag, float> &variableAxisValues)
{
FaceId faceId;
faceId.filename = "";
faceId.index = 0;
faceId.uuid = QUuid::createUuid().toByteArray();
+ faceId.variableAxes = variableAxisValues;
return init(faceId, true, Format_None, fontData);
}
};
}
-QFontEngineFT *QFontEngineFT::create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
+QFontEngineFT *QFontEngineFT::create(const QByteArray &fontData,
+ qreal pixelSize,
+ QFont::HintingPreference hintingPreference,
+ const QMap<QFont::Tag, float> &variableAxisValues)
{
QFontDef fontDef;
fontDef.pixelSize = pixelSize;
fontDef.stretch = QFont::Unstretched;
fontDef.hintingPreference = hintingPreference;
+ fontDef.variableAxisValues = variableAxisValues;
QFontEngineFTRawData *fe = new QFontEngineFTRawData(fontDef);
- if (!fe->initFromData(fontData)) {
+ if (!fe->initFromData(fontData, variableAxisValues)) {
delete fe;
return nullptr;
}
@@ -747,6 +833,37 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
static void dont_delete(void*) {}
+static FT_UShort calculateActualWeight(QFreetypeFace *freetypeFace, FT_Face face, QFontEngine::FaceId faceId)
+{
+ FT_MM_Var *var = freetypeFace->mm_var;
+ if (var != nullptr && faceId.instanceIndex >= 0 && FT_UInt(faceId.instanceIndex) < var->num_namedstyles) {
+ for (FT_UInt axis = 0; axis < var->num_axis; ++axis) {
+ if (var->axis[axis].tag == QFont::Tag("wght").value()) {
+ return var->namedstyle[faceId.instanceIndex].coords[axis] >> 16;
+ }
+ }
+ }
+ if (const TT_OS2 *os2 = reinterpret_cast<const TT_OS2 *>(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) {
+ return os2->usWeightClass;
+ }
+
+ return 700;
+}
+
+static bool calculateActualItalic(QFreetypeFace *freetypeFace, FT_Face face, QFontEngine::FaceId faceId)
+{
+ FT_MM_Var *var = freetypeFace->mm_var;
+ if (var != nullptr && faceId.instanceIndex >= 0 && FT_UInt(faceId.instanceIndex) < var->num_namedstyles) {
+ for (FT_UInt axis = 0; axis < var->num_axis; ++axis) {
+ if (var->axis[axis].tag == QFont::Tag("ital").value()) {
+ return (var->namedstyle[faceId.instanceIndex].coords[axis] >> 16) == 1;
+ }
+ }
+ }
+
+ return (face->style_flags & FT_STYLE_FLAG_ITALIC);
+}
+
bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
QFreetypeFace *freetypeFace)
{
@@ -770,7 +887,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
PS_FontInfoRec psrec;
// don't assume that type1 fonts are symbol fonts by default
if (FT_Get_PS_Font_Info(freetype->face, &psrec) == FT_Err_Ok) {
- symbol = !fontDef.families.isEmpty() && bool(fontDef.families.first().contains("symbol"_L1, Qt::CaseInsensitive));
+ symbol = !fontDef.families.isEmpty() && bool(fontDef.families.constFirst().contains("symbol"_L1, Qt::CaseInsensitive));
}
freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing, &scalableBitmapScaleFactor);
@@ -778,18 +895,18 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
FT_Face face = lockFace();
if (FT_IS_SCALABLE(face)) {
- bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !(face->style_flags & FT_STYLE_FLAG_ITALIC) && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_ITALIC");
+ bool isItalic = calculateActualItalic(freetype, face, faceId);
+ bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !isItalic && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_ITALIC");
if (fake_oblique)
obliquen = true;
FT_Set_Transform(face, &matrix, nullptr);
freetype->matrix = matrix;
// fake bold
if ((fontDef.weight >= QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face) && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD")) {
- if (const TT_OS2 *os2 = reinterpret_cast<const TT_OS2 *>(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) {
- if (os2->usWeightClass < 700 &&
- (fontDef.pixelSize < 64 || qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD_LIMIT"))) {
- embolden = true;
- }
+ FT_UShort actualWeight = calculateActualWeight(freetype, face, faceId);
+ if (actualWeight < 700 &&
+ (fontDef.pixelSize < 64 || qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD_LIMIT"))) {
+ embolden = true;
}
}
// underline metrics
@@ -799,16 +916,15 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
} else {
// ad hoc algorithm
int score = fontDef.weight * fontDef.pixelSize;
- line_thickness = score / 700;
+ line_thickness = score / 7000;
// looks better with thicker line for small pointsizes
if (line_thickness < 2 && score >= 1050)
line_thickness = 2;
underline_position = ((line_thickness * 2) + 3) / 6;
- if (isScalableBitmap()) {
+ cacheEnabled = false;
+ if (isScalableBitmap())
glyphFormat = defaultFormat = GlyphFormat::Format_ARGB;
- cacheEnabled = false;
- }
}
if (line_thickness < 1)
line_thickness = 1;
@@ -1254,7 +1370,7 @@ QFontEngine::Properties QFontEngineFT::properties() const
{
Properties p = freetype->properties();
if (p.postscriptName.isEmpty()) {
- p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.families.first().toUtf8());
+ p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.families.constFirst().toUtf8());
}
return freetype->properties();
@@ -1481,7 +1597,7 @@ void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_me
bool QFontEngineFT::supportsTransformation(const QTransform &transform) const
{
- return transform.type() <= QTransform::TxRotate;
+ return transform.type() <= QTransform::TxRotate && (freetype->isScalable() || freetype->isScalableBitmap());
}
void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
@@ -1562,15 +1678,16 @@ glyph_t QFontEngineFT::glyphIndex(uint ucs4) const
return glyph;
}
-bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
+int QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
QFontEngine::ShaperFlags flags) const
{
Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
if (*nglyphs < len) {
*nglyphs = len;
- return false;
+ return -1;
}
+ int mappedGlyphs = 0;
int glyph_pos = 0;
if (freetype->symbol_map) {
FT_Face face = freetype->face;
@@ -1603,6 +1720,8 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs
if (uc < QFreetypeFace::cmapCacheSize)
freetype->cmapCache[uc] = glyph;
}
+ if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
+ mappedGlyphs++;
++glyph_pos;
}
} else {
@@ -1624,6 +1743,8 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs
freetype->cmapCache[uc] = glyph;
}
}
+ if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
+ mappedGlyphs++;
++glyph_pos;
}
}
@@ -1634,7 +1755,7 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs
if (!(flags & GlyphIndicesOnly))
recalcAdvances(glyphs, flags);
- return true;
+ return mappedGlyphs;
}
bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const
@@ -1828,6 +1949,11 @@ glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph,
// When rendering glyphs into a cache via the alphaMap* functions, we disable
// outline drawing. To ensure the bounding box matches the rendered glyph, we
// need to do the same here.
+
+ const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
+ && matrix.type() > QTransform::TxTranslate;
+ if (needsImageTransform && format == QFontEngine::Format_Mono)
+ format = QFontEngine::Format_A8;
Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, true, true);
glyph_metrics_t overall;
@@ -1854,7 +1980,7 @@ glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph,
unlockFace();
}
- if (isScalableBitmap())
+ if (isScalableBitmap() || needsImageTransform)
overall = scaledBitmapMetrics(overall, matrix);
return overall;
}
@@ -1954,12 +2080,17 @@ QImage QFontEngineFT::alphaMapForGlyph(glyph_t g,
const QFixedPoint &subPixelPosition,
const QTransform &t)
{
- const GlyphFormat neededFormat = antialias ? Format_A8 : Format_Mono;
+ const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
+ && t.type() > QTransform::TxTranslate;
+ const GlyphFormat neededFormat = antialias || needsImageTransform ? Format_A8 : Format_Mono;
Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true);
QImage img = alphaMapFromGlyphData(glyph, neededFormat);
- img = img.copy();
+ if (needsImageTransform)
+ img = img.transformed(t, Qt::SmoothTransformation);
+ else
+ img = img.copy();
if (!cacheEnabled && glyph != &emptyGlyph)
delete glyph;
diff --git a/src/gui/text/freetype/qfontengine_ft_p.h b/src/gui/text/freetype/qfontengine_ft_p.h
index 3cc7635838..bdd4549827 100644
--- a/src/gui/text/freetype/qfontengine_ft_p.h
+++ b/src/gui/text/freetype/qfontengine_ft_p.h
@@ -19,6 +19,7 @@
#include <ft2build.h>
#include FT_FREETYPE_H
+#include FT_MULTIPLE_MASTERS_H
#ifndef Q_OS_WIN
@@ -62,6 +63,7 @@ public:
}
FT_Face face;
+ FT_MM_Var *mm_var;
int xsize; // 26.6
int ysize; // 26.6
FT_Matrix matrix;
@@ -75,6 +77,7 @@ public:
int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints);
+ bool isScalable() const;
bool isScalableBitmap() const;
static void addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale);
@@ -183,7 +186,7 @@ private:
void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
QPainterPath *path, QTextItem::RenderFlags flags) override;
- bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override;
+ int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override;
glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) override;
glyph_metrics_t boundingBox(glyph_t glyph) override;
@@ -266,7 +269,7 @@ private:
HintStyle defaultHintStyle() const { return default_hint_style; }
static QFontEngineFT *create(const QFontDef &fontDef, FaceId faceId, const QByteArray &fontData = QByteArray());
- static QFontEngineFT *create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference);
+ static QFontEngineFT *create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference, const QMap<QFont::Tag, float> &variableAxisValue);
protected:
@@ -325,8 +328,6 @@ private:
QFixed scalableBitmapScaleFactor;
};
-Q_DECLARE_TYPEINFO(QFontEngineFT::QGlyphSet, Q_RELOCATABLE_TYPE);
-
inline size_t qHash(const QFontEngineFT::GlyphAndSubPixelPosition &g, size_t seed = 0)
{
diff --git a/src/gui/text/freetype/qfreetypefontdatabase.cpp b/src/gui/text/freetype/qfreetypefontdatabase.cpp
index fd02b80619..018e590ac2 100644
--- a/src/gui/text/freetype/qfreetypefontdatabase.cpp
+++ b/src/gui/text/freetype/qfreetypefontdatabase.cpp
@@ -10,6 +10,8 @@
#include <QtCore/QLibraryInfo>
#include <QtCore/QDir>
#include <QtCore/QtEndian>
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QUuid>
#undef QT_NO_FREETYPE
#include "qfontengine_ft_p.h"
@@ -18,8 +20,14 @@
#include FT_TRUETYPE_TABLES_H
#include FT_ERRORS_H
+#include FT_MULTIPLE_MASTERS_H
+#include FT_SFNT_NAMES_H
+#include FT_TRUETYPE_IDS_H
+
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcFontDb)
+
using namespace Qt::StringLiterals;
void QFreeTypeFontDatabase::populateFontDatabase()
@@ -34,14 +42,14 @@ void QFreeTypeFontDatabase::populateFontDatabase()
return;
}
- QStringList nameFilters;
- nameFilters << "*.ttf"_L1
- << "*.ttc"_L1
- << "*.pfa"_L1
- << "*.pfb"_L1
- << "*.otf"_L1;
+ static const QString nameFilters[] = {
+ u"*.ttf"_s,
+ u"*.pfa"_s,
+ u"*.pfb"_s,
+ u"*.otf"_s,
+ };
- const auto fis = dir.entryInfoList(nameFilters, QDir::Files);
+ const auto fis = dir.entryInfoList(QStringList::fromReadOnlyData(nameFilters), QDir::Files);
for (const QFileInfo &fi : fis) {
const QByteArray file = QFile::encodeName(fi.absoluteFilePath());
QFreeTypeFontDatabase::addTTFile(QByteArray(), file);
@@ -54,14 +62,24 @@ QFontEngine *QFreeTypeFontDatabase::fontEngine(const QFontDef &fontDef, void *us
QFontEngine::FaceId faceId;
faceId.filename = QFile::encodeName(fontfile->fileName);
faceId.index = fontfile->indexValue;
+ faceId.instanceIndex = fontfile->instanceIndex;
+ faceId.variableAxes = fontDef.variableAxisValues;
+
+ // Make sure the FaceId compares uniquely in cases where a
+ // file name is not provided.
+ if (faceId.filename.isEmpty()) {
+ QUuid::Id128Bytes id{};
+ memcpy(&id, &usrPtr, sizeof(usrPtr));
+ faceId.uuid = QUuid(id).toByteArray();
+ }
- return QFontEngineFT::create(fontDef, faceId);
+ return QFontEngineFT::create(fontDef, faceId, fontfile->data);
}
QFontEngine *QFreeTypeFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize,
QFont::HintingPreference hintingPreference)
{
- return QFontEngineFT::create(fontData, pixelSize, hintingPreference);
+ return QFontEngineFT::create(fontData, pixelSize, hintingPreference, {});
}
QStringList QFreeTypeFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont)
@@ -77,6 +95,114 @@ void QFreeTypeFontDatabase::releaseHandle(void *handle)
extern FT_Library qt_getFreetype();
+void QFreeTypeFontDatabase::addNamedInstancesForFace(void *face_,
+ int faceIndex,
+ const QString &family,
+ const QString &styleName,
+ QFont::Weight weight,
+ QFont::Stretch stretch,
+ QFont::Style style,
+ bool fixedPitch,
+ const QSupportedWritingSystems &writingSystems,
+ const QByteArray &fileName,
+ const QByteArray &fontData)
+{
+ FT_Face face = reinterpret_cast<FT_Face>(face_);
+
+ // Note: The following does not actually depend on API from 2.9, but the
+ // FT_Set_Named_Instance() was added in 2.9, so to avoid populating the database with
+ // named instances that cannot be selected, we disable the feature on older Freetype
+ // versions.
+#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
+ FT_MM_Var *var = nullptr;
+ FT_Get_MM_Var(face, &var);
+ if (var != nullptr) {
+ std::unique_ptr<FT_MM_Var, void(*)(FT_MM_Var*)> varGuard(var, [](FT_MM_Var *res) {
+ FT_Done_MM_Var(qt_getFreetype(), res);
+ });
+
+ for (FT_UInt i = 0; i < var->num_namedstyles; ++i) {
+ FT_UInt id = var->namedstyle[i].strid;
+
+ QFont::Weight instanceWeight = weight;
+ QFont::Stretch instanceStretch = stretch;
+ QFont::Style instanceStyle = style;
+ for (FT_UInt axis = 0; axis < var->num_axis; ++axis) {
+ if (var->axis[axis].tag == QFont::Tag("wght").value()) {
+ instanceWeight = QFont::Weight(var->namedstyle[i].coords[axis] >> 16);
+ } else if (var->axis[axis].tag == QFont::Tag("wdth").value()) {
+ instanceStretch = QFont::Stretch(var->namedstyle[i].coords[axis] >> 16);
+ } else if (var->axis[axis].tag == QFont::Tag("ital").value()) {
+ FT_UInt ital = var->namedstyle[i].coords[axis] >> 16;
+ if (ital == 1)
+ instanceStyle = QFont::StyleItalic;
+ else
+ instanceStyle = QFont::StyleNormal;
+ }
+ }
+
+ FT_UInt count = FT_Get_Sfnt_Name_Count(face);
+ for (FT_UInt j = 0; j < count; ++j) {
+ FT_SfntName name;
+ if (FT_Get_Sfnt_Name(face, j, &name))
+ continue;
+
+ if (name.name_id != id)
+ continue;
+
+ // Only support Unicode for now
+ if (name.encoding_id != TT_MS_ID_UNICODE_CS)
+ continue;
+
+ // Sfnt names stored as UTF-16BE
+ QString instanceName;
+ for (FT_UInt k = 0; k < name.string_len; k += 2)
+ instanceName += QChar((name.string[k] << 8) + name.string[k + 1]);
+ if (instanceName != styleName) {
+ FontFile *variantFontFile = new FontFile{
+ QFile::decodeName(fileName),
+ faceIndex,
+ int(i),
+ fontData
+ };
+
+ qCDebug(lcFontDb) << "Registering named instance" << i
+ << ":" << instanceName
+ << "for font family" << family
+ << "with weight" << instanceWeight
+ << ", style" << instanceStyle
+ << ", stretch" << instanceStretch;
+
+ registerFont(family,
+ instanceName,
+ QString(),
+ instanceWeight,
+ instanceStyle,
+ instanceStretch,
+ true,
+ true,
+ 0,
+ fixedPitch,
+ writingSystems,
+ variantFontFile);
+ }
+ }
+ }
+ }
+#else
+ Q_UNUSED(face);
+ Q_UNUSED(family);
+ Q_UNUSED(styleName);
+ Q_UNUSED(weight);
+ Q_UNUSED(stretch);
+ Q_UNUSED(style);
+ Q_UNUSED(fixedPitch);
+ Q_UNUSED(writingSystems);
+ Q_UNUSED(fontData);
+#endif
+
+}
+
QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const QByteArray &file, QFontDatabasePrivate::ApplicationFont *applicationFont)
{
FT_Library library = qt_getFreetype();
@@ -191,9 +317,12 @@ QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const Q
}
QString family = QString::fromLatin1(face->family_name);
- FontFile *fontFile = new FontFile;
- fontFile->fileName = QFile::decodeName(file);
- fontFile->indexValue = index;
+ FontFile *fontFile = new FontFile{
+ QFile::decodeName(file),
+ index,
+ -1,
+ fontData
+ };
QString styleName = QString::fromLatin1(face->style_name);
@@ -209,6 +338,9 @@ QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const Q
}
registerFont(family, styleName, QString(), weight, style, stretch, true, true, 0, fixedPitch, writingSystems, fontFile);
+
+ addNamedInstancesForFace(face, index, family, styleName, weight, stretch, style, fixedPitch, writingSystems, file, fontData);
+
families.append(family);
FT_Done_Face(face);
@@ -217,4 +349,13 @@ QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const Q
return families;
}
+bool QFreeTypeFontDatabase::supportsVariableApplicationFonts() const
+{
+#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
+ return true;
+#else
+ return false;
+#endif
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/text/freetype/qfreetypefontdatabase_p.h b/src/gui/text/freetype/qfreetypefontdatabase_p.h
index f9ad0824b9..5fcec585d2 100644
--- a/src/gui/text/freetype/qfreetypefontdatabase_p.h
+++ b/src/gui/text/freetype/qfreetypefontdatabase_p.h
@@ -26,6 +26,12 @@ struct FontFile
{
QString fileName;
int indexValue;
+ int instanceIndex = -1;
+
+ // Note: The data may be implicitly shared throughout the
+ // font database and platform font database, so be careful
+ // to never detach when accessing this member!
+ const QByteArray data;
};
class Q_GUI_EXPORT QFreeTypeFontDatabase : public QPlatformFontDatabase
@@ -36,6 +42,14 @@ public:
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override;
QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr) override;
void releaseHandle(void *handle) override;
+ bool supportsVariableApplicationFonts() const override;
+
+ static void addNamedInstancesForFace(void *face, int faceIndex,
+ const QString &family, const QString &styleName,
+ QFont::Weight weight, QFont::Stretch stretch,
+ QFont::Style style, bool fixedPitch,
+ const QSupportedWritingSystems &writingSystems,
+ const QByteArray &fileName, const QByteArray &fontData);
static QStringList addTTFile(const QByteArray &fontData, const QByteArray &file, QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr);
};
diff --git a/src/gui/text/qabstracttextdocumentlayout.cpp b/src/gui/text/qabstracttextdocumentlayout.cpp
index 18f7b9b1ee..3e5144b157 100644
--- a/src/gui/text/qabstracttextdocumentlayout.cpp
+++ b/src/gui/text/qabstracttextdocumentlayout.cpp
@@ -101,7 +101,7 @@ QTextObjectInterface::~QTextObjectInterface()
\warning Copy and Paste operations ignore custom text objects.
- \sa {Text Object Example}, QTextCharFormat, QTextLayout
+ \sa QTextCharFormat, QTextLayout
*/
/*!
@@ -602,7 +602,7 @@ QTextFormat QAbstractTextDocumentLayout::formatAt(const QPointF &pos) const
if (blockBr.contains(pos)) {
QTextLayout *layout = block.layout();
int relativeCursorPos = cursorPos - block.position();
- const int preeditLength = layout ? layout->preeditAreaText().length() : 0;
+ const int preeditLength = layout ? layout->preeditAreaText().size() : 0;
if (preeditLength > 0 && relativeCursorPos > layout->preeditAreaPosition())
cursorPos -= qMin(cursorPos - layout->preeditAreaPosition(), preeditLength);
break;
diff --git a/src/gui/text/qabstracttextdocumentlayout.h b/src/gui/text/qabstracttextdocumentlayout.h
index 67eea69f28..dfafb1098f 100644
--- a/src/gui/text/qabstracttextdocumentlayout.h
+++ b/src/gui/text/qabstracttextdocumentlayout.h
@@ -107,7 +107,7 @@ public:
virtual void drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format) = 0;
};
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
Q_DECLARE_INTERFACE(QTextObjectInterface, "org.qt-project.Qt.QTextObjectInterface")
#endif
diff --git a/src/gui/text/qabstracttextdocumentlayout_p.h b/src/gui/text/qabstracttextdocumentlayout_p.h
index 34eccd89af..6bd42d78d8 100644
--- a/src/gui/text/qabstracttextdocumentlayout_p.h
+++ b/src/gui/text/qabstracttextdocumentlayout_p.h
@@ -18,7 +18,10 @@
#include <QtGui/private/qtguiglobal_p.h>
#include "private/qobject_p.h"
#include "qtextdocument_p.h"
+#include "qabstracttextdocumentlayout.h"
+
#include "QtCore/qhash.h"
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -46,6 +49,16 @@ public:
docPrivate = QTextDocumentPrivate::get(doc);
}
+ static QAbstractTextDocumentLayoutPrivate *get(QAbstractTextDocumentLayout *layout)
+ {
+ return layout->d_func();
+ }
+
+ bool hasHandlers() const
+ {
+ return !handlers.isEmpty();
+ }
+
inline int _q_dynamicPageCountSlot() const
{ return q_func()->pageCount(); }
inline QSizeF _q_dynamicDocumentSizeSlot() const
diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp
index eff9ffbc8c..e5815ffce2 100644
--- a/src/gui/text/qcssparser.cpp
+++ b/src/gui/text/qcssparser.cpp
@@ -39,14 +39,18 @@ static const QCssKnownValue properties[NumProperties - 1] = {
{ "-qt-background-role", QtBackgroundRole },
{ "-qt-block-indent", QtBlockIndent },
{ "-qt-fg-texture-cachekey", QtForegroundTextureCacheKey },
+ { "-qt-foreground", QtForeground },
{ "-qt-line-height-type", QtLineHeightType },
{ "-qt-list-indent", QtListIndent },
{ "-qt-list-number-prefix", QtListNumberPrefix },
{ "-qt-list-number-suffix", QtListNumberSuffix },
{ "-qt-paragraph-type", QtParagraphType },
+ { "-qt-stroke-color", QtStrokeColor },
+ { "-qt-stroke-width", QtStrokeWidth },
{ "-qt-style-features", QtStyleFeatures },
{ "-qt-table-type", QtTableType },
{ "-qt-user-state", QtUserState },
+ { "accent-color", QtAccent },
{ "alternate-background-color", QtAlternateBackground },
{ "background", Background },
{ "background-attachment", BackgroundAttachment },
@@ -128,6 +132,7 @@ static const QCssKnownValue properties[NumProperties - 1] = {
{ "padding-top", PaddingTop },
{ "page-break-after", PageBreakAfter },
{ "page-break-before", PageBreakBefore },
+ { "placeholder-text-color", QtPlaceHolderTextColor },
{ "position", Position },
{ "right", Right },
{ "selection-background-color", QtSelectionBackground },
@@ -408,7 +413,7 @@ int ValueExtractor::lengthValue(const Declaration &decl)
{
if (decl.d->parsed.isValid())
return lengthValueFromData(qvariant_cast<LengthData>(decl.d->parsed), f);
- if (decl.d->values.count() < 1)
+ if (decl.d->values.size() < 1)
return 0;
LengthData data = lengthValue(decl.d->values.at(0));
decl.d->parsed = QVariant::fromValue<LengthData>(data);
@@ -427,7 +432,7 @@ void ValueExtractor::lengthValues(const Declaration &decl, int *m)
LengthData datas[4];
int i;
- for (i = 0; i < qMin(decl.d->values.count(), 4); i++)
+ for (i = 0; i < qMin(decl.d->values.size(), 4); i++)
datas[i] = lengthValue(decl.d->values[i]);
if (i == 0) {
@@ -455,7 +460,7 @@ bool ValueExtractor::extractGeometry(int *w, int *h, int *minw, int *minh, int *
{
extractFont();
bool hit = false;
- for (int i = 0; i < declarations.count(); i++) {
+ for (int i = 0; i < declarations.size(); i++) {
const Declaration &decl = declarations.at(i);
switch (decl.d->propertyId) {
case Width: *w = lengthValue(decl); break;
@@ -477,7 +482,7 @@ bool ValueExtractor::extractPosition(int *left, int *top, int *right, int *botto
{
extractFont();
bool hit = false;
- for (int i = 0; i < declarations.count(); i++) {
+ for (int i = 0; i < declarations.size(); i++) {
const Declaration &decl = declarations.at(i);
switch (decl.d->propertyId) {
case Left: *left = lengthValue(decl); break;
@@ -500,7 +505,7 @@ bool ValueExtractor::extractBox(int *margins, int *paddings, int *spacing)
{
extractFont();
bool hit = false;
- for (int i = 0; i < declarations.count(); i++) {
+ for (int i = 0; i < declarations.size(); i++) {
const Declaration &decl = declarations.at(i);
switch (decl.d->propertyId) {
case PaddingLeft: paddings[LeftEdge] = lengthValue(decl); break;
@@ -527,7 +532,7 @@ bool ValueExtractor::extractBox(int *margins, int *paddings, int *spacing)
int ValueExtractor::extractStyleFeatures()
{
int features = StyleFeature_None;
- for (int i = 0; i < declarations.count(); i++) {
+ for (int i = 0; i < declarations.size(); i++) {
const Declaration &decl = declarations.at(i);
if (decl.d->propertyId == QtStyleFeatures)
features = decl.styleFeaturesValue();
@@ -544,9 +549,9 @@ QSize ValueExtractor::sizeValue(const Declaration &decl)
}
LengthData x[2] = { {0, LengthData::None }, {0, LengthData::None} };
- if (decl.d->values.count() > 0)
+ if (decl.d->values.size() > 0)
x[0] = lengthValue(decl.d->values.at(0));
- if (decl.d->values.count() > 1)
+ if (decl.d->values.size() > 1)
x[1] = lengthValue(decl.d->values.at(1));
else
x[1] = x[0];
@@ -568,7 +573,7 @@ bool ValueExtractor::extractBorder(int *borders, QBrush *colors, BorderStyle *st
{
extractFont();
bool hit = false;
- for (int i = 0; i < declarations.count(); i++) {
+ for (int i = 0; i < declarations.size(); i++) {
const Declaration &decl = declarations.at(i);
switch (decl.d->propertyId) {
case BorderLeftWidth: borders[LeftEdge] = lengthValue(decl); break;
@@ -627,7 +632,7 @@ bool ValueExtractor::extractOutline(int *borders, QBrush *colors, BorderStyle *s
{
extractFont();
bool hit = false;
- for (int i = 0; i < declarations.count(); i++) {
+ for (int i = 0; i < declarations.size(); i++) {
const Declaration &decl = declarations.at(i);
switch (decl.d->propertyId) {
case OutlineWidth: lengthValues(decl, borders); break;
@@ -696,7 +701,7 @@ static ColorData parseColorValue(QCss::Value v)
return ColorData();
QStringList lst = v.variant.toStringList();
- if (lst.count() != 2)
+ if (lst.size() != 2)
return ColorData();
const QString &identifier = lst.at(0);
@@ -726,7 +731,7 @@ static ColorData parseColorValue(QCss::Value v)
QList<QCss::Value> colorDigits;
if (!p.parseExpr(&colorDigits))
return ColorData();
- const int tokenCount = colorDigits.count();
+ const int tokenCount = colorDigits.size();
for (int i = 0; i < qMin(tokenCount, 7); i += 2) {
if (colorDigits.at(i).type == Value::Percentage) {
@@ -793,7 +798,7 @@ static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
return BrushData();
QStringList lst = v.variant.toStringList();
- if (lst.count() != 2)
+ if (lst.size() != 2)
return BrushData();
QStringList gradFuncs;
@@ -810,6 +815,10 @@ static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
QStringList spreads;
spreads << "pad"_L1 << "reflect"_L1 << "repeat"_L1;
+ int coordinateMode = -1;
+ QStringList coordinateModes;
+ coordinateModes << "logical"_L1 << "stretchtodevice"_L1 << "objectbounding"_L1 << "object"_L1;
+
bool dependsOnThePalette = false;
Parser parser(lst.at(1));
while (parser.hasNext()) {
@@ -836,11 +845,12 @@ static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
parser.next();
QCss::Value value;
(void)parser.parseTerm(&value);
- if (attr.compare("spread"_L1, Qt::CaseInsensitive) == 0) {
+ if (attr.compare("spread"_L1, Qt::CaseInsensitive) == 0)
spread = spreads.indexOf(value.variant.toString());
- } else {
+ else if (attr.compare("coordinatemode"_L1, Qt::CaseInsensitive) == 0)
+ coordinateMode = coordinateModes.indexOf(value.variant.toString());
+ else
vars[attr] = value.variant.toReal();
- }
}
parser.skipSpace();
(void)parser.test(COMMA);
@@ -849,7 +859,7 @@ static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
if (gradType == 0) {
QLinearGradient lg(vars.value("x1"_L1), vars.value("y1"_L1),
vars.value("x2"_L1), vars.value("y2"_L1));
- lg.setCoordinateMode(QGradient::ObjectBoundingMode);
+ lg.setCoordinateMode(coordinateMode < 0 ? QGradient::ObjectBoundingMode : QGradient::CoordinateMode(coordinateMode));
lg.setStops(stops);
if (spread != -1)
lg.setSpread(QGradient::Spread(spread));
@@ -863,7 +873,7 @@ static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
QRadialGradient rg(vars.value("cx"_L1), vars.value("cy"_L1),
vars.value("radius"_L1), vars.value("fx"_L1),
vars.value("fy"_L1));
- rg.setCoordinateMode(QGradient::ObjectBoundingMode);
+ rg.setCoordinateMode(coordinateMode < 0 ? QGradient::ObjectBoundingMode : QGradient::CoordinateMode(coordinateMode));
rg.setStops(stops);
if (spread != -1)
rg.setSpread(QGradient::Spread(spread));
@@ -875,7 +885,7 @@ static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
if (gradType == 2) {
QConicalGradient cg(vars.value("cx"_L1), vars.value("cy"_L1), vars.value("angle"_L1));
- cg.setCoordinateMode(QGradient::ObjectBoundingMode);
+ cg.setCoordinateMode(coordinateMode < 0 ? QGradient::ObjectBoundingMode : QGradient::CoordinateMode(coordinateMode));
cg.setStops(stops);
if (spread != -1)
cg.setSpread(QGradient::Spread(spread));
@@ -959,7 +969,7 @@ void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::Bord
if (decl.d->values.at(i).type == Value::Length || decl.d->values.at(i).type == Value::Number) {
data.width = lengthValue(decl.d->values.at(i));
*width = lengthValueFromData(data.width, f);
- if (++i >= decl.d->values.count()) {
+ if (++i >= decl.d->values.size()) {
decl.d->parsed = QVariant::fromValue<BorderData>(data);
return;
}
@@ -968,7 +978,7 @@ void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::Bord
data.style = parseStyleValue(decl.d->values.at(i));
if (data.style != BorderStyle_Unknown) {
*style = data.style;
- if (++i >= decl.d->values.count()) {
+ if (++i >= decl.d->values.size()) {
decl.d->parsed = QVariant::fromValue<BorderData>(data);
return;
}
@@ -977,9 +987,11 @@ void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::Bord
}
data.color = parseBrushValue(decl.d->values.at(i), pal);
- *color = brushFromData(data.color, pal);
- if (data.color.type != BrushData::DependsOnThePalette)
- decl.d->parsed = QVariant::fromValue<BorderData>(data);
+ if (data.color.type != BrushData::Invalid) {
+ *color = brushFromData(data.color, pal);
+ if (data.color.type != BrushData::DependsOnThePalette)
+ decl.d->parsed = QVariant::fromValue<BorderData>(data);
+ }
}
static void parseShorthandBackgroundProperty(const QList<QCss::Value> &values, BrushData *brush, QString *image, Repeat *repeat, Qt::Alignment *alignment, const QPalette &pal)
@@ -989,7 +1001,7 @@ static void parseShorthandBackgroundProperty(const QList<QCss::Value> &values, B
*repeat = Repeat_XY;
*alignment = Qt::AlignTop | Qt::AlignLeft;
- for (int i = 0; i < values.count(); ++i) {
+ for (int i = 0; i < values.size(); ++i) {
const QCss::Value &v = values.at(i);
if (v.type == Value::Uri) {
*image = v.variant.toString();
@@ -1011,7 +1023,7 @@ static void parseShorthandBackgroundProperty(const QList<QCss::Value> &values, B
if (v.type == Value::KnownIdentifier) {
const int start = i;
int count = 1;
- if (i < values.count() - 1
+ if (i < values.size() - 1
&& values.at(i + 1).type == Value::KnownIdentifier) {
++i;
++count;
@@ -1033,7 +1045,7 @@ bool ValueExtractor::extractBackground(QBrush *brush, QString *image, Repeat *re
Origin *clip)
{
bool hit = false;
- for (int i = 0; i < declarations.count(); ++i) {
+ for (int i = 0; i < declarations.size(); ++i) {
const Declaration &decl = declarations.at(i);
if (decl.d->values.isEmpty())
continue;
@@ -1181,7 +1193,7 @@ static bool setFontFamilyFromValues(const QList<QCss::Value> &values, QFont *fon
QString family;
QStringList families;
bool shouldAddSpace = false;
- for (int i = start; i < values.count(); ++i) {
+ for (int i = start; i < values.size(); ++i) {
const QCss::Value &v = values.at(i);
if (v.type == Value::TermOperatorComma) {
families << family;
@@ -1207,7 +1219,7 @@ static bool setFontFamilyFromValues(const QList<QCss::Value> &values, QFont *fon
static void setTextDecorationFromValues(const QList<QCss::Value> &values, QFont *font)
{
- for (int i = 0; i < values.count(); ++i) {
+ for (int i = 0; i < values.size(); ++i) {
if (values.at(i).type != Value::KnownIdentifier)
continue;
switch (values.at(i).variant.toInt()) {
@@ -1262,7 +1274,7 @@ static void parseShorthandFontProperty(const QList<QCss::Value> &values, QFont *
*fontSizeAdjustment = -255;
int i = 0;
- while (i < values.count()) {
+ while (i < values.size()) {
if (setFontStyleFromValue(values.at(i), font)
|| setFontWeightFromValue(values.at(i), font))
++i;
@@ -1270,12 +1282,12 @@ static void parseShorthandFontProperty(const QList<QCss::Value> &values, QFont *
break;
}
- if (i < values.count()) {
+ if (i < values.size()) {
setFontSizeFromValue(values.at(i), font, fontSizeAdjustment);
++i;
}
- if (i < values.count()) {
+ if (i < values.size()) {
setFontFamilyFromValues(values, font, i);
}
}
@@ -1312,7 +1324,7 @@ bool ValueExtractor::extractFont(QFont *font, int *fontSizeAdjustment)
}
bool hit = false;
- for (int i = 0; i < declarations.count(); ++i) {
+ for (int i = 0; i < declarations.size(); ++i) {
const Declaration &decl = declarations.at(i);
if (decl.d->values.isEmpty())
continue;
@@ -1340,16 +1352,23 @@ bool ValueExtractor::extractFont(QFont *font, int *fontSizeAdjustment)
return hit;
}
-bool ValueExtractor::extractPalette(QBrush *fg, QBrush *sfg, QBrush *sbg, QBrush *abg)
+bool ValueExtractor::extractPalette(QBrush *foreground,
+ QBrush *selectedForeground,
+ QBrush *selectedBackground,
+ QBrush *alternateBackground,
+ QBrush *placeHolderTextForeground,
+ QBrush *accent)
{
bool hit = false;
- for (int i = 0; i < declarations.count(); ++i) {
+ for (int i = 0; i < declarations.size(); ++i) {
const Declaration &decl = declarations.at(i);
switch (decl.d->propertyId) {
- case Color: *fg = decl.brushValue(pal); break;
- case QtSelectionForeground: *sfg = decl.brushValue(pal); break;
- case QtSelectionBackground: *sbg = decl.brushValue(pal); break;
- case QtAlternateBackground: *abg = decl.brushValue(pal); break;
+ case Color: *foreground = decl.brushValue(pal); break;
+ case QtSelectionForeground: *selectedForeground = decl.brushValue(pal); break;
+ case QtSelectionBackground: *selectedBackground = decl.brushValue(pal); break;
+ case QtAlternateBackground: *alternateBackground = decl.brushValue(pal); break;
+ case QtPlaceHolderTextColor: *placeHolderTextForeground = decl.brushValue(pal); break;
+ case QtAccent: *accent = decl.brushValue(pal); break;
default: continue;
}
hit = true;
@@ -1368,12 +1387,12 @@ void ValueExtractor::extractFont()
bool ValueExtractor::extractImage(QIcon *icon, Qt::Alignment *a, QSize *size)
{
bool hit = false;
- for (int i = 0; i < declarations.count(); ++i) {
+ for (int i = 0; i < declarations.size(); ++i) {
const Declaration &decl = declarations.at(i);
switch (decl.d->propertyId) {
case QtImage:
*icon = decl.iconValue();
- if (decl.d->values.count() > 0 && decl.d->values.at(0).type == Value::Uri) {
+ if (decl.d->values.size() > 0 && decl.d->values.at(0).type == Value::Uri) {
// try to pull just the size from the image...
QImageReader imageReader(decl.d->values.at(0).variant.toString());
if ((*size = imageReader.size()).isNull()) {
@@ -1426,7 +1445,7 @@ bool ValueExtractor::extractIcon(QIcon *icon, QSize *size)
// Declaration
QColor Declaration::colorValue(const QPalette &pal) const
{
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return QColor();
if (d->parsed.isValid()) {
@@ -1437,7 +1456,8 @@ QColor Declaration::colorValue(const QPalette &pal) const
return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
case qMetaTypeId<QList<QVariant>>():
if (d->parsed.toList().size() == 1) {
- const auto &value = d->parsed.toList().at(0);
+ auto parsedList = d->parsed.toList();
+ const auto &value = parsedList.at(0);
return qvariant_cast<QColor>(value);
}
break;
@@ -1456,7 +1476,7 @@ QColor Declaration::colorValue(const QPalette &pal) const
QBrush Declaration::brushValue(const QPalette &pal) const
{
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return QBrush();
if (d->parsed.isValid()) {
@@ -1487,7 +1507,7 @@ void Declaration::brushValues(QBrush *c, const QPalette &pal) const
needParse = 0;
Q_ASSERT(d->parsed.metaType() == QMetaType::fromType<QList<QVariant>>());
QList<QVariant> v = d->parsed.toList();
- for (i = 0; i < qMin(v.count(), 4); i++) {
+ for (i = 0; i < qMin(v.size(), 4); i++) {
if (v.at(i).userType() == QMetaType::QBrush) {
c[i] = qvariant_cast<QBrush>(v.at(i));
} else if (v.at(i).userType() == QMetaType::Int) {
@@ -1499,7 +1519,7 @@ void Declaration::brushValues(QBrush *c, const QPalette &pal) const
}
if (needParse != 0) {
QList<QVariant> v;
- for (i = 0; i < qMin(d->values.count(), 4); i++) {
+ for (i = 0; i < qMin(d->values.size(), 4); i++) {
if (!(needParse & (1<<i)))
continue;
BrushData data = parseBrushValue(d->values.at(i), pal);
@@ -1526,7 +1546,7 @@ void Declaration::brushValues(QBrush *c, const QPalette &pal) const
bool Declaration::realValue(qreal *real, const char *unit) const
{
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return false;
const Value &v = d->values.at(0);
if (unit && v.type != Value::Length)
@@ -1567,7 +1587,7 @@ static bool intValueHelper(const QCss::Value &v, int *i, const char *unit)
bool Declaration::intValue(int *i, const char *unit) const
{
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return false;
return intValueHelper(d->values.at(0), i, unit);
}
@@ -1578,7 +1598,7 @@ QSize Declaration::sizeValue() const
return qvariant_cast<QSize>(d->parsed);
int x[2] = { 0, 0 };
- const int count = d->values.count();
+ const int count = d->values.size();
for (int i = 0; i < count; ++i) {
if (i > 1) {
qWarning("QCssParser::sizeValue: Too many values provided");
@@ -1605,7 +1625,7 @@ QSize Declaration::sizeValue() const
QRect Declaration::rectValue() const
{
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return QRect();
if (d->parsed.isValid())
@@ -1615,10 +1635,10 @@ QRect Declaration::rectValue() const
if (v.type != Value::Function)
return QRect();
const QStringList func = v.variant.toStringList();
- if (func.count() != 2 || func.at(0).compare("rect"_L1) != 0)
+ if (func.size() != 2 || func.at(0).compare("rect"_L1) != 0)
return QRect();
const auto args = QStringView{func[1]}.split(u' ', Qt::SkipEmptyParts);
- if (args.count() != 4)
+ if (args.size() != 4)
return QRect();
QRect rect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt());
d->parsed = QVariant::fromValue<QRect>(rect);
@@ -1630,7 +1650,7 @@ void Declaration::colorValues(QColor *c, const QPalette &pal) const
int i;
if (d->parsed.isValid()) {
QList<QVariant> v = d->parsed.toList();
- for (i = 0; i < qMin(d->values.count(), 4); i++) {
+ for (i = 0; i < qMin(d->values.size(), 4); i++) {
if (v.at(i).userType() == QMetaType::QColor) {
c[i] = qvariant_cast<QColor>(v.at(i));
} else {
@@ -1639,7 +1659,7 @@ void Declaration::colorValues(QColor *c, const QPalette &pal) const
}
} else {
QList<QVariant> v;
- for (i = 0; i < qMin(d->values.count(), 4); i++) {
+ for (i = 0; i < qMin(d->values.size(), 4); i++) {
ColorData color = parseColorValue(d->values.at(i));
if (color.type == ColorData::Role) {
v += QVariant::fromValue<int>(color.role);
@@ -1660,7 +1680,7 @@ void Declaration::colorValues(QColor *c, const QPalette &pal) const
BorderStyle Declaration::styleValue() const
{
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return BorderStyle_None;
return parseStyleValue(d->values.at(0));
}
@@ -1668,7 +1688,7 @@ BorderStyle Declaration::styleValue() const
void Declaration::styleValues(BorderStyle *s) const
{
int i;
- for (i = 0; i < qMin(d->values.count(), 4); i++)
+ for (i = 0; i < qMin(d->values.size(), 4); i++)
s[i] = parseStyleValue(d->values.at(i));
if (i == 0) s[0] = s[1] = s[2] = s[3] = BorderStyle_None;
else if (i == 1) s[3] = s[2] = s[1] = s[0];
@@ -1680,7 +1700,7 @@ Repeat Declaration::repeatValue() const
{
if (d->parsed.isValid())
return static_cast<Repeat>(d->parsed.toInt());
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return Repeat_Unknown;
int v = findKnownValue(d->values.at(0).variant.toString(),
repeats, NumKnownRepeats);
@@ -1692,7 +1712,7 @@ Origin Declaration::originValue() const
{
if (d->parsed.isValid())
return static_cast<Origin>(d->parsed.toInt());
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return Origin_Unknown;
int v = findKnownValue(d->values.at(0).variant.toString(),
origins, NumKnownOrigins);
@@ -1704,7 +1724,7 @@ PositionMode Declaration::positionValue() const
{
if (d->parsed.isValid())
return static_cast<PositionMode>(d->parsed.toInt());
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return PositionMode_Unknown;
int v = findKnownValue(d->values.at(0).variant.toString(),
positions, NumKnownPositionModes);
@@ -1716,7 +1736,7 @@ Attachment Declaration::attachmentValue() const
{
if (d->parsed.isValid())
return static_cast<Attachment>(d->parsed.toInt());
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return Attachment_Unknown;
int v = findKnownValue(d->values.at(0).variant.toString(),
attachments, NumKnownAttachments);
@@ -1730,7 +1750,7 @@ int Declaration::styleFeaturesValue() const
if (d->parsed.isValid())
return d->parsed.toInt();
int features = StyleFeature_None;
- for (int i = 0; i < d->values.count(); i++) {
+ for (int i = 0; i < d->values.size(); i++) {
features |= static_cast<int>(findKnownValue(d->values.value(i).variant.toString(),
styleFeatures, NumKnownStyleFeatures));
}
@@ -1749,10 +1769,10 @@ Qt::Alignment Declaration::alignmentValue() const
{
if (d->parsed.isValid())
return Qt::Alignment(d->parsed.toInt());
- if (d->values.isEmpty() || d->values.count() > 2)
+ if (d->values.isEmpty() || d->values.size() > 2)
return Qt::AlignLeft | Qt::AlignTop;
- Qt::Alignment v = parseAlignment(d->values.constData(), d->values.count());
+ Qt::Alignment v = parseAlignment(d->values.constData(), d->values.size());
d->parsed = int(v);
return v;
}
@@ -1766,12 +1786,12 @@ void Declaration::borderImageValue(QString *image, int *cuts,
cuts[i] = -1;
*h = *v = TileMode_Stretch;
- if (d->values.count() < 2)
+ if (d->values.size() < 2)
return;
if (d->values.at(1).type == Value::Number) { // cuts!
int i;
- for (i = 0; i < qMin(d->values.count()-1, 4); i++) {
+ for (i = 0; i < qMin(d->values.size()-1, 4); i++) {
const Value& v = d->values.at(i+1);
if (v.type != Value::Number)
break;
@@ -1787,9 +1807,9 @@ void Declaration::borderImageValue(QString *image, int *cuts,
*v = static_cast<TileMode>(findKnownValue(d->values.last().variant.toString(),
tileModes, NumKnownTileModes));
}
- if (d->values[d->values.count() - 2].type == Value::Identifier) {
+ if (d->values[d->values.size() - 2].type == Value::Identifier) {
*h = static_cast<TileMode>
- (findKnownValue(d->values[d->values.count()-2].variant.toString(),
+ (findKnownValue(d->values[d->values.size()-2].variant.toString(),
tileModes, NumKnownTileModes));
} else
*h = *v;
@@ -1797,7 +1817,7 @@ void Declaration::borderImageValue(QString *image, int *cuts,
bool Declaration::borderCollapseValue() const
{
- if (d->values.count() != 1)
+ if (d->values.size() != 1)
return false;
else
return d->values.at(0).toString() == "collapse"_L1;
@@ -1809,7 +1829,7 @@ QIcon Declaration::iconValue() const
return qvariant_cast<QIcon>(d->parsed);
QIcon icon;
- for (int i = 0; i < d->values.count();) {
+ for (int i = 0; i < d->values.size();) {
const Value &value = d->values.at(i++);
if (value.type != Value::Uri)
break;
@@ -1817,7 +1837,7 @@ QIcon Declaration::iconValue() const
QIcon::Mode mode = QIcon::Normal;
QIcon::State state = QIcon::Off;
for (int j = 0; j < 2; j++) {
- if (i != d->values.count() && d->values.at(i).type == Value::KnownIdentifier) {
+ if (i != d->values.size() && d->values.at(i).type == Value::KnownIdentifier) {
switch (d->values.at(i).variant.toInt()) {
case Value_Disabled: mode = QIcon::Disabled; break;
case Value_Active: mode = QIcon::Active; break;
@@ -1839,7 +1859,7 @@ QIcon Declaration::iconValue() const
else
icon.addPixmap(uri, mode, state);
- if (i == d->values.count())
+ if (i == d->values.size())
break;
if (d->values.at(i).type == Value::TermOperatorComma)
@@ -1855,13 +1875,13 @@ QIcon Declaration::iconValue() const
int Selector::specificity() const
{
int val = 0;
- for (int i = 0; i < basicSelectors.count(); ++i) {
+ for (int i = 0; i < basicSelectors.size(); ++i) {
const BasicSelector &sel = basicSelectors.at(i);
if (!sel.elementName.isEmpty())
val += 1;
- val += (sel.pseudos.count() + sel.attributeSelectors.count()) * 0x10;
- val += sel.ids.count() * 0x100;
+ val += (sel.pseudos.size() + sel.attributeSelectors.size()) * 0x10;
+ val += sel.ids.size() * 0x100;
}
return val;
}
@@ -1880,7 +1900,7 @@ quint64 Selector::pseudoClass(quint64 *negated) const
if (bs.pseudos.isEmpty())
return PseudoClass_Unspecified;
quint64 pc = PseudoClass_Unknown;
- for (int i = !pseudoElement().isEmpty(); i < bs.pseudos.count(); i++) {
+ for (int i = !pseudoElement().isEmpty(); i < bs.pseudos.size(); i++) {
const Pseudo &pseudo = bs.pseudos.at(i);
if (pseudo.type == PseudoClass_Unknown)
return PseudoClass_Unknown;
@@ -1897,23 +1917,23 @@ quint64 Selector::pseudoClass(quint64 *negated) const
void StyleSheet::buildIndexes(Qt::CaseSensitivity nameCaseSensitivity)
{
QList<StyleRule> universals;
- for (int i = 0; i < styleRules.count(); ++i) {
+ for (int i = 0; i < styleRules.size(); ++i) {
const StyleRule &rule = styleRules.at(i);
QList<Selector> universalsSelectors;
- for (int j = 0; j < rule.selectors.count(); ++j) {
+ for (int j = 0; j < rule.selectors.size(); ++j) {
const Selector& selector = rule.selectors.at(j);
if (selector.basicSelectors.isEmpty())
continue;
if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
- if (selector.basicSelectors.count() != 1)
+ if (selector.basicSelectors.size() != 1)
continue;
- } else if (selector.basicSelectors.count() <= 1) {
+ } else if (selector.basicSelectors.size() <= 1) {
continue;
}
- const BasicSelector &sel = selector.basicSelectors.at(selector.basicSelectors.count() - 1);
+ const BasicSelector &sel = selector.basicSelectors.at(selector.basicSelectors.size() - 1);
if (!sel.ids.isEmpty()) {
StyleRule nr;
@@ -1967,14 +1987,14 @@ bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
return false;
if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
- if (selector.basicSelectors.count() != 1)
+ if (selector.basicSelectors.size() != 1)
return false;
return basicSelectorMatches(selector.basicSelectors.at(0), node);
}
- if (selector.basicSelectors.count() <= 1)
+ if (selector.basicSelectors.size() <= 1)
return false;
- int i = selector.basicSelectors.count() - 1;
+ int i = selector.basicSelectors.size() - 1;
node = duplicateNode(node);
bool match = true;
@@ -1982,7 +2002,7 @@ bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
do {
match = basicSelectorMatches(sel, node);
if (!match) {
- if (i == selector.basicSelectors.count() - 1) // first element must always match!
+ if (i == selector.basicSelectors.size() - 1) // first element must always match!
break;
if (sel.relationToNext != BasicSelector::MatchNextSelectorIfAncestor &&
sel.relationToNext != BasicSelector::MatchNextSelectorIfIndirectAdjecent)
@@ -2027,7 +2047,7 @@ bool StyleSelector::basicSelectorMatches(const BasicSelector &sel, NodePtr node)
if (!hasAttributes(node))
return false;
- for (int i = 0; i < sel.attributeSelectors.count(); ++i) {
+ for (int i = 0; i < sel.attributeSelectors.size(); ++i) {
const QCss::AttributeSelector &a = sel.attributeSelectors.at(i);
const QString attrValue = attributeValue(node, a);
@@ -2090,14 +2110,14 @@ bool StyleSelector::basicSelectorMatches(const BasicSelector &sel, NodePtr node)
void StyleSelector::matchRule(NodePtr node, const StyleRule &rule, StyleSheetOrigin origin,
int depth, QMultiMap<uint, StyleRule> *weightedRules)
{
- for (int j = 0; j < rule.selectors.count(); ++j) {
+ for (int j = 0; j < rule.selectors.size(); ++j) {
const Selector& selector = rule.selectors.at(j);
if (selectorMatches(selector, node)) {
uint weight = rule.order
+ selector.specificity() *0x100
+ (uint(origin) + depth)*0x100000;
StyleRule newRule = rule;
- if (rule.selectors.count() > 1) {
+ if (rule.selectors.size() > 1) {
newRule.selectors.resize(1);
newRule.selectors[0] = selector;
}
@@ -2118,15 +2138,15 @@ QList<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
QMultiMap<uint, StyleRule> weightedRules; // (spec, rule) that will be sorted below
//prune using indexed stylesheet
- for (int sheetIdx = 0; sheetIdx < styleSheets.count(); ++sheetIdx) {
+ for (int sheetIdx = 0; sheetIdx < styleSheets.size(); ++sheetIdx) {
const StyleSheet &styleSheet = styleSheets.at(sheetIdx);
- for (int i = 0; i < styleSheet.styleRules.count(); ++i) {
+ for (int i = 0; i < styleSheet.styleRules.size(); ++i) {
matchRule(node, styleSheet.styleRules.at(i), styleSheet.origin, styleSheet.depth, &weightedRules);
}
if (!styleSheet.idIndex.isEmpty()) {
QStringList ids = nodeIds(node);
- for (int i = 0; i < ids.count(); i++) {
+ for (int i = 0; i < ids.size(); i++) {
const QString &key = ids.at(i);
QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.idIndex.constFind(key);
while (it != styleSheet.idIndex.constEnd() && it.key() == key) {
@@ -2137,7 +2157,7 @@ QList<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
}
if (!styleSheet.nameIndex.isEmpty()) {
QStringList names = nodeNames(node);
- for (int i = 0; i < names.count(); i++) {
+ for (int i = 0; i < names.size(); i++) {
QString name = names.at(i);
if (nameCaseSensitivity == Qt::CaseInsensitive)
name = std::move(name).toLower();
@@ -2149,9 +2169,9 @@ QList<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
}
}
if (!medium.isEmpty()) {
- for (int i = 0; i < styleSheet.mediaRules.count(); ++i) {
+ for (int i = 0; i < styleSheet.mediaRules.size(); ++i) {
if (styleSheet.mediaRules.at(i).media.contains(medium, Qt::CaseInsensitive)) {
- for (int j = 0; j < styleSheet.mediaRules.at(i).styleRules.count(); ++j) {
+ for (int j = 0; j < styleSheet.mediaRules.at(i).styleRules.size(); ++j) {
matchRule(node, styleSheet.mediaRules.at(i).styleRules.at(j), styleSheet.origin,
styleSheet.depth, &weightedRules);
}
@@ -2160,7 +2180,7 @@ QList<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
}
}
- rules.reserve(weightedRules.count());
+ rules.reserve(weightedRules.size());
QMultiMap<uint, StyleRule>::const_iterator it = weightedRules.constBegin();
for ( ; it != weightedRules.constEnd() ; ++it)
rules += *it;
@@ -2174,7 +2194,7 @@ QList<Declaration> StyleSelector::declarationsForNode(NodePtr node, const char *
{
QList<Declaration> decls;
QList<StyleRule> rules = styleRulesForNode(node);
- for (int i = 0; i < rules.count(); i++) {
+ for (int i = 0; i < rules.size(); i++) {
const Selector& selector = rules.at(i).selectors.at(0);
const QString pseudoElement = selector.pseudoElement();
@@ -2888,7 +2908,7 @@ bool Parser::next(QCss::TokenType t)
bool Parser::test(QCss::TokenType t)
{
- if (index >= symbols.count())
+ if (index >= symbols.size())
return false;
if (symbols.at(index).token == t) {
++index;
diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h
index cc50fb76b1..c1cfb1ac9b 100644
--- a/src/gui/text/qcssparser_p.h
+++ b/src/gui/text/qcssparser_p.h
@@ -19,7 +19,6 @@
#include <QtCore/QStringList>
#include <QtCore/QList>
#include <QtCore/QVariant>
-#include <QtCore/QPair>
#include <QtCore/QSize>
#include <QtCore/QMultiHash>
#include <QtGui/QFont>
@@ -166,6 +165,11 @@ enum Property {
LetterSpacing,
WordSpacing,
TextDecorationColor,
+ QtPlaceHolderTextColor,
+ QtAccent,
+ QtStrokeWidth,
+ QtStrokeColor,
+ QtForeground,
NumProperties
};
@@ -785,7 +789,7 @@ public:
inline void skipSpace() { while (test(S)) {}; }
- inline bool hasNext() const { return index < symbols.count(); }
+ inline bool hasNext() const { return index < symbols.size(); }
inline TokenType next() { return symbols.at(index++).token; }
bool next(TokenType t);
bool test(TokenType t);
@@ -796,7 +800,7 @@ public:
QString lexemUntil(TokenType t);
bool until(TokenType target, TokenType target2 = NONE);
inline TokenType lookup() const {
- return (index - 1) < symbols.count() ? symbols.at(index - 1).token : NONE;
+ return (index - 1) < symbols.size() ? symbols.at(index - 1).token : NONE;
}
bool testTokenAndEndsWith(TokenType t, QLatin1StringView str);
@@ -823,7 +827,9 @@ struct Q_GUI_EXPORT ValueExtractor
bool extractBox(int *margins, int *paddings, int *spacing = nullptr);
bool extractBorder(int *borders, QBrush *colors, BorderStyle *Styles, QSize *radii);
bool extractOutline(int *borders, QBrush *colors, BorderStyle *Styles, QSize *radii, int *offsets);
- bool extractPalette(QBrush *fg, QBrush *sfg, QBrush *sbg, QBrush *abg);
+ bool extractPalette(QBrush *foreground, QBrush *selectedForeground, QBrush *selectedBackground,
+ QBrush *alternateBackground, QBrush *placeHolderTextForeground,
+ QBrush *accent);
int extractStyleFeatures();
bool extractImage(QIcon *icon, Qt::Alignment *a, QSize *size);
bool extractIcon(QIcon *icon, QSize *size);
diff --git a/src/gui/text/qcssscanner.cpp b/src/gui/text/qcssscanner.cpp
index 6b0d4c10ee..7bfa797782 100644
--- a/src/gui/text/qcssscanner.cpp
+++ b/src/gui/text/qcssscanner.cpp
@@ -8,7 +8,7 @@ public:
QCssScanner_Generated(const QString &inp);
inline QChar next() {
- return (pos < input.length()) ? input.at(pos++) : QChar();
+ return (pos < input.size()) ? input.at(pos++) : QChar();
}
int handleCommentStart();
int lex();
diff --git a/src/gui/text/qdistancefield.cpp b/src/gui/text/qdistancefield.cpp
index 013f40fed6..fe230188c6 100644
--- a/src/gui/text/qdistancefield.cpp
+++ b/src/gui/text/qdistancefield.cpp
@@ -530,14 +530,14 @@ static void makeDistanceField(QDistanceFieldData *data, const QPainterPath &path
|| (to.y() < offs << 8) || (to.y() >= (imgHeight - offs) << 8));
}
- isConvex.resize(normals.count());
- for (int next = 0, prev = normals.count() - 1; next < normals.count(); prev = next++) {
+ isConvex.resize(normals.size());
+ for (int next = 0, prev = normals.size() - 1; next < normals.size(); prev = next++) {
isConvex[prev] = normals.at(prev).x() * normals.at(next).y()
- normals.at(prev).y() * normals.at(next).x() < 0;
}
// Draw quads.
- for (int next = 0, prev = normals.count() - 1; next < normals.count(); prev = next++) {
+ for (int next = 0, prev = normals.size() - 1; next < normals.size(); prev = next++) {
QPoint n = normals.at(next);
QPoint intPrev = vertices.at(prev);
QPoint extPrev = vertices.at(prev);
@@ -1058,7 +1058,7 @@ QImage QDistanceField::toImage(QImage::Format format) const
}
if (image.format() != format)
- image = image.convertToFormat(format);
+ image = std::move(image).convertToFormat(format);
}
return image;
diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp
index 9b749f432c..f3b861957e 100644
--- a/src/gui/text/qfont.cpp
+++ b/src/gui/text/qfont.cpp
@@ -31,6 +31,8 @@
#include <QtCore/QMutexLocker>
#include <QtCore/QMutex>
+#include <array>
+
// #define QFONTCACHE_DEBUG
#ifdef QFONTCACHE_DEBUG
# define FC_DEBUG qDebug
@@ -88,6 +90,9 @@ bool QFontDef::exactMatch(const QFontDef &other) const
return false;
}
+ if (variableAxisValues != other.variableAxisValues)
+ return false;
+
return (styleHint == other.styleHint
&& styleStrategy == other.styleStrategy
&& weight == other.weight
@@ -140,7 +145,7 @@ Q_GUI_EXPORT int qt_defaultDpi()
/* Helper function to convert between legacy Qt and OpenType font weights. */
static int convertWeights(int weight, bool inverted)
{
- static const QVarLengthArray<QPair<int, int>, 9> legacyToOpenTypeMap = {
+ static constexpr std::array<int, 2> legacyToOpenTypeMap[] = {
{ 0, QFont::Thin }, { 12, QFont::ExtraLight }, { 25, QFont::Light },
{ 50, QFont::Normal }, { 57, QFont::Medium }, { 63, QFont::DemiBold },
{ 75, QFont::Bold }, { 81, QFont::ExtraBold }, { 87, QFont::Black },
@@ -151,8 +156,8 @@ static int convertWeights(int weight, bool inverted)
// Go through and find the closest mapped value
for (auto mapping : legacyToOpenTypeMap) {
- const int weightOld = inverted ? mapping.second : mapping.first;
- const int weightNew = inverted ? mapping.first : mapping.second;
+ const int weightOld = mapping[ inverted];
+ const int weightNew = mapping[!inverted];
const int dist = qAbs(weightOld - weight);
if (dist < closestDist) {
result = weightNew;
@@ -179,7 +184,7 @@ static QStringList splitIntoFamilies(const QString &family)
auto str = list.at(i).trimmed();
if ((str.startsWith(u'"') && str.endsWith(u'"'))
|| (str.startsWith(u'\'') && str.endsWith(u'\''))) {
- str = str.mid(1, str.length() - 2);
+ str = str.mid(1, str.size() - 2);
}
familyList << str.toString();
}
@@ -211,7 +216,7 @@ QFontPrivate::QFontPrivate(const QFontPrivate &other)
strikeOut(other.strikeOut), kerning(other.kerning),
capital(other.capital), letterSpacingIsAbsolute(other.letterSpacingIsAbsolute),
letterSpacing(other.letterSpacing), wordSpacing(other.wordSpacing),
- scFont(other.scFont)
+ features(other.features), scFont(other.scFont)
{
if (scFont && scFont != this)
scFont->ref.ref();
@@ -341,9 +346,38 @@ void QFontPrivate::resolve(uint mask, const QFontPrivate *other)
wordSpacing = other->wordSpacing;
if (! (mask & QFont::CapitalizationResolved))
capital = other->capital;
+
+ if (!(mask & QFont::FeaturesResolved))
+ features = other->features;
+
+ if (!(mask & QFont::VariableAxesResolved))
+ request.variableAxisValues = other->request.variableAxisValues;
+}
+
+bool QFontPrivate::hasVariableAxis(QFont::Tag tag, float value) const
+{
+ return request.variableAxisValues.contains(tag) && request.variableAxisValues.value(tag) == value;
}
+void QFontPrivate::setVariableAxis(QFont::Tag tag, float value)
+{
+ request.variableAxisValues.insert(tag, value);
+}
+
+void QFontPrivate::unsetVariableAxis(QFont::Tag tag)
+{
+ request.variableAxisValues.remove(tag);
+}
+
+void QFontPrivate::setFeature(QFont::Tag tag, quint32 value)
+{
+ features.insert(tag, value);
+}
+void QFontPrivate::unsetFeature(QFont::Tag tag)
+{
+ features.remove(tag);
+}
QFontEngineData::QFontEngineData()
@@ -458,8 +492,6 @@ QFontEngineData::~QFontEngineData()
The font matching algorithm works as follows:
\list 1
\li The specified font families (set by setFamilies()) are searched for.
- \li If not found, then if set the specified font family exists and can be used to represent
- the writing system in use, it will be selected.
\li If not, a replacement font that supports the writing system is
selected. The font matching algorithm will try to find the
best match for all the properties set in the QFont. How this is
@@ -529,7 +561,7 @@ QFontEngineData::~QFontEngineData()
Information on encodings can be found from the
\l{UTR17} page.
- \sa QFontMetrics, QFontInfo, QFontDatabase, {Character Map Example}
+ \sa QFontMetrics, QFontInfo, QFontDatabase
*/
/*!
@@ -784,7 +816,7 @@ QFont &QFont::operator=(const QFont &font)
*/
QString QFont::family() const
{
- return d->request.families.isEmpty() ? QString() : d->request.families.first();
+ return d->request.families.isEmpty() ? QString() : d->request.families.constFirst();
}
/*!
@@ -910,12 +942,6 @@ int QFont::pointSize() const
\li No hinting
\endtable
- \note Please be aware that altering the hinting preference on Windows is available through
- the DirectWrite font engine. This is available on Windows Vista after installing the platform
- update, and on Windows 7. In order to use this extension, configure Qt using -directwrite.
- The target application will then depend on the availability of DirectWrite on the target
- system.
-
*/
/*!
@@ -1011,7 +1037,8 @@ qreal QFont::pointSizeF() const
}
/*!
- Sets the font size to \a pixelSize pixels.
+ Sets the font size to \a pixelSize pixels, with a maxiumum size
+ of an unsigned 16-bit integer.
Using this function makes the font device dependent. Use
setPointSize() or setPointSizeF() to set the size of the font
@@ -1435,6 +1462,14 @@ QFont::StyleHint QFont::styleHint() const
\value NoAntialias don't antialias the fonts.
\value NoSubpixelAntialias avoid subpixel antialiasing on the fonts if possible.
\value PreferAntialias antialias if possible.
+ \value [since 6.8] ContextFontMerging If the selected font does not contain a certain character,
+ then Qt automatically chooses a similar-looking fallback font that contains the
+ character. By default this is done on a character-by-character basis. This means that in
+ certain uncommon cases, multiple fonts may be used to represent one string of text even
+ if it's in the same script. Setting \c ContextFontMerging will try finding the fallback
+ font that matches the largest subset of the input string instead. This will be more
+ expensive for strings where missing glyphs occur, but may give more consistent results.
+ If \c NoFontMerging is set, then \c ContextFontMerging will have no effect.
\value NoFontMerging If the font selected for a certain writing system
does not contain a character requested to draw, then Qt automatically chooses a similar
looking font that contains the character. The NoFontMerging flag disables this feature.
@@ -1507,7 +1542,7 @@ void QFont::setStyleStrategy(StyleStrategy s)
Predefined stretch values that follow the CSS naming convention. The higher
the value, the more stretched the text is.
- \value AnyStretch 0 Accept any stretch matched using the other QFont properties (added in Qt 5.8)
+ \value [since 5.8] AnyStretch 0 Accept any stretch matched using the other QFont properties
\value UltraCondensed 50
\value ExtraCondensed 62
\value Condensed 75
@@ -1745,6 +1780,7 @@ bool QFont::operator==(const QFont &f) const
&& f.d->letterSpacingIsAbsolute == d->letterSpacingIsAbsolute
&& f.d->letterSpacing == d->letterSpacing
&& f.d->wordSpacing == d->wordSpacing
+ && f.d->features == d->features
));
}
@@ -1782,7 +1818,37 @@ bool QFont::operator<(const QFont &f) const
int f1attrs = (f.d->underline << 3) + (f.d->overline << 2) + (f.d->strikeOut<<1) + f.d->kerning;
int f2attrs = (d->underline << 3) + (d->overline << 2) + (d->strikeOut<<1) + d->kerning;
- return f1attrs < f2attrs;
+ if (f1attrs != f2attrs) return f1attrs < f2attrs;
+
+ if (d->features.size() != f.d->features.size())
+ return f.d->features.size() < d->features.size();
+
+ {
+ auto it = d->features.constBegin();
+ auto jt = f.d->features.constBegin();
+ for (; it != d->features.constEnd(); ++it, ++jt) {
+ if (it.key() != jt.key())
+ return jt.key() < it.key();
+ if (it.value() != jt.value())
+ return jt.value() < it.value();
+ }
+ }
+
+ if (r1.variableAxisValues.size() != r2.variableAxisValues.size())
+ return r1.variableAxisValues.size() < r2.variableAxisValues.size();
+
+ {
+ auto it = r1.variableAxisValues.constBegin();
+ auto jt = r2.variableAxisValues.constBegin();
+ for (; it != r1.variableAxisValues.constEnd(); ++it, ++jt) {
+ if (it.key() != jt.key())
+ return jt.key() < it.key();
+ if (it.value() != jt.value())
+ return jt.value() < it.value();
+ }
+ }
+
+ return false;
}
@@ -2125,7 +2191,7 @@ bool QFont::fromString(const QString &descrip)
{
const auto sr = QStringView(descrip).trimmed();
const auto l = sr.split(u',');
- const int count = l.count();
+ const int count = l.size();
if (!count || (count > 2 && count < 9) || count == 9 || count > 17 ||
l.first().isEmpty()) {
qWarning("QFont::fromString: Invalid description '%s'",
@@ -2203,6 +2269,404 @@ void QFont::cacheStatistics()
{
}
+/*!
+ \class QFont::Tag
+ \brief The QFont::Tag type provides access to advanced font features.
+ \since 6.7
+ \inmodule QtGui
+
+ QFont provides access to advanced features when shaping text. A feature is defined
+ by a tag, which can be represented as a four-character string, or as a 32bit integer
+ value. This type represents such a tag in a type-safe way. It can be constructed from
+ a four-character, 8bit string literal, or from a corresponding 32bit integer value.
+ Using a shorter or longer string literal will result in a compile-time error.
+
+ \code
+ QFont font;
+ // Correct
+ font.setFeature("frac");
+
+ // Wrong - won't compile
+ font.setFeature("fraction");
+
+ // Wrong - will produce runtime warning and fail
+ font.setFeature(u"fraction"_s);
+ \endcode
+
+ The named constructors allow to create a tag from an 32bit integer or string value,
+ and will return a \c std::nullopt when the input is invalid.
+
+ \sa QFont::setFeature(), QFont::featureTags()
+*/
+
+/*!
+ \fn QFont::Tag::Tag()
+
+ Default constructor, producing an invalid tag.
+*/
+
+/*!
+ \fn template <size_t N> QFont::Tag::Tag(const char (&str)[N]) noexcept
+
+ Constructs a tag from a string literal, \a str. The literal must be exactly four
+ characters long.
+
+ \code
+ font.setFeature("frac", 1);
+ \endcode
+
+ \sa fromString(), fromValue()
+*/
+
+/*!
+ \fn bool QFont::Tag::comparesEqual(const QFont::Tag &lhs, const QFont::Tag &rhs) noexcept
+ \fn Qt::strong_ordering QFont::Tag::compareThreeWay(const QFont::Tag &lhs, const QFont::Tag &rhs) noexcept
+
+ Compare \a lhs with \a rhs for equality and ordering.
+*/
+
+/*!
+ \fn size_t QFont::Tag::qHash(QFont::Tag key, size_t seed) noexcept
+
+ Returns the hash value for \a key, using \a seed to seed the calculation.
+*/
+
+/*!
+ \fn quint32 QFont::Tag::value() const noexcept
+
+ Returns the numerical value of this tag.
+
+ \sa isValid(), fromValue()
+*/
+
+/*!
+ \fn bool QFont::Tag::isValid() const noexcept
+
+ Returns whether the tag is valid. A tag is valid if its value is not zero.
+
+ \sa value(), fromValue(), fromString()
+*/
+
+/*!
+ \fn QByteArray QFont::Tag::toString() const noexcept
+
+ Returns the string representation of this tag as a byte array.
+
+ \sa fromString()
+*/
+
+/*!
+ \fn std::optional<QFont::Tag> QFont::Tag::fromValue(quint32 value) noexcept
+
+ Returns a tag constructed from \a value, or \c std::nullopt if the tag produced
+ would be invalid.
+
+ \sa isValid()
+*/
+
+/*!
+ Returns a tag constructed from the string in \a view. The string must be exactly
+ four characters long.
+
+ Returns \c std::nullopt if the input is not four characters long, or if the tag
+ produced would be invalid.
+
+ \sa isValid(), fromValue()
+*/
+std::optional<QFont::Tag> QFont::Tag::fromString(QAnyStringView view) noexcept
+{
+ if (view.size() != 4) {
+ qWarning("The tag name must be exactly 4 characters long!");
+ return std::nullopt;
+ }
+ const QFont::Tag maybeTag = view.visit([](auto view) {
+ using CharType = decltype(view.at(0));
+ if constexpr (std::is_same_v<CharType, char>) {
+ const char bytes[5] = { view.at(0), view.at(1), view.at(2), view.at(3), 0 };
+ return Tag(bytes);
+ } else {
+ const char bytes[5] = { view.at(0).toLatin1(), view.at(1).toLatin1(),
+ view.at(2).toLatin1(), view.at(3).toLatin1(), 0 };
+ return Tag(bytes);
+ }
+ });
+ return maybeTag.isValid() ? std::optional<Tag>(maybeTag) : std::nullopt;
+}
+
+/*!
+ \fn QDataStream &operator<<(QDataStream &, QFont::Tag)
+ \fn QDataStream &operator>>(QDataStream &, QFont::Tag &)
+ \relates QFont::Tag
+
+ Data stream operators for QFont::Tag.
+*/
+
+/*!
+ \since 6.7
+
+ Applies a \a value to the variable axis corresponding to \a tag.
+
+ Variable fonts provide a way to store multiple variations (with different weights, widths
+ or styles) in the same font file. The variations are given as floating point values for
+ a pre-defined set of parameters, called "variable axes". Specific instances are typically
+ given names by the font designer, and, in Qt, these can be selected using setStyleName()
+ just like traditional sub-families.
+
+ In some cases, it is also useful to provide arbitrary values for the different axes. For
+ instance, if a font has a Regular and Bold sub-family, you may want a weight in-between these.
+ You could then manually request this by supplying a custom value for the "wght" axis in the
+ font.
+
+ \code
+ QFont font;
+ font.setVariableAxis("wght", (QFont::Normal + QFont::Bold) / 2.0f);
+ \endcode
+
+ If the "wght" axis is supported by the font and the given value is within its defined range,
+ a font corresponding to the weight 550.0 will be provided.
+
+ There are a few standard axes than many fonts provide, such as "wght" (weight), "wdth" (width),
+ "ital" (italic) and "opsz" (optical size). They each have indivdual ranges defined in the font
+ itself. For instance, "wght" may span from 100 to 900 (QFont::Thin to QFont::Black) whereas
+ "ital" can span from 0 to 1 (from not italic to fully italic).
+
+ A font may also choose to define custom axes; the only limitation is that the name has to
+ meet the requirements for a QFont::Tag (sequence of four latin-1 characters.)
+
+ By default, no variable axes are set.
+
+ \note In order to use variable axes on Windows, the application has to run with either the
+ FreeType or DirectWrite font databases. See the documentation for
+ QGuiApplication::QGuiApplication() for more information on how to select these technologies.
+
+ \sa unsetVariableAxis
+ */
+void QFont::setVariableAxis(Tag tag, float value)
+{
+ if (tag.isValid()) {
+ if (resolve_mask & QFont::VariableAxesResolved && d->hasVariableAxis(tag, value))
+ return;
+
+ detach();
+
+ d->setVariableAxis(tag, value);
+ resolve_mask |= QFont::VariableAxesResolved;
+ }
+}
+
+/*!
+ \since 6.7
+
+ Unsets a previously set variable axis value given by \a tag.
+
+ \note If no value has previously been given for this tag, the QFont will still consider its
+ variable axes as set when resolving against other QFont values.
+
+ \sa setVariableAxis
+*/
+void QFont::unsetVariableAxis(Tag tag)
+{
+ if (tag.isValid()) {
+ detach();
+
+ d->unsetVariableAxis(tag);
+ resolve_mask |= QFont::VariableAxesResolved;
+ }
+}
+
+/*!
+ \since 6.7
+
+ Returns a list of tags for all variable axes currently set on this QFont.
+
+ See \l{QFont::}{setVariableAxis()} for more details on variable axes.
+
+ \sa QFont::Tag, setVariableAxis(), unsetVariableAxis(), isVariableAxisSet(), clearVariableAxes()
+*/
+QList<QFont::Tag> QFont::variableAxisTags() const
+{
+ return d->request.variableAxisValues.keys();
+}
+
+/*!
+ \since 6.7
+
+ Returns the value set for a specific variable axis \a tag. If the tag has not been set, 0.0 will
+ be returned instead.
+
+ See \l{QFont::}{setVariableAxis()} for more details on variable axes.
+
+ \sa QFont::Tag, setVariableAxis(), unsetVariableAxis(), isVariableAxisSet(), clearVariableAxes()
+*/
+float QFont::variableAxisValue(Tag tag) const
+{
+ return d->request.variableAxisValues.value(tag);
+}
+
+/*!
+ \since 6.7
+
+ Returns true if a value for the variable axis given by \a tag has been set on the QFont,
+ otherwise returns false.
+
+ See \l{QFont::}{setVariableAxis()} for more details on font variable axes.
+
+ \sa QFont::Tag, setVariableAxis(), unsetVariableAxis(), variableAxisValue(), clearVariableAxes()
+*/
+bool QFont::isVariableAxisSet(Tag tag) const
+{
+ return d->request.variableAxisValues.contains(tag);
+}
+
+/*!
+ \since 6.7
+
+ Clears any previously set variable axis values on the QFont.
+
+ See \l{QFont::}{setVariableAxis()} for more details on variable axes.
+
+ \sa QFont::Tag, setVariableAxis(), unsetVariableAxis(), isVariableAxisSet(), variableAxisValue()
+*/
+void QFont::clearVariableAxes()
+{
+ if (d->request.variableAxisValues.isEmpty())
+ return;
+
+ detach();
+ d->request.variableAxisValues.clear();
+}
+
+
+/*!
+ \since 6.7
+ \overload
+
+ Applies an integer value to the typographical feature specified by \a tag when shaping the
+ text. This provides advanced access to the font shaping process, and can be used to support
+ font features that are otherwise not covered in the API.
+
+ The feature is specified by a \l{QFont::Tag}{tag}, which is typically encoded from the
+ four-character feature name in the font feature map.
+
+ This integer \a value passed along with the tag in most cases represents a boolean value: A zero
+ value means the feature is disabled, and a non-zero value means it is enabled. For certain
+ font features, however, it may have other interpretations. For example, when applied to the
+ \c salt feature, the value is an index that specifies the stylistic alternative to use.
+
+ For example, the \c frac font feature will convert diagonal fractions separated with a slash
+ (such as \c 1/2) with a different representation. Typically this will involve baking the full
+ fraction into a single character width (such as \c ½).
+
+ If a font supports the \c frac feature, then it can be enabled in the shaper by setting
+ \c{features["frac"] = 1} in the font feature map.
+
+ \note By default, Qt will enable and disable certain font features based on other font
+ properties. In particular, the \c kern feature will be enabled/disabled depending on the
+ \l kerning() property of the QFont. In addition, all ligature features
+ (\c liga, \c clig, \c dlig, \c hlig) will be disabled if a \l letterSpacing() is applied,
+ but only for writing systems where the use of ligature is cosmetic. For writing systems where
+ ligatures are required, the features will remain in their default state. The values set using
+ setFeature() and related functions will override the default behavior. If, for instance,
+ the feature "kern" is set to 1, then kerning will always be enabled, regardless of whether the
+ kerning property is set to false. Similarly, if it is set to 0, then it will always be disabled.
+ To reset a font feature to its default behavior, you can unset it using unsetFeature().
+
+ \sa QFont::Tag, clearFeatures(), setFeature(), unsetFeature(), featureTags()
+*/
+void QFont::setFeature(Tag tag, quint32 value)
+{
+ if (tag.isValid()) {
+ d->detachButKeepEngineData(this);
+ d->setFeature(tag, value);
+ resolve_mask |= QFont::FeaturesResolved;
+ }
+}
+
+/*!
+ \since 6.7
+ \overload
+
+ Unsets the \a tag from the map of explicitly enabled/disabled features.
+
+ \note Even if the feature has not previously been added, this will mark the font features map
+ as modified in this QFont, so that it will take precedence when resolving against other fonts.
+
+ Unsetting an existing feature on the QFont reverts behavior to the default.
+
+ See \l setFeature() for more details on font features.
+
+ \sa QFont::Tag, clearFeatures(), setFeature(), featureTags(), featureValue()
+*/
+void QFont::unsetFeature(Tag tag)
+{
+ if (tag.isValid()) {
+ d->detachButKeepEngineData(this);
+ d->unsetFeature(tag);
+ resolve_mask |= QFont::FeaturesResolved;
+ }
+}
+
+/*!
+ \since 6.7
+
+ Returns a list of tags for all font features currently set on this QFont.
+
+ See \l{QFont::}{setFeature()} for more details on font features.
+
+ \sa QFont::Tag, setFeature(), unsetFeature(), isFeatureSet(), clearFeatures()
+*/
+QList<QFont::Tag> QFont::featureTags() const
+{
+ return d->features.keys();
+}
+
+/*!
+ \since 6.7
+
+ Returns the value set for a specific feature \a tag. If the tag has not been set, 0 will be
+ returned instead.
+
+ See \l{QFont::}{setFeature()} for more details on font features.
+
+ \sa QFont::Tag, setFeature(), unsetFeature(), featureTags(), isFeatureSet()
+*/
+quint32 QFont::featureValue(Tag tag) const
+{
+ return d->features.value(tag);
+}
+
+/*!
+ \since 6.7
+
+ Returns true if a value for the feature given by \a tag has been set on the QFont, otherwise
+ returns false.
+
+ See \l{QFont::}{setFeature()} for more details on font features.
+
+ \sa QFont::Tag, setFeature(), unsetFeature(), featureTags(), featureValue()
+*/
+bool QFont::isFeatureSet(Tag tag) const
+{
+ return d->features.contains(tag);
+}
+
+/*!
+ \since 6.7
+
+ Clears any previously set features on the QFont.
+
+ See \l{QFont::}{setFeature()} for more details on font features.
+
+ \sa QFont::Tag, setFeature(), unsetFeature(), featureTags(), featureValue()
+*/
+void QFont::clearFeatures()
+{
+ if (d->features.isEmpty())
+ return;
+
+ d->detachButKeepEngineData(this);
+ d->features.clear();
+}
extern QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style,
QFont::StyleHint styleHint, QChar::Script script);
@@ -2282,9 +2746,9 @@ void QFont::setFamilies(const QStringList &families)
QDataStream &operator<<(QDataStream &s, const QFont &font)
{
if (s.version() == 1) {
- s << font.d->request.families.first().toLatin1();
+ s << font.d->request.families.constFirst().toLatin1();
} else {
- s << font.d->request.families.first();
+ s << font.d->request.families.constFirst();
if (s.version() >= QDataStream::Qt_5_4)
s << font.d->request.styleName;
}
@@ -2340,6 +2804,10 @@ QDataStream &operator<<(QDataStream &s, const QFont &font)
else
s << font.d->request.families;
}
+ if (s.version() >= QDataStream::Qt_6_6)
+ s << font.d->features;
+ if (s.version() >= QDataStream::Qt_6_7)
+ s << font.d->request.variableAxisValues;
return s;
}
@@ -2454,9 +2922,35 @@ QDataStream &operator>>(QDataStream &s, QFont &font)
else
font.d->request.families = value;
}
+ if (s.version() >= QDataStream::Qt_6_6) {
+ font.d->features.clear();
+ s >> font.d->features;
+ }
+ if (s.version() >= QDataStream::Qt_6_7) {
+ font.d->request.variableAxisValues.clear();
+ s >> font.d->request.variableAxisValues;
+ }
+
return s;
}
+QDataStream &operator<<(QDataStream &stream, QFont::Tag tag)
+{
+ stream << tag.value();
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, QFont::Tag &tag)
+{
+ quint32 value;
+ stream >> value;
+ if (const auto maybeTag = QFont::Tag::fromValue(value))
+ tag = *maybeTag;
+ else
+ stream.setStatus(QDataStream::ReadCorruptData);
+ return stream;
+}
+
#endif // QT_NO_DATASTREAM
@@ -2507,6 +3001,35 @@ QDataStream &operator>>(QDataStream &s, QFont &font)
info object is \e not updated.
\endlist
+ \section1 Checking for the existence of a font
+
+ Sometimes it can be useful to check if a font exists before attempting
+ to use it. The most thorough way of doing so is by using \l {exactMatch()}:
+
+ \code
+ const QFont segoeFont(QLatin1String("Segoe UI"));
+ if (QFontInfo(segoeFont).exactMatch()) {
+ // Use the font...
+ }
+ \endcode
+
+ However, this deep search of families can be expensive on some platforms.
+ \c QFontDatabase::families().contains() is a faster, but less thorough
+ alternative:
+
+ \code
+ const QLatin1String segoeUiFamilyName("Segoe UI");
+ if (QFontDatabase::families().contains(segoeUiFamilyName)) {
+ const QFont segoeFont(segoeUiFamilyName);
+ // Use the font...
+ }
+ \endcode
+
+ It's less thorough because it's not a complete search: some font family
+ aliases may be missing from the list. However, this approach results in
+ faster application startup times, and so should always be preferred if
+ possible.
+
\sa QFont, QFontMetrics, QFontDatabase
*/
@@ -2523,6 +3046,8 @@ QDataStream &operator>>(QDataStream &s, QFont &font)
Use QPainter::fontInfo() to get the font info when painting.
This will give correct results also when painting on paint device
that is not screen-compatible.
+
+ \sa {Checking for the existence of a font}
*/
QFontInfo::QFontInfo(const QFont &font)
: d(font.d)
@@ -2564,13 +3089,13 @@ QFontInfo &QFontInfo::operator=(const QFontInfo &fi)
/*!
Returns the family name of the matched window system font.
- \sa QFont::family()
+ \sa QFont::family(), {Checking for the existence of a font}
*/
QString QFontInfo::family() const
{
QFontEngine *engine = d->engineForScript(QChar::Script_Common);
Q_ASSERT(engine != nullptr);
- return engine->fontDef.families.isEmpty() ? QString() : engine->fontDef.families.first();
+ return engine->fontDef.families.isEmpty() ? QString() : engine->fontDef.families.constFirst();
}
/*!
@@ -2749,7 +3274,7 @@ bool QFontInfo::fixedPitch() const
QChar ch[2] = { u'i', u'm' };
QGlyphLayoutArray<2> g;
int l = 2;
- if (!engine->stringToCMap(ch, 2, &g, &l, {}))
+ if (engine->stringToCMap(ch, 2, &g, &l, {}) < 0)
Q_UNREACHABLE();
Q_ASSERT(l == 2);
engine->fontDef.fixedPitch = g.advances[0] == g.advances[1];
@@ -2793,13 +3318,15 @@ bool QFontInfo::exactMatch() const
// QFontCache
// **********************************************************************
+using namespace std::chrono_literals;
+
#ifdef QFONTCACHE_DEBUG
// fast timeouts for debugging
-static const int fast_timeout = 1000; // 1s
-static const int slow_timeout = 5000; // 5s
+static constexpr auto fast_timeout = 1s;
+static constexpr auto slow_timeout = 5s;
#else
-static const int fast_timeout = 10000; // 10s
-static const int slow_timeout = 300000; // 5m
+static constexpr auto fast_timeout = 10s;
+static constexpr auto slow_timeout = 5min;
#endif // QFONTCACHE_DEBUG
#ifndef QFONTCACHE_MIN_COST
@@ -2828,7 +3355,7 @@ void QFontCache::cleanup()
cache->setLocalData(nullptr);
}
-static QBasicAtomicInt font_cache_id = Q_BASIC_ATOMIC_INITIALIZER(0);
+Q_CONSTINIT static QBasicAtomicInt font_cache_id = Q_BASIC_ATOMIC_INITIALIZER(0);
QFontCache::QFontCache()
: QObject(), total_cost(0), max_cost(min_cost),
@@ -3009,7 +3536,7 @@ void QFontCache::increaseCost(uint cost)
return;
if (timer_id == -1 || ! fast) {
- FC_DEBUG(" TIMER: starting fast timer (%d ms)", fast_timeout);
+ FC_DEBUG(" TIMER: starting fast timer (%d s)", static_cast<int>(fast_timeout.count()));
if (timer_id != -1)
killTimer(timer_id);
@@ -3300,6 +3827,13 @@ QDebug operator<<(QDebug stream, const QFont &font)
return stream;
}
+
+QDebug operator<<(QDebug debug, QFont::Tag tag)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote() << tag.toString();
+ return debug;
+}
#endif
QT_END_NAMESPACE
diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h
index 7fb591b51d..66a5f7c155 100644
--- a/src/gui/text/qfont.h
+++ b/src/gui/text/qfont.h
@@ -4,10 +4,12 @@
#ifndef QFONT_H
#define QFONT_H
+#include <QtCore/qcompare.h>
+#include <QtCore/qendian.h>
+#include <QtCore/qshareddata.h>
#include <QtGui/qtguiglobal.h>
#include <QtGui/qwindowdefs.h>
#include <QtCore/qstring.h>
-#include <QtCore/qsharedpointer.h>
QT_BEGIN_NAMESPACE
@@ -45,6 +47,7 @@ public:
NoAntialias = 0x0100,
NoSubpixelAntialias = 0x0800,
PreferNoShaping = 0x1000,
+ ContextFontMerging = 0x2000,
NoFontMerging = 0x8000
};
Q_ENUM(StyleStrategy)
@@ -126,7 +129,9 @@ public:
HintingPreferenceResolved = 0x8000,
StyleNameResolved = 0x10000,
FamiliesResolved = 0x20000,
- AllPropertiesResolved = 0x3ffff
+ FeaturesResolved = 0x40000,
+ VariableAxesResolved = 0x80000,
+ AllPropertiesResolved = 0xfffff
};
Q_ENUM(ResolveProperties)
@@ -206,6 +211,75 @@ public:
void setHintingPreference(HintingPreference hintingPreference);
HintingPreference hintingPreference() const;
+ struct Tag
+ {
+ constexpr Tag() = default;
+
+ template <size_t N>
+ constexpr Q_IMPLICIT Tag(const char (&str)[N]) noexcept
+ : m_value((quint32(str[0]) << 24) | (quint32(str[1]) << 16)
+ | (quint32(str[2]) << 8) | quint32(str[3]))
+ {
+ static_assert(N == 5, "The tag name must be exactly 4 characters long!");
+ }
+
+ constexpr bool isValid() const noexcept { return m_value != 0; }
+ constexpr quint32 value() const noexcept { return m_value; }
+
+ QByteArray toString() const
+ {
+ const char data[] = {
+ char((m_value & 0xff000000) >> 24),
+ char((m_value & 0x00ff0000) >> 16),
+ char((m_value & 0x0000ff00) >> 8),
+ char((m_value & 0x000000ff)) };
+ return QByteArray(data, sizeof(data));
+ }
+
+ static constexpr std::optional<Tag> fromValue(quint32 value) noexcept
+ {
+ Tag maybeTag;
+ maybeTag.m_value = value;
+ return maybeTag.isValid() ? std::optional<Tag>(maybeTag) : std::nullopt;
+ }
+ Q_GUI_EXPORT static std::optional<Tag> fromString(QAnyStringView view) noexcept;
+
+#ifndef QT_NO_DATASTREAM
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, Tag);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, Tag &);
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_GUI_EXPORT QDebug operator<<(QDebug debug, Tag tag);
+#endif
+
+ friend constexpr size_t qHash(Tag key, size_t seed = 0) noexcept
+ { return qHash(key.value(), seed); }
+
+ private:
+ friend constexpr bool comparesEqual(const Tag &lhs, const Tag &rhs) noexcept
+ { return lhs.m_value == rhs.m_value; }
+ friend constexpr Qt::strong_ordering compareThreeWay(const Tag &lhs, const Tag &rhs) noexcept
+ { return Qt::compareThreeWay(lhs.m_value, rhs.m_value); }
+ Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(QFont::Tag)
+
+ quint32 m_value = 0;
+ };
+
+ void setFeature(Tag tag, quint32 value);
+ void unsetFeature(Tag tag);
+ quint32 featureValue(Tag tag) const;
+ bool isFeatureSet(Tag tag) const;
+ QList<Tag> featureTags() const;
+ void clearFeatures();
+
+ void setVariableAxis(Tag tag, float value);
+ void unsetVariableAxis(Tag tag);
+ bool isVariableAxisSet(Tag tag) const;
+ float variableAxisValue(Tag tag) const;
+ void clearVariableAxes();
+ QList<Tag> variableAxisTags() const;
+
// dupicated from QFontInfo
bool exactMatch() const;
diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h
index 13738bb096..b674e71103 100644
--- a/src/gui/text/qfont_p.h
+++ b/src/gui/text/qfont_p.h
@@ -55,6 +55,7 @@ struct QFontDef
QString styleName;
QStringList fallBackFamilies;
+ QMap<QFont::Tag, float> variableAxisValues;
qreal pointSize;
qreal pixelSize;
@@ -85,6 +86,7 @@ struct QFontDef
&& families == other.families
&& styleName == other.styleName
&& hintingPreference == other.hintingPreference
+ && variableAxisValues == other.variableAxisValues
;
}
inline bool operator<(const QFontDef &other) const
@@ -103,6 +105,22 @@ struct QFontDef
if (ignorePitch != other.ignorePitch) return ignorePitch < other.ignorePitch;
if (fixedPitch != other.fixedPitch) return fixedPitch < other.fixedPitch;
+ if (variableAxisValues != other.variableAxisValues) {
+ if (variableAxisValues.size() != other.variableAxisValues.size())
+ return variableAxisValues.size() < other.variableAxisValues.size();
+
+ {
+ auto it = variableAxisValues.constBegin();
+ auto jt = other.variableAxisValues.constBegin();
+ for (; it != variableAxisValues.constEnd(); ++it, ++jt) {
+ if (it.key() != jt.key())
+ return jt.key() < it.key();
+ if (it.value() != jt.value())
+ return jt.value() < it.value();
+ }
+ }
+ }
+
return false;
}
};
@@ -120,7 +138,9 @@ inline size_t qHash(const QFontDef &fd, size_t seed = 0) noexcept
fd.fixedPitch,
fd.families,
fd.styleName,
- fd.hintingPreference);
+ fd.hintingPreference,
+ fd.variableAxisValues.keys(),
+ fd.variableAxisValues.values());
}
class QFontEngineData
@@ -164,6 +184,7 @@ public:
QFixed letterSpacing;
QFixed wordSpacing;
+ QHash<QFont::Tag, quint32> features;
mutable QFontPrivate *scFont;
QFont smallCapsFont() const { return QFont(smallCapsFontPrivate()); }
@@ -178,6 +199,13 @@ public:
static void detachButKeepEngineData(QFont *font);
+ void setFeature(QFont::Tag tag, quint32 value);
+ void unsetFeature(QFont::Tag tag);
+
+ void setVariableAxis(QFont::Tag tag, float value);
+ void unsetVariableAxis(QFont::Tag tag);
+ bool hasVariableAxis(QFont::Tag tag, float value) const;
+
private:
QFontPrivate &operator=(const QFontPrivate &) { return *this; }
};
diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp
index a4aa8bf356..d3a13d801b 100644
--- a/src/gui/text/qfontdatabase.cpp
+++ b/src/gui/text/qfontdatabase.cpp
@@ -48,6 +48,11 @@ Q_AUTOTEST_EXPORT void qt_setQtEnableTestFont(bool value)
}
#endif
+Q_TRACE_POINT(qtgui, QFontDatabase_loadEngine, const QString &families, int pointSize);
+Q_TRACE_POINT(qtgui, QFontDatabasePrivate_addAppFont, const QString &fileName);
+Q_TRACE_POINT(qtgui, QFontDatabase_addApplicationFont, const QString &fileName);
+Q_TRACE_POINT(qtgui, QFontDatabase_load, const QString &family, int pointSize);
+
static int getFontWeight(const QString &weightString)
{
QString s = weightString.toLower();
@@ -254,13 +259,13 @@ bool QtFontFamily::matchesFamilyName(const QString &familyName) const
return equalsCaseInsensitive(name, familyName) || aliases.contains(familyName, Qt::CaseInsensitive);
}
-void QtFontFamily::ensurePopulated()
+bool QtFontFamily::ensurePopulated()
{
if (populated)
- return;
+ return true;
QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFamily(name);
- Q_ASSERT_X(populated, Q_FUNC_INFO, qPrintable(name));
+ return populated;
}
void QFontDatabasePrivate::clearFamilies()
@@ -280,6 +285,8 @@ void QFontDatabasePrivate::clearFamilies()
void QFontDatabasePrivate::invalidate()
{
+ qCDebug(lcFontDb) << "Invalidating font database";
+
QFontCache::instance()->clear();
fallbacksCache.clear();
@@ -329,8 +336,10 @@ QtFontFamily *QFontDatabasePrivate::family(const QString &f, FamilyRequestFlags
fam = families[pos];
}
- if (fam && (flags & EnsurePopulated))
- fam->ensurePopulated();
+ if (fam && (flags & EnsurePopulated)) {
+ if (!fam->ensurePopulated())
+ return nullptr;
+ }
return fam;
}
@@ -435,7 +444,7 @@ static void parseFontName(const QString &name, QString &foundry, QString &family
// capitalize the family/foundry names
bool space = true;
QChar *s = family.data();
- int len = family.length();
+ int len = family.size();
while(len--) {
if (space) *s = s->toUpper();
space = s->isSpace();
@@ -444,7 +453,7 @@ static void parseFontName(const QString &name, QString &foundry, QString &family
space = true;
s = foundry.data();
- len = foundry.length();
+ len = foundry.size();
while(len--) {
if (space) *s = s->toUpper();
space = s->isSpace();
@@ -562,6 +571,8 @@ void qt_registerFont(const QString &familyName, const QString &stylename,
void qt_registerFontFamily(const QString &familyName)
{
+ qCDebug(lcFontDb) << "Registering family" << familyName;
+
// Create uninitialized/unpopulated family
QFontDatabasePrivate::instance()->family(familyName, QFontDatabasePrivate::EnsureCreated);
}
@@ -571,6 +582,8 @@ void qt_registerAliasToFontFamily(const QString &familyName, const QString &alia
if (alias.isEmpty())
return;
+ qCDebug(lcFontDb) << "Registering alias" << alias << "to family" << familyName;
+
auto *d = QFontDatabasePrivate::instance();
QtFontFamily *f = d->family(familyName, QFontDatabasePrivate::RequestFamily);
if (!f)
@@ -657,7 +670,8 @@ static QStringList fallbacksForFamily(const QString &family, QFont::Style style,
return *fallbacks;
// make sure that the db has all fallback families
- QStringList retList = QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(family,style,styleHint,script);
+ QStringList userFallbacks = db->applicationFallbackFontFamilies.value(script == QChar::Script_Common ? QChar::Script_Latin : script);
+ QStringList retList = userFallbacks + QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(family,style,styleHint,script);
QStringList::iterator i;
for (i = retList.begin(); i != retList.end(); ++i) {
@@ -720,7 +734,7 @@ QFontEngine *QFontDatabasePrivate::loadSingleEngine(int script,
// Also check for OpenType tables when using complex scripts
if (Q_UNLIKELY(!engine->supportsScript(QChar::Script(script)))) {
qWarning(" OpenType support missing for \"%s\", script %d",
- qPrintable(def.families.first()), script);
+ qPrintable(def.families.constFirst()), script);
return nullptr;
}
@@ -745,7 +759,7 @@ QFontEngine *QFontDatabasePrivate::loadSingleEngine(int script,
// Also check for OpenType tables when using complex scripts
if (!engine->supportsScript(QChar::Script(script))) {
qWarning(" OpenType support missing for \"%s\", script %d",
- +qPrintable(def.families.first()), script);
+ +qPrintable(def.families.constFirst()), script);
if (engine->ref.loadRelaxed() == 0)
delete engine;
return nullptr;
@@ -772,7 +786,7 @@ QFontEngine *QFontDatabasePrivate::loadEngine(int script, const QFontDef &reques
QFontEngine *engine = loadSingleEngine(script, request, family, foundry, style, size);
if (engine && !(request.styleStrategy & QFont::NoFontMerging) && !engine->symbol) {
- Q_TRACE(QFontDatabase_loadEngine, request.families, request.pointSize);
+ Q_TRACE(QFontDatabase_loadEngine, request.families.join(QLatin1Char(';')), request.pointSize);
QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
QFontEngineMulti *pfMultiEngine = pfdb->fontEngineMulti(engine, QChar::Script(script));
@@ -1047,7 +1061,8 @@ int QFontDatabasePrivate::match(int script, const QFontDef &request, const QStri
if (!matchFamilyName(family_name, test.family))
continue;
- test.family->ensurePopulated();
+ if (!test.family->ensurePopulated())
+ continue;
// Check if family is supported in the script we want
if (writingSystem != QFontDatabase::Any && !familySupportsWritingSystem(test.family, writingSystem))
@@ -1187,7 +1202,7 @@ QString QFontDatabase::styleString(const QFontInfo &fontInfo)
each combination of family and style, displaying this information
in a tree view.
- \sa QFont, QFontInfo, QFontMetrics, {Character Map Example}
+ \sa QFont, QFontInfo, QFontMetrics
*/
/*!
@@ -1313,6 +1328,7 @@ QFontDatabasePrivate *QFontDatabasePrivate::ensureFontDatabase()
// The font database may have been partially populated, but to ensure
// we can answer queries for any platform- or user-provided family we
// need to fully populate it now.
+ qCDebug(lcFontDb) << "Populating font database";
if (Q_UNLIKELY(qGuiApp == nullptr || QGuiApplicationPrivate::platformIntegration() == nullptr))
qFatal("QFontDatabase: Must construct a QGuiApplication before accessing QFontDatabase");
@@ -1320,7 +1336,7 @@ QFontDatabasePrivate *QFontDatabasePrivate::ensureFontDatabase()
auto *platformFontDatabase = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
platformFontDatabase->populateFontDatabase();
- for (int i = 0; i < d->applicationFonts.count(); i++) {
+ for (int i = 0; i < d->applicationFonts.size(); i++) {
auto *font = &d->applicationFonts[i];
if (!font->isNull() && !font->isPopulated())
platformFontDatabase->addApplicationFont(font->data, font->fileName, font);
@@ -1351,7 +1367,8 @@ QList<QFontDatabase::WritingSystem> QFontDatabase::writingSystems()
for (int i = 0; i < d->count; ++i) {
QtFontFamily *family = d->families[i];
- family->ensurePopulated();
+ if (!family->ensurePopulated())
+ continue;
if (family->count == 0)
continue;
@@ -1423,7 +1440,8 @@ QStringList QFontDatabase::families(WritingSystem writingSystem)
if (f->populated && f->count == 0)
continue;
if (writingSystem != Any) {
- f->ensurePopulated();
+ if (!f->ensurePopulated())
+ continue;
if (f->writingSystems[writingSystem] != QtFontFamily::Supported)
continue;
}
@@ -1571,8 +1589,8 @@ bool QFontDatabase::isSmoothlyScalable(const QString &family, const QString &sty
for (int i = 0; i < d->count; i++) {
if (d->families[i]->matchesFamilyName(familyName)) {
f = d->families[i];
- f->ensurePopulated();
- break;
+ if (f->ensurePopulated())
+ break;
}
}
}
@@ -1892,7 +1910,19 @@ bool QFontDatabase::hasFamily(const QString &family)
QString parsedFamily, foundry;
parseFontName(family, foundry, parsedFamily);
const QString familyAlias = QFontDatabasePrivate::resolveFontFamilyAlias(parsedFamily);
- return families().contains(familyAlias, Qt::CaseInsensitive);
+
+ QMutexLocker locker(fontDatabaseMutex());
+ QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
+
+ for (int i = 0; i < d->count; i++) {
+ QtFontFamily *f = d->families[i];
+ if (f->populated && f->count == 0)
+ continue;
+ if (familyAlias.compare(f->name, Qt::CaseInsensitive) == 0)
+ return true;
+ }
+
+ return false;
}
@@ -2135,12 +2165,12 @@ int QFontDatabasePrivate::addAppFont(const QByteArray &fontData, const QString &
Q_TRACE(QFontDatabasePrivate_addAppFont, fileName);
int i;
- for (i = 0; i < applicationFonts.count(); ++i)
+ for (i = 0; i < applicationFonts.size(); ++i)
if (applicationFonts.at(i).isNull())
break;
- if (i >= applicationFonts.count()) {
+ if (i >= applicationFonts.size()) {
applicationFonts.append(ApplicationFont());
- i = applicationFonts.count() - 1;
+ i = applicationFonts.size() - 1;
}
if (font.fileName.isEmpty() && !fontData.isEmpty())
@@ -2164,7 +2194,7 @@ int QFontDatabasePrivate::addAppFont(const QByteArray &fontData, const QString &
bool QFontDatabasePrivate::isApplicationFont(const QString &fileName)
{
- for (int i = 0; i < applicationFonts.count(); ++i)
+ for (int i = 0; i < applicationFonts.size(); ++i)
if (applicationFonts.at(i).fileName == fileName)
return true;
return false;
@@ -2296,7 +2326,7 @@ bool QFontDatabase::removeApplicationFont(int handle)
QMutexLocker locker(fontDatabaseMutex());
auto *db = QFontDatabasePrivate::instance();
- if (handle < 0 || handle >= db->applicationFonts.count())
+ if (handle < 0 || handle >= db->applicationFonts.size())
return false;
db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
@@ -2331,6 +2361,126 @@ bool QFontDatabase::removeAllApplicationFonts()
}
/*!
+ \since 6.8
+
+ Adds \a familyName as an application-defined fallback font for \a script.
+
+ When Qt encounters characters that are not supported by the selected font, it will search
+ through a list of fallback fonts to find a match for them. This ensures that combining multiple
+ scripts in a single string is possible, even if the main font does not support them.
+
+ The list of fallback fonts is selected based on the script of the string as well as other
+ conditions, such as system language.
+
+ While the system fallback list is usually sufficient, there are cases where it is useful
+ to override the default behavior. One such case is for using application fonts as fallback to
+ ensure cross-platform consistency.
+
+ In another case the application may be written in a script with regional differences and want
+ to run it untranslated in multiple regions. In this case, it might be useful to override the
+ local region's fallback with one that matches the language of the application.
+
+ By passing \a familyName to addApplicationFallbackFontFamily(), this will become the preferred
+ family when matching missing characters from \a script. The \a script must be a valid script
+ (\c QChar::Script_Latin or higher). When adding multiple fonts for the same script, they will
+ be prioritized in reverse order, so that the last family added will be checked first and so
+ on.
+
+ \sa setApplicationFallbackFontFamilies(), removeApplicationFallbackFontFamily(), applicationFallbackFontFamilies()
+*/
+void QFontDatabase::addApplicationFallbackFontFamily(QChar::Script script, const QString &familyName)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ if (script < QChar::Script_Latin) {
+ qCWarning(lcFontDb) << "Invalid script passed to addApplicationFallbackFontFamily:" << script;
+ return;
+ }
+
+ auto *db = QFontDatabasePrivate::instance();
+ auto it = db->applicationFallbackFontFamilies.find(script);
+ if (it == db->applicationFallbackFontFamilies.end())
+ it = db->applicationFallbackFontFamilies.insert(script, QStringList{});
+
+ it->prepend(familyName);
+ db->fallbacksCache.clear();
+}
+
+/*!
+ \since 6.8
+
+ Removes \a familyName from the list of application-defined fallback fonts for \a script,
+ provided that it has previously been added with \l{addApplicationFallbackFontFamily()}.
+
+ Returns true if the family name was in the list and false if it was not.
+
+ \sa addApplicationFallbackFontFamily(), setApplicationFallbackFontFamilies(), applicationFallbackFontFamilies()
+*/
+bool QFontDatabase::removeApplicationFallbackFontFamily(QChar::Script script, const QString &familyName)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ auto *db = QFontDatabasePrivate::instance();
+ auto it = db->applicationFallbackFontFamilies.find(script);
+ if (it != db->applicationFallbackFontFamilies.end()) {
+ if (it->removeAll(familyName) > 0) {
+ if (it->isEmpty())
+ it = db->applicationFallbackFontFamilies.erase(it);
+ QFontCache::instance()->clear();
+ db->fallbacksCache.clear();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*!
+ \since 6.8
+
+ Sets the list of application-defined fallback fonts for \a script to \a familyNames.
+
+ When Qt encounters a character in \a script which is not supported by the current font, it will
+ check the families in \a familyNames, in order from first to last, until it finds a match. See
+ \l{addApplicationFallbackFontFamily()} for more details.
+
+ This function overwrites the current list of application-defined fallback fonts for \a script.
+
+ \sa addApplicationFallbackFontFamily(), removeApplicationFallbackFontFamily(), applicationFallbackFontFamilies()
+*/
+void QFontDatabase::setApplicationFallbackFontFamilies(QChar::Script script, const QStringList &familyNames)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ if (script < QChar::Script_Latin) {
+ qCWarning(lcFontDb) << "Invalid script passed to setApplicationFallbackFontFamilies:" << script;
+ return;
+ }
+
+ auto *db = QFontDatabasePrivate::instance();
+ db->applicationFallbackFontFamilies[script] = familyNames;
+
+ QFontCache::instance()->clear();
+ db->fallbacksCache.clear();
+}
+
+/*!
+ \since 6.8
+
+ Returns the list of application-defined fallback font families previously added for \a script
+ by the \l{addApplicationFallbackFontFamily()} function.
+
+ \sa setApplicationFallbackFontFamilies(), addApplicationFallbackFontFamily(), removeApplicationFallbackFontFamily()
+*/
+QStringList QFontDatabase::applicationFallbackFontFamilies(QChar::Script script)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ auto *db = QFontDatabasePrivate::instance();
+ return db->applicationFallbackFontFamilies.value(script);
+}
+
+/*!
\internal
*/
QFontEngine *QFontDatabasePrivate::findFont(const QFontDef &req,
@@ -2436,7 +2586,7 @@ QFontEngine *QFontDatabasePrivate::findFont(const QFontDef &req,
if (!engine) {
QtFontDesc desc;
do {
- index = match(multi ? QChar::Script_Common : script, def, def.families.first(), ""_L1, &desc, blackListed);
+ index = match(multi ? QChar::Script_Common : script, def, def.families.constFirst(), ""_L1, &desc, blackListed);
if (index >= 0) {
QFontDef loadDef = def;
if (loadDef.families.isEmpty())
@@ -2498,7 +2648,7 @@ void QFontDatabasePrivate::load(const QFontPrivate *d, int script)
QFontEngine *fe = nullptr;
- Q_TRACE(QFontDatabase_load, req.families, req.pointSize);
+ Q_TRACE(QFontDatabase_load, req.families.join(QLatin1Char(';')), req.pointSize);
req.fallBackFamilies = fallBackFamilies;
if (!req.fallBackFamilies.isEmpty())
@@ -2512,7 +2662,7 @@ void QFontDatabasePrivate::load(const QFontPrivate *d, int script)
family_list << req.families.at(0);
// add the default family
- auto families = QGuiApplication::font().families();
+ const auto families = QGuiApplication::font().families();
if (!families.isEmpty()) {
QString defaultFamily = families.first();
if (! family_list.contains(defaultFamily))
@@ -2580,8 +2730,8 @@ Q_GUI_EXPORT QStringList qt_sort_families_by_writing_system(QChar::Script script
for (int x = 0; x < db->count; ++x) {
if (Q_UNLIKELY(matchFamilyName(family, db->families[x]))) {
testFamily = db->families[x];
- testFamily->ensurePopulated();
- break;
+ if (testFamily->ensurePopulated())
+ break;
}
}
diff --git a/src/gui/text/qfontdatabase.h b/src/gui/text/qfontdatabase.h
index c66451a164..91a534265e 100644
--- a/src/gui/text/qfontdatabase.h
+++ b/src/gui/text/qfontdatabase.h
@@ -112,6 +112,11 @@ public:
static bool removeApplicationFont(int id);
static bool removeAllApplicationFonts();
+ static void addApplicationFallbackFontFamily(QChar::Script script, const QString &familyName);
+ static bool removeApplicationFallbackFontFamily(QChar::Script script, const QString &familyName);
+ static void setApplicationFallbackFontFamilies(QChar::Script, const QStringList &familyNames);
+ static QStringList applicationFallbackFontFamilies(QChar::Script script);
+
static QFont systemFont(SystemFont type);
};
diff --git a/src/gui/text/qfontdatabase_p.h b/src/gui/text/qfontdatabase_p.h
index b40a651bbb..38e1b4ad20 100644
--- a/src/gui/text/qfontdatabase_p.h
+++ b/src/gui/text/qfontdatabase_p.h
@@ -174,7 +174,7 @@ struct Q_GUI_EXPORT QtFontFamily
bool matchesFamilyName(const QString &familyName) const;
QtFontFoundry *foundry(const QString &f, bool = false);
- void ensurePopulated();
+ bool ensurePopulated();
};
class Q_GUI_EXPORT QFontDatabasePrivate
@@ -204,9 +204,15 @@ public:
QtFontFamily **families;
bool populated = false;
+ QHash<QChar::Script, QStringList> applicationFallbackFontFamilies;
+
QCache<QtFontFallbacksCacheKey, QStringList> fallbacksCache;
struct ApplicationFont {
QString fileName;
+
+ // Note: The data may be implicitly shared throughout the
+ // font database and platform font database, so be careful
+ // to never detach when accessing this member!
QByteArray data;
bool isNull() const { return fileName.isEmpty(); }
diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp
index 6814b9f2b5..4e78aaac2e 100644
--- a/src/gui/text/qfontengine.cpp
+++ b/src/gui/text/qfontengine.cpp
@@ -13,6 +13,7 @@
#include "qpainter.h"
#include "qpainterpath.h"
#include "qvarlengtharray.h"
+#include "qtextengine_p.h"
#include <qmath.h>
#include <qendian.h>
#include <private/qstringiterator_p.h>
@@ -182,8 +183,10 @@ bool QFontEngine::supportsScript(QChar::Script script) const
#if QT_CONFIG(harfbuzz)
// in AAT fonts, 'gsub' table is effectively replaced by 'mort'/'morx' table
uint lenMort = 0, lenMorx = 0;
- if (getSfntTableData(MAKE_TAG('m','o','r','t'), nullptr, &lenMort) || getSfntTableData(MAKE_TAG('m','o','r','x'), nullptr, &lenMorx))
+ if (getSfntTableData(QFont::Tag("mort").value(), nullptr, &lenMort)
+ || getSfntTableData(QFont::Tag("morx").value(), nullptr, &lenMorx)) {
return true;
+ }
if (hb_face_t *face = hb_qt_face_get_for_engine(const_cast<QFontEngine *>(this))) {
unsigned int script_count = HB_OT_MAX_TAGS_PER_SCRIPT;
@@ -381,7 +384,7 @@ void QFontEngine::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rig
bool QFontEngine::processHheaTable() const
{
- QByteArray hhea = getSfntTable(MAKE_TAG('h', 'h', 'e', 'a'));
+ QByteArray hhea = getSfntTable(QFont::Tag("hhea").value());
if (hhea.size() >= 10) {
auto ptr = hhea.constData();
qint16 ascent = qFromBigEndian<qint16>(ptr + 4);
@@ -407,15 +410,21 @@ bool QFontEngine::processHheaTable() const
void QFontEngine::initializeHeightMetrics() const
{
bool hasEmbeddedBitmaps =
- !getSfntTable(MAKE_TAG('E', 'B', 'L', 'C')).isEmpty()
- || !getSfntTable(MAKE_TAG('C', 'B', 'L', 'C')).isEmpty()
- || !getSfntTable(MAKE_TAG('b', 'd', 'a', 't')).isEmpty();
+ !getSfntTable(QFont::Tag("EBLC").value()).isEmpty()
+ || !getSfntTable(QFont::Tag("CBLC").value()).isEmpty()
+ || !getSfntTable(QFont::Tag("bdat").value()).isEmpty();
if (!hasEmbeddedBitmaps) {
// Get HHEA table values if available
processHheaTable();
// Allow OS/2 metrics to override if present
processOS2Table();
+
+ if (!supportsSubPixelPositions()) {
+ m_ascent = m_ascent.round();
+ m_descent = m_descent.round();
+ m_leading = m_leading.round();
+ }
}
m_heightMetricsQueried = true;
@@ -423,7 +432,7 @@ void QFontEngine::initializeHeightMetrics() const
bool QFontEngine::processOS2Table() const
{
- QByteArray os2 = getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
+ QByteArray os2 = getSfntTable(QFont::Tag("OS/2").value());
if (os2.size() >= 78) {
auto ptr = os2.constData();
quint16 fsSelection = qFromBigEndian<quint16>(ptr + 62);
@@ -448,6 +457,7 @@ bool QFontEngine::processOS2Table() const
return false;
m_ascent = QFixed::fromReal(winAscent * fontDef.pixelSize) / unitsPerEm;
m_descent = QFixed::fromReal(winDescent * fontDef.pixelSize) / unitsPerEm;
+ m_leading = QFixed{};
}
return true;
@@ -498,7 +508,7 @@ qreal QFontEngine::minRightBearing() const
if (m_minRightBearing == kBearingNotInitialized) {
// Try the 'hhea' font table first, which covers the entire font
- QByteArray hheaTable = getSfntTable(MAKE_TAG('h', 'h', 'e', 'a'));
+ QByteArray hheaTable = getSfntTable(QFont::Tag("hhea").value());
if (hheaTable.size() >= int(kMinRightSideBearingOffset + sizeof(qint16))) {
const uchar *tableData = reinterpret_cast<const uchar *>(hheaTable.constData());
Q_ASSERT(q16Dot16ToFloat(qFromBigEndian<quint32>(tableData)) == 1.0);
@@ -562,6 +572,16 @@ qreal QFontEngine::minRightBearing() const
return m_minRightBearing;
}
+glyph_metrics_t QFontEngine::boundingBox(const QGlyphLayout &glyphs)
+{
+ QFixed w;
+ for (int i = 0; i < glyphs.numGlyphs; ++i)
+ w += glyphs.effectiveAdvance(i);
+ const QFixed leftBearing = firstLeftBearing(glyphs);
+ const QFixed rightBearing = lastRightBearing(glyphs);
+ return glyph_metrics_t(leftBearing, -(ascent()), w - leftBearing - rightBearing, ascent() + descent(), w, 0);
+}
+
glyph_metrics_t QFontEngine::tightBoundingBox(const QGlyphLayout &glyphs)
{
glyph_metrics_t overall;
@@ -1028,7 +1048,7 @@ void QFontEngine::loadKerningPairs(QFixed scalingFactor)
{
kerning_pairs.clear();
- QByteArray tab = getSfntTable(MAKE_TAG('k', 'e', 'r', 'n'));
+ QByteArray tab = getSfntTable(QFont::Tag("kern").value());
if (tab.isEmpty())
return;
@@ -1117,7 +1137,7 @@ end:
int QFontEngine::glyphCount() const
{
- QByteArray maxpTable = getSfntTable(MAKE_TAG('m', 'a', 'x', 'p'));
+ QByteArray maxpTable = getSfntTable(QFont::Tag("maxp").value());
if (maxpTable.size() < 6)
return 0;
@@ -1164,7 +1184,7 @@ const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSy
int tableToUse = -1;
int score = Invalid;
for (int n = 0; n < numTables; ++n) {
- quint16 platformId;
+ quint16 platformId = 0;
if (!qSafeFromBigEndian(maps + 8 * n, endPtr, &platformId))
return nullptr;
@@ -1215,6 +1235,7 @@ const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSy
default:
break;
}
+ break;
default:
break;
}
@@ -1294,7 +1315,7 @@ resolveTable:
quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint unicode)
{
const uchar *end = cmap + cmapSize;
- quint16 format;
+ quint16 format = 0;
if (!qSafeFromBigEndian(cmap, end, &format))
return 0;
@@ -1311,7 +1332,7 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint
if (unicode >= 0xffff)
return 0;
- quint16 segCountX2;
+ quint16 segCountX2 = 0;
if (!qSafeFromBigEndian(cmap + 6, end, &segCountX2))
return 0;
@@ -1319,7 +1340,7 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint
int i = 0;
for (; i < segCountX2/2; ++i) {
- quint16 codePoint;
+ quint16 codePoint = 0;
if (!qSafeFromBigEndian(ends + 2 * i, end, &codePoint))
return 0;
if (codePoint >= unicode)
@@ -1328,7 +1349,7 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint
const unsigned char *idx = ends + segCountX2 + 2 + 2*i;
- quint16 startIndex;
+ quint16 startIndex = 0;
if (!qSafeFromBigEndian(idx, end, &startIndex))
return 0;
if (startIndex > unicode)
@@ -1336,20 +1357,20 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint
idx += segCountX2;
- quint16 tmp;
+ quint16 tmp = 0;
if (!qSafeFromBigEndian(idx, end, &tmp))
return 0;
qint16 idDelta = qint16(tmp);
idx += segCountX2;
- quint16 idRangeoffset_t;
+ quint16 idRangeoffset_t = 0;
if (!qSafeFromBigEndian(idx, end, &idRangeoffset_t))
return 0;
- quint16 glyphIndex;
+ quint16 glyphIndex = 0;
if (idRangeoffset_t) {
- quint16 id;
+ quint16 id = 0;
if (!qSafeFromBigEndian(idRangeoffset_t + 2 * (unicode - startIndex) + idx, end, &id))
return 0;
@@ -1362,17 +1383,17 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint
}
return glyphIndex;
} else if (format == 6) {
- quint16 tableSize;
+ quint16 tableSize = 0;
if (!qSafeFromBigEndian(cmap + 2, end, &tableSize))
return 0;
- quint16 firstCode6;
+ quint16 firstCode6 = 0;
if (!qSafeFromBigEndian(cmap + 6, end, &firstCode6))
return 0;
if (unicode < firstCode6)
return 0;
- quint16 entryCount6;
+ quint16 entryCount6 = 0;
if (!qSafeFromBigEndian(cmap + 8, end, &entryCount6))
return 0;
if (entryCount6 * 2 + 10 > tableSize)
@@ -1388,7 +1409,7 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint
qSafeFromBigEndian(cmap + 10 + (entryIndex6 * 2), end, &index);
return index;
} else if (format == 12) {
- quint32 nGroups;
+ quint32 nGroups = 0;
if (!qSafeFromBigEndian(cmap + 12, end, &nGroups))
return 0;
@@ -1398,19 +1419,19 @@ quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint
while (left <= right) {
int middle = left + ( ( right - left ) >> 1 );
- quint32 startCharCode;
+ quint32 startCharCode = 0;
if (!qSafeFromBigEndian(cmap + 12 * middle, end, &startCharCode))
return 0;
if (unicode < startCharCode)
right = middle - 1;
else {
- quint32 endCharCode;
+ quint32 endCharCode = 0;
if (!qSafeFromBigEndian(cmap + 12 * middle + 4, end, &endCharCode))
return 0;
if (unicode <= endCharCode) {
- quint32 index;
+ quint32 index = 0;
if (!qSafeFromBigEndian(cmap + 12 * middle + 8, end, &index))
return 0;
@@ -1452,6 +1473,17 @@ bool QFontEngine::hasUnreliableGlyphOutline() const
return glyphFormat == QFontEngine::Format_ARGB;
}
+QFixed QFontEngine::firstLeftBearing(const QGlyphLayout &glyphs)
+{
+ for (int i = 0; i < glyphs.numGlyphs; ++i) {
+ glyph_t glyph = glyphs.glyphs[i];
+ glyph_metrics_t gi = boundingBox(glyph);
+ if (gi.isValid() && gi.width > 0)
+ return gi.leftBearing();
+ }
+ return 0;
+}
+
QFixed QFontEngine::lastRightBearing(const QGlyphLayout &glyphs)
{
if (glyphs.numGlyphs >= 1) {
@@ -1511,12 +1543,12 @@ glyph_t QFontEngineBox::glyphIndex(uint ucs4) const
return 1;
}
-bool QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
+int QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
{
Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
if (*nglyphs < len) {
*nglyphs = len;
- return false;
+ return -1;
}
int ucs4Length = 0;
@@ -1532,7 +1564,7 @@ bool QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyph
if (!(flags & GlyphIndicesOnly))
recalcAdvances(glyphs, flags);
- return true;
+ return *nglyphs;
}
void QFontEngineBox::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags) const
@@ -1699,7 +1731,7 @@ void QFontEngineMulti::ensureFallbackFamiliesQueried()
if (styleHint == QFont::AnyStyle && fontDef.fixedPitch)
styleHint = QFont::TypeWriter;
- setFallbackFamiliesList(qt_fallbacksForFamily(fontDef.families.first(),
+ setFallbackFamiliesList(qt_fallbacksForFamily(fontDef.families.constFirst(),
QFont::Style(fontDef.style), styleHint,
QChar::Script(m_script)));
}
@@ -1715,7 +1747,7 @@ void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamili
QFontEngine *engine = m_engines.at(0);
engine->ref.ref();
m_engines[1] = engine;
- m_fallbackFamilies << fontDef.families.first();
+ m_fallbackFamilies << fontDef.families.constFirst();
} else {
m_engines.resize(m_fallbackFamilies.size() + 1);
}
@@ -1762,11 +1794,7 @@ QFontEngine *QFontEngineMulti::loadEngine(int at)
glyph_t QFontEngineMulti::glyphIndex(uint ucs4) const
{
glyph_t glyph = engine(0)->glyphIndex(ucs4);
- if (glyph == 0
- && ucs4 != QChar::LineSeparator
- && ucs4 != QChar::LineFeed
- && ucs4 != QChar::CarriageReturn
- && ucs4 != QChar::ParagraphSeparator) {
+ if (glyph == 0 && !isIgnorableChar(ucs4)) {
if (!m_fallbackFamiliesQueried)
const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
@@ -1793,17 +1821,60 @@ glyph_t QFontEngineMulti::glyphIndex(uint ucs4) const
return glyph;
}
-bool QFontEngineMulti::stringToCMap(const QChar *str, int len,
- QGlyphLayout *glyphs, int *nglyphs,
- QFontEngine::ShaperFlags flags) const
+int QFontEngineMulti::stringToCMap(const QChar *str, int len,
+ QGlyphLayout *glyphs, int *nglyphs,
+ QFontEngine::ShaperFlags flags) const
{
- if (!engine(0)->stringToCMap(str, len, glyphs, nglyphs, flags))
- return false;
+ const int originalNumGlyphs = glyphs->numGlyphs;
+ int mappedGlyphCount = engine(0)->stringToCMap(str, len, glyphs, nglyphs, flags);
+ if (mappedGlyphCount < 0)
+ return -1;
+
+ // If ContextFontMerging is set and the match for the string was incomplete, we try all
+ // fallbacks on the full string until we find the best match.
+ bool contextFontMerging = mappedGlyphCount < *nglyphs && (fontDef.styleStrategy & QFont::ContextFontMerging);
+ if (contextFontMerging) {
+ QVarLengthGlyphLayoutArray tempLayout(len);
+ if (!m_fallbackFamiliesQueried)
+ const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
+
+ int maxGlyphCount = 0;
+ uchar engineIndex = 0;
+ for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
+ int numGlyphs = len;
+ const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
+ maxGlyphCount = engine(x)->stringToCMap(str, len, &tempLayout, &numGlyphs, flags);
+
+ // If we found a better match, we copy data into the main QGlyphLayout
+ if (maxGlyphCount > mappedGlyphCount) {
+ *nglyphs = numGlyphs;
+ glyphs->numGlyphs = originalNumGlyphs;
+ glyphs->copy(&tempLayout);
+ engineIndex = x;
+ if (maxGlyphCount == numGlyphs)
+ break;
+ }
+ }
+ if (engineIndex > 0) {
+ for (int y = 0; y < glyphs->numGlyphs; ++y) {
+ if (glyphs->glyphs[y] != 0)
+ glyphs->glyphs[y] |= (engineIndex << 24);
+ }
+ } else {
+ contextFontMerging = false;
+ }
+
+ mappedGlyphCount = maxGlyphCount;
+ }
+
+ // Fill in missing glyphs by going through string one character at the time and finding
+ // the first viable fallback.
int glyph_pos = 0;
QStringIterator it(str, str + len);
int lastFallback = -1;
+ char32_t previousUcs4 = 0;
while (it.hasNext()) {
const char32_t ucs4 = it.peekNext();
@@ -1829,14 +1900,10 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len,
lastFallback = -1;
}
- if (glyphs->glyphs[glyph_pos] == 0
- && ucs4 != QChar::LineSeparator
- && ucs4 != QChar::LineFeed
- && ucs4 != QChar::CarriageReturn
- && ucs4 != QChar::ParagraphSeparator) {
+ if (glyphs->glyphs[glyph_pos] == 0 && !isIgnorableChar(ucs4)) {
if (!m_fallbackFamiliesQueried)
const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
- for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
+ for (int x = contextFontMerging ? 0 : 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
QFontEngine *engine = m_engines.at(x);
if (!engine) {
if (!shouldLoadFontEngineForCharacter(x, ucs4))
@@ -1865,16 +1932,55 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len,
break;
}
}
+
+ // For variant-selectors, they are modifiers to the previous character. If we
+ // end up with different font selections for the selector and the character it
+ // modifies, we try applying the selector font to the preceding character as well
+ const int variantSelectorBlock = 0xFE00;
+ if ((ucs4 & 0xFFF0) == variantSelectorBlock && glyph_pos > 0) {
+ int selectorFontEngine = glyphs->glyphs[glyph_pos] >> 24;
+ int precedingCharacterFontEngine = glyphs->glyphs[glyph_pos - 1] >> 24;
+
+ if (selectorFontEngine != precedingCharacterFontEngine) {
+ // Emoji variant selectors are specially handled and should affect font
+ // selection. If VS-16 is used, then this means we want to select a color
+ // font. If the selected font is already a color font, we do not need search
+ // again. If the VS-15 is used, then this means we want to select a non-color
+ // font. If the selected font is not a color font, we don't do anything.
+ const QFontEngine *selectedEngine = m_engines.at(precedingCharacterFontEngine);
+ const bool colorFont = selectedEngine->isColorFont();
+ const char32_t vs15 = 0xFE0E;
+ const char32_t vs16 = 0xFE0F;
+ bool adaptVariantSelector = ucs4 < vs15
+ || (ucs4 == vs15 && colorFont)
+ || (ucs4 == vs16 && !colorFont);
+
+ if (adaptVariantSelector) {
+ QFontEngine *engine = m_engines.at(selectorFontEngine);
+ glyph_t glyph = engine->glyphIndex(previousUcs4);
+ if (glyph != 0) {
+ glyphs->glyphs[glyph_pos - 1] = glyph;
+ if (!(flags & GlyphIndicesOnly)) {
+ QGlyphLayout g = glyphs->mid(glyph_pos - 1, 1);
+ engine->recalcAdvances(&g, flags);
+ }
+
+ // set the high byte to indicate which engine the glyph came from
+ glyphs->glyphs[glyph_pos - 1] |= (selectorFontEngine << 24);
+ }
+ }
+ }
+ }
}
it.advance();
++glyph_pos;
+ previousUcs4 = ucs4;
}
*nglyphs = glyph_pos;
glyphs->numGlyphs = glyph_pos;
-
- return true;
+ return mappedGlyphCount;
}
bool QFontEngineMulti::shouldLoadFontEngineForCharacter(int at, uint ucs4) const
@@ -2166,7 +2272,7 @@ bool QFontEngineMulti::canRender(const QChar *string, int len) const
QGlyphLayout g;
g.numGlyphs = nglyphs;
g.glyphs = glyphs.data();
- if (!stringToCMap(string, len, &g, &nglyphs, GlyphIndicesOnly))
+ if (stringToCMap(string, len, &g, &nglyphs, GlyphIndicesOnly) < 0)
Q_UNREACHABLE();
for (int i = 0; i < nglyphs; i++) {
diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h
index 6289afa9d0..a0e0801354 100644
--- a/src/gui/text/qfontengine_p.h
+++ b/src/gui/text/qfontengine_p.h
@@ -29,13 +29,6 @@ class QFontEngineGlyphCache;
struct QGlyphLayout;
-#define MAKE_TAG(ch1, ch2, ch3, ch4) (\
- (((quint32)(ch1)) << 24) | \
- (((quint32)(ch2)) << 16) | \
- (((quint32)(ch3)) << 8) | \
- ((quint32)(ch4)) \
- )
-
// ### this only used in getPointInOutline(), refactor it and then remove these magic numbers
enum HB_Compat_Error {
Err_Ok = 0x0000,
@@ -83,7 +76,8 @@ public:
enum ShaperFlag {
DesignMetrics = 0x0002,
- GlyphIndicesOnly = 0x0004
+ GlyphIndicesOnly = 0x0004,
+ FullStringFallback = 0x008
};
Q_DECLARE_FLAGS(ShaperFlags, ShaperFlag)
@@ -127,11 +121,13 @@ public:
virtual bool getSfntTableData(uint tag, uchar *buffer, uint *length) const;
struct FaceId {
- FaceId() : index(0), encoding(0) {}
+ FaceId() : index(0), instanceIndex(-1), encoding(0) {}
QByteArray filename;
QByteArray uuid;
int index;
+ int instanceIndex;
int encoding;
+ QMap<QFont::Tag, float> variableAxes;
};
virtual FaceId faceId() const { return FaceId(); }
enum SynthesizedFlags {
@@ -147,16 +143,26 @@ public:
virtual bool supportsHorizontalSubPixelPositions() const { return false; }
virtual bool supportsVerticalSubPixelPositions() const { return false; }
virtual QFixedPoint subPixelPositionFor(const QFixedPoint &position) const;
- QFixed subPixelPositionForX(const QFixed &x) const
+ QFixed subPixelPositionForX(QFixed x) const
{
return subPixelPositionFor(QFixedPoint(x, 0)).x;
}
+ bool isColorFont() const { return glyphFormat == Format_ARGB; }
+ static bool isIgnorableChar(char32_t ucs4)
+ {
+ return ucs4 == QChar::LineSeparator
+ || ucs4 == QChar::LineFeed
+ || ucs4 == QChar::CarriageReturn
+ || ucs4 == QChar::ParagraphSeparator
+ || QChar::category(ucs4) == QChar::Other_Control;
+ }
+
virtual QFixed emSquareSize() const { return ascent(); }
/* returns 0 as glyph index for non existent glyphs */
virtual glyph_t glyphIndex(uint ucs4) const = 0;
- virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const = 0;
+ virtual int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const = 0;
virtual void recalcAdvances(QGlyphLayout *, ShaperFlags) const {}
virtual void doKerning(QGlyphLayout *, ShaperFlags) const;
@@ -189,7 +195,7 @@ public:
virtual void removeGlyphFromCache(glyph_t);
- virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) = 0;
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
virtual glyph_metrics_t boundingBox(glyph_t glyph) = 0;
virtual glyph_metrics_t boundingBox(glyph_t glyph, const QTransform &matrix);
glyph_metrics_t tightBoundingBox(const QGlyphLayout &glyphs);
@@ -277,8 +283,8 @@ public:
explicit Holder(void *p, qt_destroy_func_t d) : ptr(p), destroy_func(d) {}
~Holder() { if (ptr && destroy_func) destroy_func(ptr); }
Holder(Holder &&other) noexcept
- : ptr(qExchange(other.ptr, nullptr)),
- destroy_func(qExchange(other.destroy_func, nullptr))
+ : ptr(std::exchange(other.ptr, nullptr)),
+ destroy_func(std::exchange(other.destroy_func, nullptr))
{
}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(Holder)
@@ -332,6 +338,7 @@ public:
protected:
explicit QFontEngine(Type type);
+ QFixed firstLeftBearing(const QGlyphLayout &glyphs);
QFixed lastRightBearing(const QGlyphLayout &glyphs);
QFixed calculatedCapHeight() const;
@@ -369,13 +376,18 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QFontEngine::ShaperFlags)
inline bool operator ==(const QFontEngine::FaceId &f1, const QFontEngine::FaceId &f2)
{
- return f1.index == f2.index && f1.encoding == f2.encoding && f1.filename == f2.filename && f1.uuid == f2.uuid;
+ return f1.index == f2.index
+ && f1.encoding == f2.encoding
+ && f1.filename == f2.filename
+ && f1.uuid == f2.uuid
+ && f1.instanceIndex == f2.instanceIndex
+ && f1.variableAxes == f2.variableAxes;
}
inline size_t qHash(const QFontEngine::FaceId &f, size_t seed = 0)
noexcept(noexcept(qHash(f.filename)))
{
- return qHashMulti(seed, f.filename, f.uuid, f.index, f.encoding);
+ return qHashMulti(seed, f.filename, f.uuid, f.index, f.instanceIndex, f.encoding, f.variableAxes.keys(), f.variableAxes.values());
}
@@ -390,7 +402,7 @@ public:
~QFontEngineBox();
virtual glyph_t glyphIndex(uint ucs4) const override;
- virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override;
+ virtual int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override;
virtual void recalcAdvances(QGlyphLayout *, ShaperFlags) const override;
void draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &si);
@@ -428,7 +440,7 @@ public:
~QFontEngineMulti();
virtual glyph_t glyphIndex(uint ucs4) const override;
- virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override;
+ virtual int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override;
virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) override;
virtual glyph_metrics_t boundingBox(glyph_t glyph) override;
diff --git a/src/gui/text/qfontengineglyphcache_p.h b/src/gui/text/qfontengineglyphcache_p.h
index b829b79788..9054ea5950 100644
--- a/src/gui/text/qfontengineglyphcache_p.h
+++ b/src/gui/text/qfontengineglyphcache_p.h
@@ -15,7 +15,7 @@
// We mean it.
//
-
+#include <QtCore/qshareddata.h>
#include <QtGui/private/qtguiglobal_p.h>
#include "QtCore/qatomic.h"
#include <QtCore/qvarlengtharray.h>
diff --git a/src/gui/text/qfontinfo.h b/src/gui/text/qfontinfo.h
index 5818ca0d00..0edee5abe5 100644
--- a/src/gui/text/qfontinfo.h
+++ b/src/gui/text/qfontinfo.h
@@ -6,7 +6,8 @@
#include <QtGui/qtguiglobal.h>
#include <QtGui/qfont.h>
-#include <QtCore/qsharedpointer.h>
+
+#include <QtCore/qshareddata.h>
QT_BEGIN_NAMESPACE
diff --git a/src/gui/text/qfontmetrics.cpp b/src/gui/text/qfontmetrics.cpp
index 7997f643ab..f7e405f0b5 100644
--- a/src/gui/text/qfontmetrics.cpp
+++ b/src/gui/text/qfontmetrics.cpp
@@ -75,15 +75,23 @@ extern void qt_format_text(const QFont& font, const QRectF &_r,
inFont(). You can also treat the character as a string, and use
the string functions on it.
- The string functions include horizontalAdvance(), to return the width of a
- string in pixels (or points, for a printer), boundingRect(), to
- return a rectangle large enough to contain the rendered string,
+ The string functions include horizontalAdvance(), to return the advance
+ width of a string in pixels (or points, for a printer), boundingRect(),
+ to return a rectangle large enough to contain the rendered string,
and size(), to return the size of that rectangle.
+ \note The advance width can be different from the width of the actual
+ rendered text. It refers to the distance from the origin of the string to
+ where you would append additional characters. As text may have overhang
+ (in the case of an italic font for instance) or padding between
+ characters, the advance width can be either smaller or larger than the
+ actual rendering of the text. This is called the right bearing of the
+ text.
+
Example:
\snippet code/src_gui_text_qfontmetrics.cpp 0
- \sa QFont, QFontInfo, QFontDatabase, {Character Map Example}
+ \sa QFont, QFontInfo, QFontDatabase
*/
/*!
@@ -473,10 +481,13 @@ int QFontMetrics::rightBearing(QChar ch) const
return qRound(rb);
}
+static constexpr QLatin1Char s_variableLengthStringSeparator('\x9c');
+
/*!
Returns the horizontal advance in pixels of the first \a len characters of \a
text. If \a len is negative (the default), the entire string is
- used.
+ used. The entire length of \a text is analysed even if \a len is substantially
+ shorter.
This is the distance appropriate for drawing a subsequent character
after \a text.
@@ -487,11 +498,13 @@ int QFontMetrics::rightBearing(QChar ch) const
*/
int QFontMetrics::horizontalAdvance(const QString &text, int len) const
{
- int pos = text.indexOf(QLatin1Char('\x9c'));
+ int pos = (len >= 0)
+ ? QStringView(text).left(len).indexOf(s_variableLengthStringSeparator)
+ : text.indexOf(s_variableLengthStringSeparator);
if (pos != -1) {
- len = (len < 0) ? pos : qMin(pos, len);
+ len = pos;
} else if (len < 0) {
- len = text.length();
+ len = text.size();
}
if (len == 0)
return 0;
@@ -512,12 +525,12 @@ int QFontMetrics::horizontalAdvance(const QString &text, int len) const
*/
int QFontMetrics::horizontalAdvance(const QString &text, const QTextOption &option) const
{
- int pos = text.indexOf(QLatin1Char('\x9c'));
+ int pos = text.indexOf(s_variableLengthStringSeparator);
int len = -1;
if (pos != -1) {
- len = (len < 0) ? pos : qMin(pos, len);
- } else if (len < 0) {
- len = text.length();
+ len = pos;
+ } else {
+ len = text.size();
}
if (len == 0)
return 0;
@@ -604,12 +617,12 @@ int QFontMetrics::horizontalAdvance(QChar ch) const
*/
QRect QFontMetrics::boundingRect(const QString &text) const
{
- if (text.length() == 0)
+ if (text.size() == 0)
return QRect();
QStackTextEngine layout(text, QFont(d.data()));
layout.itemize();
- glyph_metrics_t gm = layout.boundingBox(0, text.length());
+ glyph_metrics_t gm = layout.boundingBox(0, text.size());
return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height));
}
@@ -639,13 +652,13 @@ QRect QFontMetrics::boundingRect(const QString &text) const
*/
QRect QFontMetrics::boundingRect(const QString &text, const QTextOption &option) const
{
- if (text.length() == 0)
+ if (text.size() == 0)
return QRect();
QStackTextEngine layout(text, QFont(d.data()));
layout.option = option;
layout.itemize();
- glyph_metrics_t gm = layout.boundingBox(0, text.length());
+ glyph_metrics_t gm = layout.boundingBox(0, text.size());
return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height));
}
@@ -807,12 +820,12 @@ QSize QFontMetrics::size(int flags, const QString &text, int tabStops, int *tabA
*/
QRect QFontMetrics::tightBoundingRect(const QString &text) const
{
- if (text.length() == 0)
+ if (text.size() == 0)
return QRect();
QStackTextEngine layout(text, QFont(d.data()));
layout.itemize();
- glyph_metrics_t gm = layout.tightBoundingBox(0, text.length());
+ glyph_metrics_t gm = layout.tightBoundingBox(0, text.size());
return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height));
}
@@ -839,13 +852,13 @@ QRect QFontMetrics::tightBoundingRect(const QString &text) const
*/
QRect QFontMetrics::tightBoundingRect(const QString &text, const QTextOption &option) const
{
- if (text.length() == 0)
+ if (text.size() == 0)
return QRect();
QStackTextEngine layout(text, QFont(d.data()));
layout.option = option;
layout.itemize();
- glyph_metrics_t gm = layout.tightBoundingBox(0, text.length());
+ glyph_metrics_t gm = layout.tightBoundingBox(0, text.size());
return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height));
}
@@ -876,13 +889,13 @@ QString QFontMetrics::elidedText(const QString &text, Qt::TextElideMode mode, in
QString _text = text;
if (!(flags & Qt::TextLongestVariant)) {
int posA = 0;
- int posB = _text.indexOf(QLatin1Char('\x9c'));
+ int posB = _text.indexOf(s_variableLengthStringSeparator);
while (posB >= 0) {
QString portion = _text.mid(posA, posB - posA);
if (size(flags, portion).width() <= width)
return portion;
posA = posB + 1;
- posB = _text.indexOf(QLatin1Char('\x9c'), posA);
+ posB = _text.indexOf(s_variableLengthStringSeparator, posA);
}
_text = _text.mid(posA);
}
@@ -1387,7 +1400,8 @@ qreal QFontMetricsF::rightBearing(QChar ch) const
/*!
Returns the horizontal advance in pixels of the first \a length characters of \a
text. If \a length is negative (the default), the entire string is
- used.
+ used. The entire length of \a text is analysed even if \a length is substantially
+ shorter.
The advance is the distance appropriate for drawing a subsequent
character after \a text.
@@ -1398,11 +1412,13 @@ qreal QFontMetricsF::rightBearing(QChar ch) const
*/
qreal QFontMetricsF::horizontalAdvance(const QString &text, int length) const
{
- int pos = text.indexOf(QLatin1Char('\x9c'));
+ int pos = (length >= 0)
+ ? QStringView(text).left(length).indexOf(s_variableLengthStringSeparator)
+ : text.indexOf(s_variableLengthStringSeparator);
if (pos != -1)
- length = (length < 0) ? pos : qMin(pos, length);
+ length = pos;
else if (length < 0)
- length = text.length();
+ length = text.size();
if (length == 0)
return 0;
@@ -1424,12 +1440,12 @@ qreal QFontMetricsF::horizontalAdvance(const QString &text, int length) const
*/
qreal QFontMetricsF::horizontalAdvance(const QString &text, const QTextOption &option) const
{
- int pos = text.indexOf(QLatin1Char('\x9c'));
+ int pos = text.indexOf(s_variableLengthStringSeparator);
int length = -1;
if (pos != -1)
- length = (length < 0) ? pos : qMin(pos, length);
- else if (length < 0)
- length = text.length();
+ length = pos;
+ else
+ length = text.size();
if (length == 0)
return 0;
@@ -1516,7 +1532,7 @@ qreal QFontMetricsF::horizontalAdvance(QChar ch) const
*/
QRectF QFontMetricsF::boundingRect(const QString &text) const
{
- int len = text.length();
+ int len = text.size();
if (len == 0)
return QRectF();
@@ -1551,13 +1567,13 @@ QRectF QFontMetricsF::boundingRect(const QString &text) const
*/
QRectF QFontMetricsF::boundingRect(const QString &text, const QTextOption &option) const
{
- if (text.length() == 0)
+ if (text.size() == 0)
return QRectF();
QStackTextEngine layout(text, QFont(d.data()));
layout.option = option;
layout.itemize();
- glyph_metrics_t gm = layout.boundingBox(0, text.length());
+ glyph_metrics_t gm = layout.boundingBox(0, text.size());
return QRectF(gm.x.toReal(), gm.y.toReal(),
gm.width.toReal(), gm.height.toReal());
}
@@ -1725,12 +1741,12 @@ QSizeF QFontMetricsF::size(int flags, const QString &text, int tabStops, int *ta
*/
QRectF QFontMetricsF::tightBoundingRect(const QString &text) const
{
- if (text.length() == 0)
+ if (text.size() == 0)
return QRectF();
QStackTextEngine layout(text, QFont(d.data()));
layout.itemize();
- glyph_metrics_t gm = layout.tightBoundingBox(0, text.length());
+ glyph_metrics_t gm = layout.tightBoundingBox(0, text.size());
return QRectF(gm.x.toReal(), gm.y.toReal(), gm.width.toReal(), gm.height.toReal());
}
@@ -1757,13 +1773,13 @@ QRectF QFontMetricsF::tightBoundingRect(const QString &text) const
*/
QRectF QFontMetricsF::tightBoundingRect(const QString &text, const QTextOption &option) const
{
- if (text.length() == 0)
+ if (text.size() == 0)
return QRectF();
QStackTextEngine layout(text, QFont(d.data()));
layout.option = option;
layout.itemize();
- glyph_metrics_t gm = layout.tightBoundingBox(0, text.length());
+ glyph_metrics_t gm = layout.tightBoundingBox(0, text.size());
return QRectF(gm.x.toReal(), gm.y.toReal(), gm.width.toReal(), gm.height.toReal());
}
@@ -1793,13 +1809,13 @@ QString QFontMetricsF::elidedText(const QString &text, Qt::TextElideMode mode, q
QString _text = text;
if (!(flags & Qt::TextLongestVariant)) {
int posA = 0;
- int posB = _text.indexOf(QLatin1Char('\x9c'));
+ int posB = _text.indexOf(s_variableLengthStringSeparator);
while (posB >= 0) {
QString portion = _text.mid(posA, posB - posA);
if (size(flags, portion).width() <= width)
return portion;
posA = posB + 1;
- posB = _text.indexOf(QLatin1Char('\x9c'), posA);
+ posB = _text.indexOf(s_variableLengthStringSeparator, posA);
}
_text = _text.mid(posA);
}
diff --git a/src/gui/text/qfontmetrics.h b/src/gui/text/qfontmetrics.h
index 18b54861f0..1942d1fa83 100644
--- a/src/gui/text/qfontmetrics.h
+++ b/src/gui/text/qfontmetrics.h
@@ -6,10 +6,11 @@
#include <QtGui/qtguiglobal.h>
#include <QtGui/qfont.h>
-#include <QtCore/qsharedpointer.h>
+
#ifndef QT_INCLUDE_COMPAT
#include <QtCore/qrect.h>
#endif
+#include <QtCore/qshareddata.h>
QT_BEGIN_NAMESPACE
diff --git a/src/gui/text/qfontsubset.cpp b/src/gui/text/qfontsubset.cpp
index f28cc72258..f6c973e522 100644
--- a/src/gui/text/qfontsubset.cpp
+++ b/src/gui/text/qfontsubset.cpp
@@ -78,24 +78,6 @@ QByteArray QFontSubset::glyphName(unsigned short unicode, bool symbol)
return buffer;
}
-QByteArray QFontSubset::glyphName(unsigned int glyph, const QList<int> &reverseMap) const
-{
- uint glyphIndex = glyph_indices[glyph];
-
- if (glyphIndex == 0)
- return "/.notdef";
-
- QByteArray ba;
- QPdf::ByteStream s(&ba);
- if (reverseMap[glyphIndex] && reverseMap[glyphIndex] < 0x10000) {
- s << '/' << glyphName(reverseMap[glyphIndex], false);
- } else {
- s << "/gl" << (int)glyphIndex;
- }
- return ba;
-}
-
-
QByteArray QFontSubset::widthArray() const
{
Q_ASSERT(!widths.isEmpty());
@@ -108,7 +90,7 @@ QByteArray QFontSubset::widthArray() const
QFixed defWidth = widths[0];
//qDebug("defWidth=%d, scale=%f", defWidth.toInt(), scale.toReal());
- for (int i = 0; i < nGlyphs(); ++i) {
+ for (qsizetype i = 0; i < nGlyphs(); ++i) {
if (defWidth != widths[i])
defWidth = 0;
}
@@ -116,10 +98,10 @@ QByteArray QFontSubset::widthArray() const
s << "/DW " << qRound(defWidth.toInt() * scale);
} else {
s << "/W [";
- for (int g = 0; g < nGlyphs();) {
+ for (qsizetype g = 0; g < nGlyphs();) {
QFixed w = widths[g];
- int start = g;
- int startLinear = 0;
+ qsizetype start = g;
+ qsizetype startLinear = 0;
++g;
while (g < nGlyphs()) {
QFixed nw = widths[g];
@@ -137,11 +119,11 @@ QByteArray QFontSubset::widthArray() const
// qDebug("start=%x startLinear=%x g-1=%x",start,startLinear,g-1);
if (g - startLinear < 10)
startLinear = 0;
- int endnonlinear = startLinear ? startLinear : g;
+ qsizetype endnonlinear = startLinear ? startLinear : g;
// qDebug(" startLinear=%x endnonlinear=%x", startLinear,endnonlinear);
if (endnonlinear > start) {
s << start << '[';
- for (int i = start; i < endnonlinear; ++i)
+ for (qsizetype i = start; i < endnonlinear; ++i)
s << qRound(widths[i].toInt() * scale);
s << "]\n";
}
@@ -195,14 +177,14 @@ QByteArray QFontSubset::createToUnicodeMap() const
QPdf::ByteStream s(&ranges);
char buf[5];
- for (int g = 1; g < nGlyphs(); ) {
+ for (qsizetype g = 1; g < nGlyphs(); ) {
int uc0 = reverseMap.at(g);
if (!uc0) {
++g;
continue;
}
- int start = g;
- int startLinear = 0;
+ qsizetype start = g;
+ qsizetype startLinear = 0;
++g;
while (g < nGlyphs()) {
int uc = reverseMap[g];
@@ -223,7 +205,7 @@ QByteArray QFontSubset::createToUnicodeMap() const
// qDebug("start=%x startLinear=%x g-1=%x",start,startLinear,g-1);
if (g - startLinear < 10)
startLinear = 0;
- int endnonlinear = startLinear ? startLinear : g;
+ qsizetype endnonlinear = startLinear ? startLinear : g;
// qDebug(" startLinear=%x endnonlinear=%x", startLinear,endnonlinear);
if (endnonlinear > start) {
s << '<' << QPdf::toHex((ushort)start, buf) << "> <";
@@ -232,7 +214,7 @@ QByteArray QFontSubset::createToUnicodeMap() const
s << '<' << QPdf::toHex((ushort)reverseMap[start], buf) << ">\n";
} else {
s << '[';
- for (int i = start; i < endnonlinear; ++i) {
+ for (qsizetype i = start; i < endnonlinear; ++i) {
s << '<' << QPdf::toHex((ushort)reverseMap[i], buf) << "> ";
}
s << "]\n";
@@ -241,9 +223,9 @@ QByteArray QFontSubset::createToUnicodeMap() const
}
if (startLinear) {
while (startLinear < g) {
- int len = g - startLinear;
- int uc_start = reverseMap[startLinear];
- int uc_end = uc_start + len - 1;
+ qsizetype len = g - startLinear;
+ qsizetype uc_start = reverseMap[startLinear];
+ qsizetype uc_end = uc_start + len - 1;
if ((uc_end >> 8) != (uc_start >> 8))
len = 256 - (uc_start & 0xff);
s << '<' << QPdf::toHex((ushort)startLinear, buf) << "> <";
@@ -266,14 +248,14 @@ QByteArray QFontSubset::createToUnicodeMap() const
return touc;
}
-int QFontSubset::addGlyph(uint index)
+qsizetype QFontSubset::addGlyph(uint index)
{
qsizetype idx = glyph_indices.indexOf(index);
if (idx < 0) {
idx = glyph_indices.size();
glyph_indices.append(index);
}
- return (int)idx;
+ return idx;
}
#endif // QT_NO_PDF
@@ -419,7 +401,7 @@ static QTtfTable generateHead(const qttf_head_table &head)
{
const int head_size = 54;
QTtfTable t;
- t.tag = MAKE_TAG('h', 'e', 'a', 'd');
+ t.tag = QFont::Tag("head").value();
t.data.resize(head_size);
QTtfStream s(t.data);
@@ -490,7 +472,7 @@ static QTtfTable generateHhea(const qttf_hhea_table &hhea)
{
const int hhea_size = 36;
QTtfTable t;
- t.tag = MAKE_TAG('h', 'h', 'e', 'a');
+ t.tag = QFont::Tag("hhea").value();
t.data.resize(hhea_size);
QTtfStream s(t.data);
@@ -541,7 +523,7 @@ static QTtfTable generateMaxp(const qttf_maxp_table &maxp)
{
const int maxp_size = 32;
QTtfTable t;
- t.tag = MAKE_TAG('m', 'a', 'x', 'p');
+ t.tag = QFont::Tag("maxp").value();
t.data.resize(maxp_size);
QTtfStream s(t.data);
@@ -621,12 +603,12 @@ static QTtfTable generateName(const QList<QTtfNameRecord> &name)
const int char_size = 2;
QTtfTable t;
- t.tag = MAKE_TAG('n', 'a', 'm', 'e');
+ t.tag = QFont::Tag("name").value();
const int name_size = 6 + 12*name.size();
int string_size = 0;
for (int i = 0; i < name.size(); ++i) {
- string_size += name.at(i).value.length()*char_size;
+ string_size += name.at(i).value.size()*char_size;
}
t.data.resize(name_size + string_size);
@@ -642,7 +624,7 @@ static QTtfTable generateName(const QList<QTtfNameRecord> &name)
int off = 0;
for (int i = 0; i < name.size(); ++i) {
- int len = name.at(i).value.length()*char_size;
+ int len = name.at(i).value.size()*char_size;
// quint16 platformID Platform ID.
// quint16 encodingID Platform-specific encoding ID.
// quint16 languageID Language ID.
@@ -976,15 +958,15 @@ static QList<QTtfTable> generateGlyphTables(qttf_font_tables &tables, const QLis
tables.hhea.numberOfHMetrics = nGlyphs;
QTtfTable glyf;
- glyf.tag = MAKE_TAG('g', 'l', 'y', 'f');
+ glyf.tag = QFont::Tag("glyf").value();
QTtfTable loca;
- loca.tag = MAKE_TAG('l', 'o', 'c', 'a');
+ loca.tag = QFont::Tag("loca").value();
loca.data.resize(glyf_size < max_size_small ? (nGlyphs+1)*sizeof(quint16) : (nGlyphs+1)*sizeof(quint32));
QTtfStream ls(loca.data);
QTtfTable hmtx;
- hmtx.tag = MAKE_TAG('h', 'm', 't', 'x');
+ hmtx.tag = QFont::Tag("hmtx").value();
hmtx.data.resize(nGlyphs*4);
QTtfStream hs(hmtx.data);
@@ -1084,7 +1066,7 @@ static QByteArray bindFont(const QList<QTtfTable>& _tables)
for (int i = 0; i < tables.size(); ++i) {
const QTtfTable &t = tables.at(i);
const quint32 size = (t.data.size() + 3) & ~3;
- if (t.tag == MAKE_TAG('h', 'e', 'a', 'd'))
+ if (t.tag == QFont::Tag("head").value())
head_offset = table_offset;
f << t.tag
<< checksum(t.data)
@@ -1165,12 +1147,12 @@ QByteArray QFontSubset::toTruetype() const
font.maxp.maxCompositeContours = 0;
font.maxp.maxComponentElements = 0;
font.maxp.maxComponentDepth = 0;
- const int numGlyphs = nGlyphs();
- font.maxp.numGlyphs = numGlyphs;
+ const qsizetype numGlyphs = nGlyphs();
+ font.maxp.numGlyphs = quint16(numGlyphs);
QList<QTtfGlyph> glyphs;
glyphs.reserve(numGlyphs);
- for (int i = 0; i < numGlyphs; ++i) {
+ for (qsizetype i = 0; i < numGlyphs; ++i) {
glyph_t g = glyph_indices.at(i);
QPainterPath path;
glyph_metrics_t metric;
@@ -1204,7 +1186,7 @@ QByteArray QFontSubset::toTruetype() const
tables.append(generateMaxp(font.maxp));
// name
QTtfTable name_table;
- name_table.tag = MAKE_TAG('n', 'a', 'm', 'e');
+ name_table.tag = QFont::Tag("name").value();
if (!noEmbed)
name_table.data = fontEngine->getSfntTable(name_table.tag);
if (name_table.data.isEmpty()) {
@@ -1213,7 +1195,7 @@ QByteArray QFontSubset::toTruetype() const
name.copyright = "Fake font"_L1;
else
name.copyright = QLatin1StringView(properties.copyright);
- name.family = fontEngine->fontDef.families.first();
+ name.family = fontEngine->fontDef.families.constFirst();
name.subfamily = "Regular"_L1; // ######
name.postscript_name = QLatin1StringView(properties.postscriptName);
name_table = generateName(name);
@@ -1222,7 +1204,7 @@ QByteArray QFontSubset::toTruetype() const
if (!noEmbed) {
QTtfTable os2;
- os2.tag = MAKE_TAG('O', 'S', '/', '2');
+ os2.tag = QFont::Tag("OS/2").value();
os2.data = fontEngine->getSfntTable(os2.tag);
if (!os2.data.isEmpty())
tables.append(os2);
diff --git a/src/gui/text/qfontsubset_p.h b/src/gui/text/qfontsubset_p.h
index 1cd24aa73d..1ec47f176d 100644
--- a/src/gui/text/qfontsubset_p.h
+++ b/src/gui/text/qfontsubset_p.h
@@ -41,11 +41,10 @@ public:
QByteArray widthArray() const;
QByteArray createToUnicodeMap() const;
QList<int> getReverseMap() const;
- QByteArray glyphName(unsigned int glyph, const QList<int> &reverseMap) const;
static QByteArray glyphName(unsigned short unicode, bool symbol);
- int addGlyph(uint index);
+ qsizetype addGlyph(uint index);
#endif
const uint object_id;
bool noEmbed;
@@ -53,7 +52,7 @@ public:
QList<uint> glyph_indices;
mutable int downloaded_glyphs;
mutable bool standard_font;
- int nGlyphs() const { return glyph_indices.size(); }
+ qsizetype nGlyphs() const { return glyph_indices.size(); }
mutable QFixed emSquare;
mutable QList<QFixed> widths;
};
diff --git a/src/gui/text/qglyphrun.cpp b/src/gui/text/qglyphrun.cpp
index 371dd26637..4252d9e7f1 100644
--- a/src/gui/text/qglyphrun.cpp
+++ b/src/gui/text/qglyphrun.cpp
@@ -465,7 +465,80 @@ QRectF QGlyphRun::boundingRect() const
*/
bool QGlyphRun::isEmpty() const
{
- return d->glyphIndexDataSize == 0;
+ return d->glyphIndexDataSize == 0
+ && d->glyphPositionDataSize == 0
+ && d->stringIndexes.isEmpty()
+ && d->sourceString.isEmpty();
+}
+
+/*!
+ \since 6.5
+
+ Returns the string indexes corresponding to each glyph index, if the glyph run has been
+ constructed from a string and string indexes have been requested from the layout. In this case,
+ the length of the returned vector will correspond to the length of glyphIndexes(). In other
+ cases, it will be empty.
+
+ Since a single glyph may correspond to multiple characters in the source string, there may be
+ gaps in the list of string indexes. For instance, if the string "first" is processed by a font
+ which contains a ligature for the character pair "fi", then the five character string will
+ generate a glyph run consisting of only four glyphs. Then the glyph indexes may in this case be
+ (1, 2, 3, 4) (four arbitrary glyph indexes) whereas the string indexes would be (0, 2, 3, 4).
+ The glyphs are in the logical order of the string, thus it is implied that the first glyphs
+ spans characters 0 and 1 in this case.
+
+ Inversely, a single character may also generate multiple glyphs, in which case there will be
+ duplicate entries in the list of string indexes.
+
+ The string indexes correspond to the string, optionally available through sourceString().
+
+ \sa setStringIndexes(), sourceString(), QTextLayout::glyphRuns()
+*/
+QList<qsizetype> QGlyphRun::stringIndexes() const
+{
+ return d->stringIndexes;
+}
+
+/*!
+ \since 6.5
+
+ Sets the list of string indexes corresponding to the glyph indexes to \a stringIndexes
+
+ See stringIndexes() for more details on the conventions of this list.
+
+ \sa sourceString()
+ */
+void QGlyphRun::setStringIndexes(const QList<qsizetype> &stringIndexes)
+{
+ detach();
+ d->stringIndexes = stringIndexes;
+}
+
+/*!
+ \since 6.5
+
+ Returns the string corresponding to the glyph run, if the glyph run has been created from
+ a string and the string has been requested from the layout.
+
+ \sa setSourceString(), stringIndexes(), QTextLayout::glyphRuns()
+ */
+QString QGlyphRun::sourceString() const
+{
+ return d->sourceString;
+}
+
+/*!
+ \since 6.5
+
+ Set the string corresponding to the glyph run to \a sourceString. If set, the indexes returned
+ by stringIndexes() should be indexes into this string.
+
+ \sa sourceString(), stringIndexes()
+ */
+void QGlyphRun::setSourceString(const QString &sourceString)
+{
+ detach();
+ d->sourceString = sourceString;
}
QT_END_NAMESPACE
diff --git a/src/gui/text/qglyphrun.h b/src/gui/text/qglyphrun.h
index 47c41d849d..88f9957dd8 100644
--- a/src/gui/text/qglyphrun.h
+++ b/src/gui/text/qglyphrun.h
@@ -7,8 +7,8 @@
#include <QtGui/qtguiglobal.h>
#include <QtCore/qlist.h>
#include <QtCore/qpoint.h>
-#include <QtCore/qsharedpointer.h>
#include <QtGui/qrawfont.h>
+#include <QtCore/qshareddata.h>
#if !defined(QT_NO_RAWFONT)
@@ -74,6 +74,12 @@ public:
void setBoundingRect(const QRectF &boundingRect);
QRectF boundingRect() const;
+ QList<qsizetype> stringIndexes() const;
+ void setStringIndexes(const QList<qsizetype> &stringIndexes);
+
+ void setSourceString(const QString &sourceString);
+ QString sourceString() const;
+
bool isEmpty() const;
private:
diff --git a/src/gui/text/qglyphrun_p.h b/src/gui/text/qglyphrun_p.h
index 75bc832f77..db160344c6 100644
--- a/src/gui/text/qglyphrun_p.h
+++ b/src/gui/text/qglyphrun_p.h
@@ -15,6 +15,7 @@
// We mean it.
//
+#include <QtCore/qshareddata.h>
#include <QtGui/private/qtguiglobal_p.h>
#include "qglyphrun.h"
#include "qrawfont.h"
@@ -42,8 +43,10 @@ public:
: QSharedData(other)
, glyphIndexes(other.glyphIndexes)
, glyphPositions(other.glyphPositions)
+ , stringIndexes(other.stringIndexes)
, rawFont(other.rawFont)
, boundingRect(other.boundingRect)
+ , sourceString(other.sourceString)
, flags(other.flags)
, glyphIndexData(other.glyphIndexData)
, glyphIndexDataSize(other.glyphIndexDataSize)
@@ -56,8 +59,10 @@ public:
QList<quint32> glyphIndexes;
QList<QPointF> glyphPositions;
+ QList<qsizetype> stringIndexes;
QRawFont rawFont;
QRectF boundingRect;
+ QString sourceString;
QGlyphRun::GlyphRunFlags flags;
diff --git a/src/gui/text/qharfbuzzng.cpp b/src/gui/text/qharfbuzzng.cpp
index 875cf28b99..5db6f78319 100644
--- a/src/gui/text/qharfbuzzng.cpp
+++ b/src/gui/text/qharfbuzzng.cpp
@@ -222,6 +222,14 @@ static const hb_script_t _qtscript_to_hbscript[] = {
HB_SCRIPT_TOTO,
HB_SCRIPT_VITHKUQI,
#endif
+ // Unicode 15.0 additions (not present in harfbuzz-ng 5.1.0 and earlier)
+#if !HB_VERSION_ATLEAST(5, 2, 0)
+ hb_script_t(HB_TAG('K','a','w','i')), // Script_Kawi
+ hb_script_t(HB_TAG('N','a','g','m')), // Script_NagMundari
+#else
+ HB_SCRIPT_KAWI,
+ HB_SCRIPT_NAG_MUNDARI,
+#endif
};
static_assert(QChar::ScriptCount == sizeof(_qtscript_to_hbscript) / sizeof(_qtscript_to_hbscript[0]));
diff --git a/src/gui/text/qinputcontrol.cpp b/src/gui/text/qinputcontrol.cpp
index 87d8c59128..4edd46ff0e 100644
--- a/src/gui/text/qinputcontrol.cpp
+++ b/src/gui/text/qinputcontrol.cpp
@@ -43,7 +43,7 @@ bool QInputControl::isAcceptableInput(const QKeyEvent *event) const
if (c.category() == QChar::Other_PrivateUse)
return true;
- if (c.isHighSurrogate() && text.length() > 1 && text.at(1).isLowSurrogate())
+ if (c.isHighSurrogate() && text.size() > 1 && text.at(1).isLowSurrogate())
return true;
if (m_type == TextEdit && c == u'\t')
diff --git a/src/gui/text/qplatformfontdatabase.cpp b/src/gui/text/qplatformfontdatabase.cpp
index eb54a4e5cb..a146254f68 100644
--- a/src/gui/text/qplatformfontdatabase.cpp
+++ b/src/gui/text/qplatformfontdatabase.cpp
@@ -279,6 +279,17 @@ QFontEngineMulti *QPlatformFontDatabase::fontEngineMulti(QFontEngine *fontEngine
/*!
Returns the font engine that can be used to render the font described by
the font definition, \a fontDef, in the specified \a script.
+
+ This function is called by QFontDatabase both for system fonts provided
+ by the platform font database, as well as for application fonts added by
+ the application developer.
+
+ The handle is the QPlatformFontDatabase specific handle passed when
+ registering the font family via QPlatformFontDatabase::registerFont.
+
+ The function is called for both fonts added via a filename as well
+ as fonts added from QByteArray data. Subclasses will need to handle
+ both cases via its platform specific handle.
*/
QFontEngine *QPlatformFontDatabase::fontEngine(const QFontDef &fontDef, void *handle)
{
@@ -288,6 +299,13 @@ QFontEngine *QPlatformFontDatabase::fontEngine(const QFontDef &fontDef, void *ha
return nullptr;
}
+/*!
+ Returns the font engine that will be used to back a QRawFont,
+ based on the given \fontData, \a pixelSize, and \a hintingPreference.
+
+ This function is called by QRawFont, and does not play a part in
+ the normal operations of QFontDatabase.
+*/
QFontEngine *QPlatformFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize,
QFont::HintingPreference hintingPreference)
{
@@ -600,13 +618,19 @@ void QPlatformFontDatabase::registerAliasToFontFamily(const QString &familyName,
}
/*!
- Inform the Qt font database that the platform's available fonts have changed.
+ Requests that the platform font database should be repopulated.
This will result in invalidating the entire font database.
+ The next time the font database is accessed it will be repopulated
+ via a call to QPlatformFontDatabase::populate().
+
+ Application fonts will not be removed, and will be automatically
+ populated when the font database is repopulated.
+
\since 6.4
*/
-void QPlatformFontDatabase::handleAvailableFontsChanged()
+void QPlatformFontDatabase::repopulateFontDatabase()
{
QFontDatabasePrivate::instance()->invalidate();
}
@@ -622,6 +646,18 @@ bool QPlatformFontDatabase::isFamilyPopulated(const QString &familyName)
}
/*!
+ Returns true if this font database supports loading named instances from variable application
+ fonts.
+
+ \since 6.7
+*/
+bool QPlatformFontDatabase::supportsVariableApplicationFonts() const
+{
+ return false;
+}
+
+
+/*!
\class QPlatformFontDatabase
\since 5.0
\internal
diff --git a/src/gui/text/qplatformfontdatabase.h b/src/gui/text/qplatformfontdatabase.h
index ffb32d701c..3007a11838 100644
--- a/src/gui/text/qplatformfontdatabase.h
+++ b/src/gui/text/qplatformfontdatabase.h
@@ -25,7 +25,7 @@
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcQpaFonts)
+Q_DECLARE_EXPORTED_LOGGING_CATEGORY(lcQpaFonts, Q_GUI_EXPORT)
class QWritingSystemsPrivate;
@@ -72,13 +72,13 @@ public:
virtual void populateFamily(const QString &familyName);
virtual void invalidate();
- virtual QFontEngineMulti *fontEngineMulti(QFontEngine *fontEngine, QChar::Script script);
- virtual QFontEngine *fontEngine(const QFontDef &fontDef, void *handle);
virtual QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const;
virtual QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *font = nullptr);
- virtual void releaseHandle(void *handle);
+ virtual QFontEngine *fontEngine(const QFontDef &fontDef, void *handle);
virtual QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference);
+ virtual QFontEngineMulti *fontEngineMulti(QFontEngine *fontEngine, QChar::Script script);
+ virtual void releaseHandle(void *handle);
virtual QString fontDir() const;
@@ -89,6 +89,8 @@ public:
virtual bool fontsAlwaysScalable() const;
virtual QList<int> standardSizes() const;
+ virtual bool supportsVariableApplicationFonts() const;
+
// helper
static QSupportedWritingSystems writingSystemsFromTrueTypeBits(quint32 unicodeRange[4], quint32 codePageRange[2]);
static QSupportedWritingSystems writingSystemsFromOS2Table(const char *os2Table, size_t length);
@@ -103,7 +105,7 @@ public:
static void registerFontFamily(const QString &familyName);
static void registerAliasToFontFamily(const QString &familyName, const QString &alias);
- static void handleAvailableFontsChanged();
+ static void repopulateFontDatabase();
static bool isFamilyPopulated(const QString &familyName);
};
diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp
index 5b4f5d6b1b..54676b3560 100644
--- a/src/gui/text/qrawfont.cpp
+++ b/src/gui/text/qrawfont.cpp
@@ -440,7 +440,7 @@ qreal QRawFont::underlinePosition() const
*/
QString QRawFont::familyName() const
{
- return d->isValid() ? d->fontEngine->fontDef.families.first() : QString();
+ return d->isValid() ? d->fontEngine->fontDef.families.constFirst() : QString();
}
/*!
@@ -498,7 +498,7 @@ QList<quint32> QRawFont::glyphIndexesForString(const QString &text) const
QGlyphLayout glyphs;
glyphs.numGlyphs = numGlyphs;
glyphs.glyphs = glyphIndexes.data();
- if (!d->fontEngine->stringToCMap(text.data(), text.size(), &glyphs, &numGlyphs, QFontEngine::GlyphIndicesOnly))
+ if (d->fontEngine->stringToCMap(text.data(), text.size(), &glyphs, &numGlyphs, QFontEngine::GlyphIndicesOnly) < 0)
Q_UNREACHABLE();
glyphIndexes.resize(numGlyphs);
@@ -531,7 +531,7 @@ bool QRawFont::glyphIndexesForChars(const QChar *chars, int numChars, quint32 *g
QGlyphLayout glyphs;
glyphs.numGlyphs = *numGlyphs;
glyphs.glyphs = glyphIndexes;
- return d->fontEngine->stringToCMap(chars, numChars, &glyphs, numGlyphs, QFontEngine::GlyphIndicesOnly);
+ return d->fontEngine->stringToCMap(chars, numChars, &glyphs, numGlyphs, QFontEngine::GlyphIndicesOnly) >= 0;
}
/*!
@@ -632,17 +632,33 @@ QFont::HintingPreference QRawFont::hintingPreference() const
}
/*!
- Retrieves the sfnt table named \a tagName from the underlying physical font, or an empty
- byte array if no such table was found. The returned font table's byte order is Big Endian, like
- the sfnt format specifies. The \a tagName must be four characters long and should be formatted
- in the default endianness of the current platform.
+ \fn QByteArray QRawFont::fontTable(const char *tag) const
+ \overload fontTable(QFont::Tag)
+
+ The name must be a four-character string.
+*/
+
+/*!
+ \fn QByteArray QRawFont::fontTable(QFont::Tag tag) const
+ \since 6.7
+
+ Retrieves the sfnt table specified by \a tag from the underlying physical font,
+ or an empty byte array if no such table was found. The returned font table's byte order is
+ Big Endian, like the sfnt format specifies.
*/
-QByteArray QRawFont::fontTable(const char *tagName) const
+QByteArray QRawFont::fontTable(const char *tag) const
+{
+ if (auto maybeTag = QFont::Tag::fromString(tag))
+ return fontTable(*maybeTag);
+ return QByteArray();
+}
+
+QByteArray QRawFont::fontTable(QFont::Tag tag) const
{
if (!d->isValid())
return QByteArray();
- return d->fontEngine->getSfntTable(MAKE_TAG(tagName[0], tagName[1], tagName[2], tagName[3]));
+ return d->fontEngine->getSfntTable(tag.value());
}
/*!
diff --git a/src/gui/text/qrawfont.h b/src/gui/text/qrawfont.h
index 8da39c24f8..d23d0c1493 100644
--- a/src/gui/text/qrawfont.h
+++ b/src/gui/text/qrawfont.h
@@ -105,6 +105,7 @@ public:
QList<QFontDatabase::WritingSystem> supportedWritingSystems() const;
QByteArray fontTable(const char *tagName) const;
+ QByteArray fontTable(QFont::Tag tag) const;
static QRawFont fromFont(const QFont &font,
QFontDatabase::WritingSystem writingSystem = QFontDatabase::Any);
@@ -127,7 +128,7 @@ inline QList<QPointF> QRawFont::advancesForGlyphIndexes(const QList<quint32> &gl
QRawFont::LayoutFlags layoutFlags) const
{
QList<QPointF> advances(glyphIndexes.size());
- if (advancesForGlyphIndexes(glyphIndexes.constData(), advances.data(), glyphIndexes.size(), layoutFlags))
+ if (advancesForGlyphIndexes(glyphIndexes.constData(), advances.data(), int(glyphIndexes.size()), layoutFlags))
return advances;
return QList<QPointF>();
}
diff --git a/src/gui/text/qsyntaxhighlighter.cpp b/src/gui/text/qsyntaxhighlighter.cpp
index c377472439..6c1e2e74fc 100644
--- a/src/gui/text/qsyntaxhighlighter.cpp
+++ b/src/gui/text/qsyntaxhighlighter.cpp
@@ -66,7 +66,7 @@ void QSyntaxHighlighterPrivate::applyFormatChanges()
QList<QTextLayout::FormatRange> ranges = layout->formats();
const int preeditAreaStart = layout->preeditAreaPosition();
- const int preeditAreaLength = layout->preeditAreaText().length();
+ const int preeditAreaLength = layout->preeditAreaText().size();
if (preeditAreaLength != 0) {
auto isOutsidePreeditArea = [=](const QTextLayout::FormatRange &range) {
@@ -81,22 +81,22 @@ void QSyntaxHighlighterPrivate::applyFormatChanges()
}
int i = 0;
- while (i < formatChanges.count()) {
+ while (i < formatChanges.size()) {
QTextLayout::FormatRange r;
- while (i < formatChanges.count() && formatChanges.at(i) == r.format)
+ while (i < formatChanges.size() && formatChanges.at(i) == r.format)
++i;
- if (i == formatChanges.count())
+ if (i == formatChanges.size())
break;
r.start = i;
r.format = formatChanges.at(i);
- while (i < formatChanges.count() && formatChanges.at(i) == r.format)
+ while (i < formatChanges.size() && formatChanges.at(i) == r.format)
++i;
- Q_ASSERT(i <= formatChanges.count());
+ Q_ASSERT(i <= formatChanges.size());
r.length = i - r.start;
if (preeditAreaLength != 0) {
@@ -219,7 +219,7 @@ void QSyntaxHighlighterPrivate::reformatBlock(const QTextBlock &block)
an int value. If no state is set, the returned value is -1. You
can designate any other value to identify any given state using
the setCurrentBlockState() function. Once the state is set the
- QTextBlock keeps that value until it is set set again or until the
+ QTextBlock keeps that value until it is set again or until the
corresponding paragraph of text is deleted.
For example, if you're writing a simple C++ syntax highlighter,
@@ -403,10 +403,10 @@ void QSyntaxHighlighter::rehighlightBlock(const QTextBlock &block)
void QSyntaxHighlighter::setFormat(int start, int count, const QTextCharFormat &format)
{
Q_D(QSyntaxHighlighter);
- if (start < 0 || start >= d->formatChanges.count())
+ if (start < 0 || start >= d->formatChanges.size())
return;
- const int end = qMin(start + count, d->formatChanges.count());
+ const int end = qMin(start + count, d->formatChanges.size());
for (int i = start; i < end; ++i)
d->formatChanges[i] = format;
}
@@ -456,7 +456,7 @@ void QSyntaxHighlighter::setFormat(int start, int count, const QFont &font)
QTextCharFormat QSyntaxHighlighter::format(int pos) const
{
Q_D(const QSyntaxHighlighter);
- if (pos < 0 || pos >= d->formatChanges.count())
+ if (pos < 0 || pos >= d->formatChanges.size())
return QTextCharFormat();
return d->formatChanges.at(pos);
}
diff --git a/src/gui/text/qt_attribution.json b/src/gui/text/qt_attribution.json
index c3a57267e2..f4998da6ea 100644
--- a/src/gui/text/qt_attribution.json
+++ b/src/gui/text/qt_attribution.json
@@ -5,7 +5,7 @@
"QDocModule": "qtgui",
"Description": "Provides standardized names for glyphs.",
"QtUsage": "Used by PDF generator to make it easier for reader applications to resolve the original contents of rendered text.",
- "Path": "qfontsubset_agl.cpp",
+ "Files": "qfontsubset_agl.cpp",
"Homepage": "https://github.com/adobe-type-tools/agl-aglfn",
"Version": "1.7",
diff --git a/src/gui/text/qtextcursor.cpp b/src/gui/text/qtextcursor.cpp
index 884a16ca78..5730f55e6a 100644
--- a/src/gui/text/qtextcursor.cpp
+++ b/src/gui/text/qtextcursor.cpp
@@ -1423,18 +1423,18 @@ void QTextCursor::insertText(const QString &text, const QTextCharFormat &_format
QTextBlockFormat blockFmt = blockFormat();
- int textStart = d->priv->text.length();
+ int textStart = d->priv->text.size();
int blockStart = 0;
d->priv->text += text;
- int textEnd = d->priv->text.length();
+ int textEnd = d->priv->text.size();
- for (int i = 0; i < text.length(); ++i) {
+ for (int i = 0; i < text.size(); ++i) {
QChar ch = text.at(i);
const int blockEnd = i;
if (ch == u'\r'
- && (i + 1) < text.length()
+ && (i + 1) < text.size()
&& text.at(i + 1) == u'\n') {
++i;
ch = text.at(i);
@@ -1679,7 +1679,7 @@ static void getText(QString &text, QTextDocumentPrivate *priv, const QString &do
const int offsetInFragment = qMax(0, pos - fragIt.position());
const int len = qMin(int(frag->size_array[0] - offsetInFragment), end - pos);
- text += QString(docText.constData() + frag->stringPosition + offsetInFragment, len);
+ text += QStringView(docText.constData() + frag->stringPosition + offsetInFragment, len);
pos += len;
}
}
@@ -2300,7 +2300,7 @@ void QTextCursor::insertImage(const QTextImageFormat &format, QTextFrameFormat::
d->priv->beginEditBlock();
d->remove();
const int idx = d->priv->formatCollection()->indexForFormat(fmt);
- d->priv->insert(d->position, QString(QChar(QChar::ObjectReplacementCharacter)), idx);
+ d->priv->insert(d->position, QChar(QChar::ObjectReplacementCharacter), idx);
d->priv->endEditBlock();
}
diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp
index 463026ed23..15a313e13d 100644
--- a/src/gui/text/qtextdocument.cpp
+++ b/src/gui/text/qtextdocument.cpp
@@ -11,6 +11,7 @@
#include "qtexttable.h"
#include "qtextlist.h"
#include <qdebug.h>
+#include <qloggingcategory.h>
#if QT_CONFIG(regularexpression)
#include <qregularexpression.h>
#endif
@@ -42,6 +43,8 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcLayout);
+
using namespace Qt::StringLiterals;
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION unsigned int qt_int_sqrt(unsigned int n);
@@ -51,6 +54,8 @@ namespace {
};
/*!
+ \fn bool Qt::mightBeRichText(QAnyStringView text)
+
Returns \c true if the string \a text is likely to be rich text;
otherwise returns \c false.
@@ -60,21 +65,24 @@ namespace {
for common cases, there is no guarantee.
This function is defined in the \c <QTextDocument> header file.
-*/
-bool Qt::mightBeRichText(const QString& text)
+
+ \note In Qt versions prior to 6.7, this function took QString only.
+ */
+template <typename T>
+static bool mightBeRichTextImpl(T text)
{
if (text.isEmpty())
return false;
- int start = 0;
+ qsizetype start = 0;
- while (start < text.length() && text.at(start).isSpace())
+ while (start < text.size() && QChar(text.at(start)).isSpace())
++start;
// skip a leading <?xml ... ?> as for example with xhtml
- if (QStringView{text}.mid(start, 5).compare("<?xml"_L1) == 0) {
- while (start < text.length()) {
+ if (text.mid(start, 5).compare("<?xml"_L1) == 0) {
+ while (start < text.size()) {
if (text.at(start) == u'?'
- && start + 2 < text.length()
+ && start + 2 < text.size()
&& text.at(start + 1) == u'>') {
start += 2;
break;
@@ -82,35 +90,36 @@ bool Qt::mightBeRichText(const QString& text)
++start;
}
- while (start < text.length() && text.at(start).isSpace())
+ while (start < text.size() && QChar(text.at(start)).isSpace())
++start;
}
- if (QStringView{text}.mid(start, 5).compare("<!doc"_L1, Qt::CaseInsensitive) == 0)
+ if (text.mid(start, 5).compare("<!doc"_L1, Qt::CaseInsensitive) == 0)
return true;
- int open = start;
- while (open < text.length() && text.at(open) != u'<'
+ qsizetype open = start;
+ while (open < text.size() && text.at(open) != u'<'
&& text.at(open) != u'\n') {
- if (text.at(open) == u'&' && QStringView{text}.mid(open + 1, 3) == "lt;"_L1)
+ if (text.at(open) == u'&' && text.mid(open + 1, 3) == "lt;"_L1)
return true; // support desperate attempt of user to see <...>
++open;
}
- if (open < text.length() && text.at(open) == u'<') {
- const int close = text.indexOf(u'>', open);
+ if (open < text.size() && text.at(open) == u'<') {
+ const qsizetype close = text.indexOf(u'>', open);
if (close > -1) {
- QString tag;
- for (int i = open+1; i < close; ++i) {
- if (text[i].isDigit() || text[i].isLetter())
- tag += text[i];
- else if (!tag.isEmpty() && text[i].isSpace())
+ QVarLengthArray<char16_t> tag;
+ for (qsizetype i = open + 1; i < close; ++i) {
+ const auto current = QChar(text[i]);
+ if (current.isDigit() || current.isLetter())
+ tag.append(current.toLower().unicode());
+ else if (!tag.isEmpty() && current.isSpace())
break;
- else if (!tag.isEmpty() && text[i] == u'/' && i + 1 == close)
+ else if (!tag.isEmpty() && current == u'/' && i + 1 == close)
break;
- else if (!text[i].isSpace() && (!tag.isEmpty() || text[i] != u'!'))
+ else if (!current.isSpace() && (!tag.isEmpty() || current != u'!'))
return false; // that's not a tag
}
#ifndef QT_NO_TEXTHTMLPARSER
- return QTextHtmlParser::lookupElement(std::move(tag).toLower()) != -1;
+ return QTextHtmlParser::lookupElement(tag) != -1;
#else
return false;
#endif // QT_NO_TEXTHTMLPARSER
@@ -119,6 +128,16 @@ bool Qt::mightBeRichText(const QString& text)
return false;
}
+static bool mightBeRichTextImpl(QUtf8StringView text)
+{
+ return mightBeRichTextImpl(QLatin1StringView(QByteArrayView(text)));
+}
+
+bool Qt::mightBeRichText(QAnyStringView text)
+{
+ return text.visit([](auto text) { return mightBeRichTextImpl(text); });
+}
+
/*!
Converts the plain text string \a plain to an HTML-formatted
paragraph while preserving most of its look.
@@ -131,13 +150,13 @@ bool Qt::mightBeRichText(const QString& text)
*/
QString Qt::convertFromPlainText(const QString &plain, Qt::WhiteSpaceMode mode)
{
- int col = 0;
+ qsizetype col = 0;
QString rich;
rich += "<p>"_L1;
- for (int i = 0; i < plain.length(); ++i) {
+ for (qsizetype i = 0; i < plain.size(); ++i) {
if (plain[i] == u'\n'){
- int c = 1;
- while (i+1 < plain.length() && plain[i+1] == u'\n') {
+ qsizetype c = 1;
+ while (i+1 < plain.size() && plain[i+1] == u'\n') {
i++;
c++;
}
@@ -235,7 +254,7 @@ QString Qt::convertFromPlainText(const QString &plain, Qt::WhiteSpaceMode mode)
\li Text block group format changes.
\endlist
- \sa QTextCursor, QTextEdit, {Rich Text Processing}, {Text Object Example}
+ \sa QTextCursor, QTextEdit, {Rich Text Processing}
*/
/*!
@@ -711,6 +730,8 @@ void QTextDocument::setTextWidth(qreal width)
{
Q_D(QTextDocument);
QSizeF sz = d->pageSize;
+
+ qCDebug(lcLayout) << "page size" << sz << "-> width" << width;
sz.setWidth(width);
sz.setHeight(-1);
setPageSize(sz);
@@ -1138,6 +1159,8 @@ QString QTextDocument::metaInformation(MetaInformation info) const
return d->url;
case CssMedia:
return d->cssMedia;
+ case FrontMatter:
+ return d->frontMatter;
}
return QString();
}
@@ -1161,6 +1184,9 @@ void QTextDocument::setMetaInformation(MetaInformation info, const QString &stri
case CssMedia:
d->cssMedia = string;
break;
+ case FrontMatter:
+ d->frontMatter = string;
+ break;
}
}
@@ -1200,10 +1226,18 @@ QString QTextDocument::toPlainText() const
Q_D(const QTextDocument);
QString txt = d->plainText();
+ constexpr char16_t delims[] = { 0xfdd0, 0xfdd1,
+ QChar::ParagraphSeparator, QChar::LineSeparator, QChar::Nbsp };
+
+ const size_t pos = std::u16string_view(txt).find_first_of(
+ std::u16string_view(delims, std::size(delims)));
+ if (pos == std::u16string_view::npos)
+ return txt;
+
QChar *uc = txt.data();
- QChar *e = uc + txt.size();
+ QChar *const e = uc + txt.size();
- for (; uc != e; ++uc) {
+ for (uc += pos; uc != e; ++uc) {
switch (uc->unicode()) {
case 0xfdd0: // QTextBeginningOfFrame
case 0xfdd1: // QTextEndOfFrame
@@ -1267,6 +1301,8 @@ void QTextDocument::setHtml(const QString &html)
d->enableUndoRedo(false);
d->beginEditBlock();
d->clear();
+ // ctor calls parse() to build up QTextHtmlParser::nodes list
+ // then import() populates the QTextDocument from those
QTextHtmlImporter(this, html, QTextHtmlImporter::ImportToDocument).import();
d->endEditBlock();
d->enableUndoRedo(previousState);
@@ -1298,6 +1334,10 @@ void QTextDocument::setHtml(const QString &html)
\value CssMedia This value is used to select the corresponding '@media'
rule, if any, from a specified CSS stylesheet when setHtml()
is called. This enum value has been introduced in Qt 6.3.
+ \value FrontMatter This value is used to select header material, if any was
+ extracted during parsing of the source file (currently
+ only from Markdown format). This enum value has been
+ introduced in Qt 6.8.
\sa metaInformation(), setMetaInformation(), setHtml()
*/
@@ -1310,7 +1350,7 @@ static bool findInBlock(const QTextBlock &block, const QString &expression, int
Qt::CaseSensitivity sensitivity = options & QTextDocument::FindCaseSensitively ? Qt::CaseSensitive : Qt::CaseInsensitive;
int idx = -1;
- while (offset >= 0 && offset <= text.length()) {
+ while (offset >= 0 && offset <= text.size()) {
idx = (options & QTextDocument::FindBackward) ?
text.lastIndexOf(expression, offset, sensitivity) : text.indexOf(expression, offset, sensitivity);
if (idx == -1)
@@ -1318,9 +1358,9 @@ static bool findInBlock(const QTextBlock &block, const QString &expression, int
if (options & QTextDocument::FindWholeWords) {
const int start = idx;
- const int end = start + expression.length();
+ const int end = start + expression.size();
if ((start != 0 && text.at(start - 1).isLetterOrNumber())
- || (end != text.length() && text.at(end).isLetterOrNumber())) {
+ || (end != text.size() && text.at(end).isLetterOrNumber())) {
//if this is not a whole word, continue the search in the string
offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
idx = -1;
@@ -1330,7 +1370,7 @@ static bool findInBlock(const QTextBlock &block, const QString &expression, int
//we have a hit, return the cursor for that.
*cursor = QTextCursorPrivate::fromPosition(const_cast<QTextDocumentPrivate *>(QTextDocumentPrivate::get(block)),
block.position() + idx);
- cursor->setPosition(cursor->position() + expression.length(), QTextCursor::KeepAnchor);
+ cursor->setPosition(cursor->position() + expression.size(), QTextCursor::KeepAnchor);
return true;
}
return false;
@@ -1430,7 +1470,7 @@ static bool findInBlock(const QTextBlock &block, const QRegularExpression &expr,
QRegularExpressionMatch match;
int idx = -1;
- while (offset >= 0 && offset <= text.length()) {
+ while (offset >= 0 && offset <= text.size()) {
idx = (options & QTextDocument::FindBackward) ?
text.lastIndexOf(expr, offset, &match) : text.indexOf(expr, offset, &match);
if (idx == -1)
@@ -1440,7 +1480,7 @@ static bool findInBlock(const QTextBlock &block, const QRegularExpression &expr,
const int start = idx;
const int end = start + match.capturedLength();
if ((start != 0 && text.at(start - 1).isLetterOrNumber())
- || (end != text.length() && text.at(end).isLetterOrNumber())) {
+ || (end != text.size() && text.at(end).isLetterOrNumber())) {
//if this is not a whole word, continue the search in the string
offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
idx = -1;
@@ -1471,6 +1511,10 @@ static bool findInBlock(const QTextBlock &block, const QRegularExpression &expr,
If the \a from position is 0 (the default) the search begins from the beginning
of the document; otherwise it begins at the specified position.
+
+ \warning For historical reasons, the case sensitivity option set on
+ \a expr is ignored. Instead, the \a options are used to determine
+ if the search is case sensitive or not.
*/
QTextCursor QTextDocument::find(const QRegularExpression &expr, int from, FindFlags options) const
{
@@ -1826,9 +1870,10 @@ void QTextDocument::setBaselineOffset(qreal baseline)
\fn qreal QTextDocument::baselineOffset() const
\since 6.0
- Returns the the baseline offset in % used in the document layout.
+ Returns the baseline offset in % used in the document layout.
- \sa setBaselineOffset(), setSubScriptBaseline(), subScriptBaseline(), setSuperScriptBaseline(), superScriptBaseline()
+ \sa setBaselineOffset(), setSubScriptBaseline(), subScriptBaseline(), setSuperScriptBaseline(),
+ superScriptBaseline()
*/
qreal QTextDocument::baselineOffset() const
{
@@ -2235,7 +2280,8 @@ QVariant QTextDocument::loadResource(int type, const QUrl &name)
int index = me->indexOfMethod("loadResource(int,QUrl)");
if (index >= 0) {
QMetaMethod loader = me->method(index);
- loader.invoke(p, Q_RETURN_ARG(QVariant, r), Q_ARG(int, type), Q_ARG(QUrl, name));
+ // don't invoke() via a queued connection: this function needs to return a value
+ loader.invoke(p, Qt::DirectConnection, Q_RETURN_ARG(QVariant, r), Q_ARG(int, type), Q_ARG(QUrl, name));
}
}
@@ -2323,7 +2369,7 @@ static QString colorValue(QColor color)
result = color.name();
} else if (color.alpha()) {
QString alphaValue = QString::number(color.alphaF(), 'f', 6);
- while (alphaValue.length() > 1 && alphaValue.at(alphaValue.size() - 1) == u'0')
+ while (alphaValue.size() > 1 && alphaValue.at(alphaValue.size() - 1) == u'0')
alphaValue.chop(1);
if (alphaValue.at(alphaValue.size() - 1) == u'.')
alphaValue.chop(1);
@@ -2364,11 +2410,14 @@ QString QTextHtmlExporter::toHtml(ExportMode mode)
fragmentMarkers = (mode == ExportFragment);
- html += QString::fromLatin1("<meta charset=\"utf-8\" />");
+ html += "<meta charset=\"utf-8\" />"_L1;
QString title = doc->metaInformation(QTextDocument::DocumentTitle);
- if (!title.isEmpty())
- html += QString::fromLatin1("<title>") + title + QString::fromLatin1("</title>");
+ if (!title.isEmpty()) {
+ html += "<title>"_L1;
+ html += title;
+ html += "</title>"_L1;
+ }
html += "<style type=\"text/css\">\n"_L1;
html += "p, li { white-space: pre-wrap; }\n"_L1;
html += "hr { height: 1px; border-width: 0; }\n"_L1;
@@ -2516,7 +2565,9 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
html += u';';
attributesEmitted = true;
}
- } else if (format.hasProperty(QTextFormat::FontPixelSize)) {
+ } else if (format.hasProperty(QTextFormat::FontPixelSize)
+ && format.property(QTextFormat::FontPixelSize)
+ != defaultCharFormat.property(QTextFormat::FontPixelSize)) {
html += " font-size:"_L1;
html += QString::number(format.intProperty(QTextFormat::FontPixelSize));
html += "px;"_L1;
@@ -2595,6 +2646,53 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
html += " -qt-fg-texture-cachekey:"_L1;
html += QString::number(cacheKey);
html += ";"_L1;
+ } else if (brush.style() == Qt::LinearGradientPattern
+ || brush.style() == Qt::RadialGradientPattern
+ || brush.style() == Qt::ConicalGradientPattern) {
+ const QGradient *gradient = brush.gradient();
+ if (gradient->type() == QGradient::LinearGradient) {
+ const QLinearGradient *linearGradient = static_cast<const QLinearGradient *>(brush.gradient());
+
+ html += " -qt-foreground: qlineargradient("_L1;
+ html += "x1:"_L1 + QString::number(linearGradient->start().x()) + u',';
+ html += "y1:"_L1 + QString::number(linearGradient->start().y()) + u',';
+ html += "x2:"_L1 + QString::number(linearGradient->finalStop().x()) + u',';
+ html += "y2:"_L1 + QString::number(linearGradient->finalStop().y()) + u',';
+ } else if (gradient->type() == QGradient::RadialGradient) {
+ const QRadialGradient *radialGradient = static_cast<const QRadialGradient *>(brush.gradient());
+
+ html += " -qt-foreground: qradialgradient("_L1;
+ html += "cx:"_L1 + QString::number(radialGradient->center().x()) + u',';
+ html += "cy:"_L1 + QString::number(radialGradient->center().y()) + u',';
+ html += "fx:"_L1 + QString::number(radialGradient->focalPoint().x()) + u',';
+ html += "fy:"_L1 + QString::number(radialGradient->focalPoint().y()) + u',';
+ html += "radius:"_L1 + QString::number(radialGradient->radius()) + u',';
+ } else {
+ const QConicalGradient *conicalGradient = static_cast<const QConicalGradient *>(brush.gradient());
+
+ html += " -qt-foreground: qconicalgradient("_L1;
+ html += "cx:"_L1 + QString::number(conicalGradient->center().x()) + u',';
+ html += "cy:"_L1 + QString::number(conicalGradient->center().y()) + u',';
+ html += "angle:"_L1 + QString::number(conicalGradient->angle()) + u',';
+ }
+
+ const QStringList coordinateModes = { "logical"_L1, "stretchtodevice"_L1, "objectbounding"_L1, "object"_L1 };
+ html += "coordinatemode:"_L1;
+ html += coordinateModes.at(int(gradient->coordinateMode()));
+ html += u',';
+
+ const QStringList spreads = { "pad"_L1, "reflect"_L1, "repeat"_L1 };
+ html += "spread:"_L1;
+ html += spreads.at(int(gradient->spread()));
+
+ for (const QGradientStop &stop : gradient->stops()) {
+ html += ",stop:"_L1;
+ html += QString::number(stop.first);
+ html += u' ';
+ html += colorValue(stop.second);
+ }
+
+ html += ");"_L1;
} else {
html += " color:"_L1;
html += colorValue(brush.color());
@@ -2650,6 +2748,18 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
attributesEmitted = true;
}
+ if (format.hasProperty(QTextFormat::TextOutline)) {
+ QPen outlinePen = format.textOutline();
+ html += " -qt-stroke-color:"_L1;
+ html += colorValue(outlinePen.color());
+ html += u';';
+
+ html += " -qt-stroke-width:"_L1;
+ html += QString::number(outlinePen.widthF());
+ html += "px;"_L1;
+ attributesEmitted = true;
+ }
+
return attributesEmitted;
}
@@ -2829,7 +2939,7 @@ void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
html.chop(styleTag.size());
if (isObject) {
- for (int i = 0; isImage && i < txt.length(); ++i) {
+ for (int i = 0; isImage && i < txt.size(); ++i) {
QTextImageFormat imgFmt = format.toImageFormat();
html += "<img"_L1;
@@ -2994,19 +3104,27 @@ void QTextHtmlExporter::emitBlock(const QTextBlock &block)
if (list->itemNumber(block) == 0) { // first item? emit <ul> or appropriate
const QTextListFormat format = list->format();
const int style = format.style();
+ bool ordered = false;
switch (style) {
- case QTextListFormat::ListDecimal: html += "<ol"_L1; break;
case QTextListFormat::ListDisc: html += "<ul"_L1; break;
case QTextListFormat::ListCircle: html += "<ul type=\"circle\""_L1; break;
case QTextListFormat::ListSquare: html += "<ul type=\"square\""_L1; break;
- case QTextListFormat::ListLowerAlpha: html += "<ol type=\"a\""_L1; break;
- case QTextListFormat::ListUpperAlpha: html += "<ol type=\"A\""_L1; break;
- case QTextListFormat::ListLowerRoman: html += "<ol type=\"i\""_L1; break;
- case QTextListFormat::ListUpperRoman: html += "<ol type=\"I\""_L1; break;
+ case QTextListFormat::ListDecimal: html += "<ol"_L1; ordered = true; break;
+ case QTextListFormat::ListLowerAlpha: html += "<ol type=\"a\""_L1; ordered = true; break;
+ case QTextListFormat::ListUpperAlpha: html += "<ol type=\"A\""_L1; ordered = true; break;
+ case QTextListFormat::ListLowerRoman: html += "<ol type=\"i\""_L1; ordered = true; break;
+ case QTextListFormat::ListUpperRoman: html += "<ol type=\"I\""_L1; ordered = true; break;
default: html += "<ul"_L1; // ### should not happen
}
- QString styleString = QString::fromLatin1("margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;");
+ if (ordered && format.start() != 1) {
+ html += " start=\""_L1;
+ html += QString::number(format.start());
+ html += u'"';
+ }
+
+ QString styleString;
+ styleString += "margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;"_L1;
if (format.hasProperty(QTextFormat::ListIndent)) {
styleString += " -qt-list-indent: "_L1;
@@ -3259,7 +3377,7 @@ void QTextHtmlExporter::emitTable(const QTextTable *table)
columnWidths.resize(columns);
columnWidths.fill(QTextLength());
}
- Q_ASSERT(columnWidths.count() == columns);
+ Q_ASSERT(columnWidths.size() == columns);
QVarLengthArray<bool> widthEmittedForColumn(columns);
for (int i = 0; i < columns; ++i)
@@ -3436,7 +3554,7 @@ void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType
{
const auto styleAttribute = " style=\""_L1;
html += styleAttribute;
- const int originalHtmlLength = html.length();
+ const qsizetype originalHtmlLength = html.size();
if (frameType == TextFrame)
html += "-qt-table-type: frame;"_L1;
@@ -3470,7 +3588,7 @@ void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType
if (format.property(QTextFormat::TableBorderCollapse).toBool())
html += " border-collapse:collapse;"_L1;
- if (html.length() == originalHtmlLength) // nothing emitted?
+ if (html.size() == originalHtmlLength) // nothing emitted?
html.chop(styleAttribute.size());
else
html += u'\"';
@@ -3545,7 +3663,7 @@ QString QTextDocument::toMarkdown(QTextDocument::MarkdownFeatures features) cons
#if QT_CONFIG(textmarkdownreader)
void QTextDocument::setMarkdown(const QString &markdown, QTextDocument::MarkdownFeatures features)
{
- QTextMarkdownImporter(features).import(this, markdown);
+ QTextMarkdownImporter(this, features).import(markdown);
}
#endif
diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h
index 01ed9789ed..b6253bfa46 100644
--- a/src/gui/text/qtextdocument.h
+++ b/src/gui/text/qtextdocument.h
@@ -35,7 +35,10 @@ class QTextCursor;
namespace Qt
{
+#if QT_GUI_REMOVED_SINCE(6, 7)
Q_GUI_EXPORT bool mightBeRichText(const QString&);
+#endif
+ Q_GUI_EXPORT bool mightBeRichText(QAnyStringView);
Q_GUI_EXPORT QString convertFromPlainText(const QString &plain, WhiteSpaceMode mode = WhiteSpacePre);
}
@@ -102,7 +105,8 @@ public:
enum MetaInformation {
DocumentTitle,
DocumentUrl,
- CssMedia
+ CssMedia,
+ FrontMatter,
};
void setMetaInformation(MetaInformation info, const QString &);
QString metaInformation(MetaInformation info) const;
@@ -116,7 +120,7 @@ public:
enum MarkdownFeature {
MarkdownNoHTML = 0x0020 | 0x0040,
MarkdownDialectCommonMark = 0,
- MarkdownDialectGitHub = 0x0004 | 0x0008 | 0x0400 | 0x0100 | 0x0200 | 0x0800 | 0x4000
+ MarkdownDialectGitHub = 0x0004 | 0x0008 | 0x0400 | 0x0100 | 0x0200 | 0x0800 | 0x4000 | 0x100000
};
Q_DECLARE_FLAGS(MarkdownFeatures, MarkdownFeature)
Q_FLAG(MarkdownFeatures)
diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp
index e61194ab1e..3c1fc04d4b 100644
--- a/src/gui/text/qtextdocument_p.cpp
+++ b/src/gui/text/qtextdocument_p.cpp
@@ -202,7 +202,7 @@ void QTextDocumentPrivate::clear()
{
Q_Q(QTextDocument);
- for (QTextCursorPrivate *curs : qAsConst(cursors)) {
+ for (QTextCursorPrivate *curs : std::as_const(cursors)) {
curs->setPosition(0);
curs->currentCharFormat = -1;
curs->anchor = 0;
@@ -255,7 +255,7 @@ void QTextDocumentPrivate::clear()
QTextDocumentPrivate::~QTextDocumentPrivate()
{
- for (QTextCursorPrivate *curs : qAsConst(cursors))
+ for (QTextCursorPrivate *curs : std::as_const(cursors))
curs->priv = nullptr;
cursors.clear();
undoState = 0;
@@ -349,8 +349,10 @@ int QTextDocumentPrivate::insert_block(int pos, uint strPos, int format, int blo
QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blockFormat));
if (group) {
group->blockInserted(QTextBlock(this, b));
- docChangeOldLength--;
- docChangeLength--;
+ if (command != QTextUndoCommand::BlockDeleted) {
+ docChangeOldLength--;
+ docChangeLength--;
+ }
}
QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(formats.format(format)));
@@ -373,7 +375,7 @@ int QTextDocumentPrivate::insertBlock(QChar blockSeparator,
beginEditBlock();
- int strPos = text.length();
+ int strPos = text.size();
text.append(blockSeparator);
int ob = blocks.findNode(pos);
@@ -443,16 +445,16 @@ void QTextDocumentPrivate::insert(int pos, int strPos, int strLength, int format
finishEdit();
}
-void QTextDocumentPrivate::insert(int pos, const QString &str, int format)
+void QTextDocumentPrivate::insert(int pos, QStringView str, int format)
{
if (str.size() == 0)
return;
Q_ASSERT(noBlockInString(str));
- int strPos = text.length();
+ int strPos = text.size();
text.append(str);
- insert(pos, strPos, str.length(), format);
+ insert(pos, strPos, str.size(), format);
}
int QTextDocumentPrivate::remove_string(int pos, uint length, QTextUndoCommand::Operation op)
@@ -646,7 +648,7 @@ void QTextDocumentPrivate::remove(int pos, int length, QTextUndoCommand::Operati
blockCursorAdjustment = true;
move(pos, -1, length, op);
blockCursorAdjustment = false;
- for (QTextCursorPrivate *curs : qAsConst(cursors)) {
+ for (QTextCursorPrivate *curs : std::as_const(cursors)) {
if (curs->adjustPosition(pos, -length, op) == QTextCursorPrivate::CursorMoved) {
curs->changed = true;
}
@@ -1204,13 +1206,13 @@ void QTextDocumentPrivate::finishEdit()
}
QList<QTextCursor> changedCursors;
- for (QTextCursorPrivate *curs : qAsConst(cursors)) {
+ for (QTextCursorPrivate *curs : std::as_const(cursors)) {
if (curs->changed) {
curs->changed = false;
changedCursors.append(QTextCursor(curs));
}
}
- for (const QTextCursor &cursor : qAsConst(changedCursors))
+ for (const QTextCursor &cursor : std::as_const(changedCursors))
emit q->cursorPositionChanged(cursor);
contentsChanged();
@@ -1256,7 +1258,7 @@ void QTextDocumentPrivate::adjustDocumentChangesAndCursors(int from, int addedOr
if (blockCursorAdjustment) {
; // postpone, will be called again from QTextDocumentPrivate::remove()
} else {
- for (QTextCursorPrivate *curs : qAsConst(cursors)) {
+ for (QTextCursorPrivate *curs : std::as_const(cursors)) {
if (curs->adjustPosition(from, addedOrRemoved, op) == QTextCursorPrivate::CursorMoved) {
curs->changed = true;
}
@@ -1419,6 +1421,16 @@ QTextFrame *QTextDocumentPrivate::rootFrame() const
return rtFrame;
}
+void QTextDocumentPrivate::addCursor(QTextCursorPrivate *c)
+{
+ cursors.insert(c);
+}
+
+void QTextDocumentPrivate::removeCursor(QTextCursorPrivate *c)
+{
+ cursors.remove(c);
+}
+
QTextFrame *QTextDocumentPrivate::frameAt(int pos) const
{
QTextFrame *f = rootFrame();
@@ -1433,7 +1445,7 @@ QTextFrame *QTextDocumentPrivate::frameAt(int pos) const
void QTextDocumentPrivate::clearFrame(QTextFrame *f)
{
- for (int i = 0; i < f->d_func()->childFrames.count(); ++i)
+ for (int i = 0; i < f->d_func()->childFrames.size(); ++i)
clearFrame(f->d_func()->childFrames.at(i));
f->d_func()->childFrames.clear();
f->d_func()->parentFrame = nullptr;
@@ -1703,7 +1715,7 @@ bool QTextDocumentPrivate::ensureMaximumBlockCount()
void QTextDocumentPrivate::aboutToRemoveCell(int from, int to)
{
Q_ASSERT(from <= to);
- for (QTextCursorPrivate *curs : qAsConst(cursors))
+ for (QTextCursorPrivate *curs : std::as_const(cursors))
curs->aboutToRemoveCell(from, to);
}
diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h
index effcfcabed..1c4edc4329 100644
--- a/src/gui/text/qtextdocument_p.h
+++ b/src/gui/text/qtextdocument_p.h
@@ -29,7 +29,9 @@
#include "QtCore/qurl.h"
#include "QtCore/qvariant.h"
+#if QT_CONFIG(cssparser)
#include "private/qcssparser_p.h"
+#endif
#include "private/qfragmentmap_p.h"
#include "private/qobject_p.h"
#include "private/qtextformat_p.h"
@@ -140,7 +142,9 @@ public:
void setLayout(QAbstractTextDocumentLayout *layout);
- void insert(int pos, const QString &text, int format);
+ void insert(int pos, QStringView text, int format);
+ void insert(int pos, QChar c, int format)
+ { insert(pos, QStringView(&c, 1), format); }
void insert(int pos, int strPos, int strLength, int format);
int insertBlock(int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation = QTextUndoCommand::MoveCursor);
int insertBlock(QChar blockSeparator, int pos, int blockFormat, int charFormat,
@@ -243,8 +247,8 @@ private:
public:
void documentChange(int from, int length);
- inline void addCursor(QTextCursorPrivate *c) { cursors.insert(c); }
- inline void removeCursor(QTextCursorPrivate *c) { cursors.remove(c); }
+ void addCursor(QTextCursorPrivate *c);
+ void removeCursor(QTextCursorPrivate *c);
QTextFrame *frameAt(int pos) const;
QTextFrame *rootFrame() const;
@@ -352,6 +356,7 @@ public:
QString title;
QString url;
QString cssMedia;
+ QString frontMatter;
qreal indentWidth;
qreal documentMargin;
QUrl baseUrl;
diff --git a/src/gui/text/qtextdocumentfragment.cpp b/src/gui/text/qtextdocumentfragment.cpp
index 0118c34c97..1b6e76c201 100644
--- a/src/gui/text/qtextdocumentfragment.cpp
+++ b/src/gui/text/qtextdocumentfragment.cpp
@@ -84,7 +84,7 @@ int QTextCopyHelper::appendFragment(int pos, int endPos, int objectIndex)
}
QString txtToInsert(originalText.constData() + frag->stringPosition + inFragmentOffset, charsToCopy);
- if (txtToInsert.length() == 1
+ if (txtToInsert.size() == 1
&& (txtToInsert.at(0) == QChar::ParagraphSeparator
|| txtToInsert.at(0) == QTextBeginningOfFrame
|| txtToInsert.at(0) == QTextEndOfFrame
@@ -109,7 +109,7 @@ int QTextCopyHelper::appendFragment(int pos, int endPos, int objectIndex)
const int userState = nextBlock.userState();
if (userState != -1)
dst->blocksFind(insertPos).setUserState(userState);
- insertPos += txtToInsert.length();
+ insertPos += txtToInsert.size();
}
return charsToCopy;
@@ -488,7 +488,8 @@ void QTextHtmlImporter::import()
* means there was a tag closing in the input html
*/
if (currentNodeIdx > 0 && (currentNode->parent != currentNodeIdx - 1)) {
- blockTagClosed = closeTag();
+ const bool lastBlockTagClosed = closeTag();
+ blockTagClosed = blockTagClosed || lastBlockTagClosed;
// visually collapse subsequent block tags, but if the element after the closed block tag
// is for example an inline element (!isBlock) we have to make sure we start a new paragraph by setting
// hasBlock to false.
@@ -540,6 +541,7 @@ void QTextHtmlImporter::import()
appendBlock(block, currentNode->charFormat);
+ blockTagClosed = false;
hasBlock = true;
}
@@ -575,14 +577,12 @@ bool QTextHtmlImporter::appendNodeText()
if (wsm == QTextHtmlParserNode::WhiteSpacePre || wsm == QTextHtmlParserNode::WhiteSpacePreWrap)
compressNextWhitespace = PreserveWhiteSpace;
- QString text = currentNode->text;
+ const QString text = currentNode->text;
QString textToInsert;
textToInsert.reserve(text.size());
- for (int i = 0; i < text.length(); ++i) {
- QChar ch = text.at(i);
-
+ for (QChar ch : text) {
if (ch.isSpace()
&& ch != QChar::Nbsp
&& ch != QChar::ParagraphSeparator) {
@@ -621,7 +621,7 @@ bool QTextHtmlImporter::appendNodeText()
|| ch == QChar::ParagraphSeparator) {
if (!textToInsert.isEmpty()) {
- if (wsm == QTextHtmlParserNode::WhiteSpacePreLine && textToInsert.at(textToInsert.length() - 1) == u' ')
+ if (wsm == QTextHtmlParserNode::WhiteSpacePreLine && textToInsert.at(textToInsert.size() - 1) == u' ')
textToInsert = textToInsert.chopped(1);
cursor.insertText(textToInsert, format);
textToInsert.clear();
@@ -699,6 +699,8 @@ QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processSpecialNodes()
listFmt.setNumberPrefix(currentNode->textListNumberPrefix);
if (!currentNode->textListNumberSuffix.isNull())
listFmt.setNumberSuffix(currentNode->textListNumberSuffix);
+ if (currentNode->listStart != 1)
+ listFmt.setStart(currentNode->listStart);
++indent;
if (currentNode->hasCssListIndent)
@@ -906,7 +908,7 @@ QTextHtmlImporter::Table QTextHtmlImporter::scanTable(int tableNodeIdx)
int tableHeaderRowCount = 0;
QList<int> rowNodes;
- rowNodes.reserve(at(tableNodeIdx).children.count());
+ rowNodes.reserve(at(tableNodeIdx).children.size());
for (int row : at(tableNodeIdx).children) {
switch (at(row).id) {
case Html_tr:
@@ -931,7 +933,7 @@ QTextHtmlImporter::Table QTextHtmlImporter::scanTable(int tableNodeIdx)
QList<RowColSpanInfo> rowColSpanForColumn;
int effectiveRow = 0;
- for (int row : qAsConst(rowNodes)) {
+ for (int row : std::as_const(rowNodes)) {
int colsInRow = 0;
for (int cell : at(row).children) {
@@ -959,7 +961,7 @@ QTextHtmlImporter::Table QTextHtmlImporter::scanTable(int tableNodeIdx)
if (spanInfo.colSpan > 1 || spanInfo.rowSpan > 1)
rowColSpans.append(spanInfo);
- columnWidths.resize(qMax(columnWidths.count(), colsInRow));
+ columnWidths.resize(qMax(columnWidths.size(), colsInRow));
rowColSpanForColumn.resize(columnWidths.size());
for (int i = currentColumn; i < currentColumn + c.tableCellColSpan; ++i) {
if (columnWidths.at(i).type() == QTextLength::VariableLength) {
@@ -1041,7 +1043,7 @@ QTextHtmlImporter::Table QTextHtmlImporter::scanTable(int tableNodeIdx)
QTextTable *textTable = cursor.insertTable(table.rows, table.columns, fmt.toTableFormat());
table.frame = textTable;
- for (int i = 0; i < rowColSpans.count(); ++i) {
+ for (int i = 0; i < rowColSpans.size(); ++i) {
const RowColSpanInfo &nfo = rowColSpans.at(i);
textTable->mergeCells(nfo.row, nfo.col, nfo.rowSpan, nfo.colSpan);
}
@@ -1311,8 +1313,7 @@ QTextDocumentFragment QTextDocumentFragment::fromMarkdown(const QString &markdow
QTextDocumentFragment res;
res.d = new QTextDocumentFragmentPrivate;
- QTextMarkdownImporter importer(features);
- importer.import(res.d->doc, markdown);
+ QTextMarkdownImporter(res.d->doc, features).import(markdown);
return res;
}
diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp
index 98e4419e40..452f814231 100644
--- a/src/gui/text/qtextdocumentlayout.cpp
+++ b/src/gui/text/qtextdocumentlayout.cpp
@@ -7,7 +7,9 @@
#include "qtexttable.h"
#include "qtextlist.h"
#include "qtextengine_p.h"
+#if QT_CONFIG(cssparser)
#include "private/qcssutil_p.h"
+#endif
#include "private/qguiapplication_p.h"
#include "qabstracttextdocumentlayout_p.h"
@@ -23,6 +25,7 @@
#include <qbasictimer.h>
#include "private/qfunctions_p.h"
#include <qloggingcategory.h>
+#include <QtCore/qpointer.h>
#include <algorithm>
@@ -545,7 +548,7 @@ public:
QTextLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, const QTextBlockFormat *previousBlockFormat);
void layoutFlow(QTextFrame::Iterator it, QTextLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, QFixed width = 0);
- void floatMargins(const QFixed &y, const QTextLayoutStruct *layoutStruct, QFixed *left, QFixed *right) const;
+ void floatMargins(QFixed y, const QTextLayoutStruct *layoutStruct, QFixed *left, QFixed *right) const;
QFixed findY(QFixed yFrom, const QTextLayoutStruct *layoutStruct, QFixed requiredWidth) const;
QList<QCheckPoint> checkPoints;
@@ -794,7 +797,7 @@ QTextDocumentLayoutPrivate::hitTest(const QTextBlock &bl, const QFixedPoint &poi
textrect.translate(tl->position());
qCDebug(lcHit) << " checking block" << bl.position() << "point=" << point.toPointF() << " tlrect" << textrect;
*position = bl.position();
- if (point.y.toReal() < textrect.top()) {
+ if (point.y.toReal() < textrect.top() - bl.blockFormat().topMargin()) {
qCDebug(lcHit) << " before pos=" << *position;
return PointBefore;
} else if (point.y.toReal() > textrect.bottom()) {
@@ -1175,7 +1178,7 @@ void QTextDocumentLayoutPrivate::drawFrame(const QPointF &offset, QPainter *pain
it = frameIteratorForYPosition(QFixed::fromReal(context.clip.top()));
QList<QTextFrame *> floats;
- const int numFloats = fd->floats.count();
+ const int numFloats = fd->floats.size();
floats.reserve(numFloats);
for (int i = 0; i < numFloats; ++i)
floats.append(fd->floats.at(i));
@@ -1209,8 +1212,7 @@ static inline QTextFormat::Property borderPropertyForEdge(QCss::Edge edge)
case QCss::RightEdge:
return QTextFormat::TableCellRightBorder;
default:
- Q_UNREACHABLE();
- return QTextFormat::UserProperty;
+ Q_UNREACHABLE_RETURN(QTextFormat::UserProperty);
}
}
@@ -1226,8 +1228,7 @@ static inline QTextFormat::Property borderStylePropertyForEdge(QCss::Edge edge)
case QCss::RightEdge:
return QTextFormat::TableCellRightBorderStyle;
default:
- Q_UNREACHABLE();
- return QTextFormat::UserProperty;
+ Q_UNREACHABLE_RETURN(QTextFormat::UserProperty);
}
}
@@ -1243,8 +1244,7 @@ static inline QCss::Edge adjacentEdge(QCss::Edge edge)
case QCss::LeftEdge:
return QCss::RightEdge;
default:
- Q_UNREACHABLE();
- return QCss::NumEdges;
+ Q_UNREACHABLE_RETURN(QCss::NumEdges);
}
}
@@ -1323,8 +1323,7 @@ static inline bool sharesAxis(const QTextTableCell &cell, QCss::Edge edge,
return cell.column() + cell.columnSpan() ==
competingCell.column() + (competingCellEdge == QCss::LeftEdge ? 0 : competingCell.columnSpan());
default:
- Q_UNREACHABLE();
- return false;
+ Q_UNREACHABLE_RETURN(false);
}
}
@@ -1819,11 +1818,20 @@ void QTextDocumentLayoutPrivate::drawTableCell(const QRectF &cellRect, QPainter
if (r >= headerRowCount)
topMargin += td->headerHeight.toReal();
- if (!td->borderCollapse && td->border != 0) {
+ // If cell border configured, don't draw default border for cells. It will be taken care later by
+ // drawTableCellBorder().
+ bool cellBorderConfigured = (cell.format().hasProperty(QTextFormat::TableCellLeftBorder) ||
+ cell.format().hasProperty(QTextFormat::TableCellTopBorder) ||
+ cell.format().hasProperty(QTextFormat::TableCellRightBorder) ||
+ cell.format().hasProperty(QTextFormat::TableCellBottomBorder));
+
+ if (!td->borderCollapse && td->border != 0 && !cellBorderConfigured) {
const QBrush oldBrush = painter->brush();
const QPen oldPen = painter->pen();
- const qreal border = td->border.toReal();
+ // If border is configured for the table (and not explicitly for the cell), then
+ // always draw 1px border around the cell
+ const qreal border = 1;
QRectF borderRect(cellRect.left() - border, cellRect.top() - border, cellRect.width() + border, cellRect.height() + border);
@@ -1886,7 +1894,8 @@ void QTextDocumentLayoutPrivate::drawTableCell(const QRectF &cellRect, QPainter
}
// paint over the background - otherwise we would have to adjust the background paint cellRect for the border values
- drawTableCellBorder(cellRect, painter, table, td, cell);
+ if (cellBorderConfigured)
+ drawTableCellBorder(cellRect, painter, table, td, cell);
const QFixed verticalOffset = td->cellVerticalOffsets.at(c + r * table->columns());
@@ -1967,7 +1976,7 @@ void QTextDocumentLayoutPrivate::drawFlow(const QPointF &offset, QPainter *paint
previousFrame = c;
}
- for (int i = 0; i < floats.count(); ++i) {
+ for (int i = 0; i < floats.size(); ++i) {
QTextFrame *frame = floats.at(i);
if (!isFrameFromInlineObject(frame)
|| frame->frameFormat().position() == QTextFrameFormat::InFlow)
@@ -2072,7 +2081,7 @@ void QTextDocumentLayoutPrivate::drawBlock(const QPointF &offset, QPainter *pain
const qreal width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth).value(r.width());
const auto color = blockFormat.hasProperty(QTextFormat::BackgroundBrush)
? qvariant_cast<QBrush>(blockFormat.property(QTextFormat::BackgroundBrush)).color()
- : context.palette.color(QPalette::Dark);
+ : context.palette.color(QPalette::Inactive, QPalette::WindowText);
painter->setPen(color);
qreal y = r.bottom();
if (bl.length() == 1)
@@ -2207,17 +2216,15 @@ void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *p
}
case QTextListFormat::ListSquare:
if (!marker)
- painter->fillRect(r, brush);
+ painter->fillRect(r, painter->pen().brush());
break;
case QTextListFormat::ListCircle:
- if (!marker) {
- painter->setPen(QPen(brush, 0));
+ if (!marker)
painter->drawEllipse(r.translated(0.5, 0.5)); // pixel align for sharper rendering
- }
break;
case QTextListFormat::ListDisc:
if (!marker) {
- painter->setBrush(brush);
+ painter->setBrush(painter->pen().brush());
painter->setPen(Qt::NoPen);
painter->drawEllipse(r);
}
@@ -2368,7 +2375,7 @@ QRectF QTextDocumentLayoutPrivate::layoutTable(QTextTable *table, int layoutFrom
td->childFrameMap.clear();
{
const QList<QTextFrame *> children = table->childFrames();
- for (int i = 0; i < children.count(); ++i) {
+ for (int i = 0; i < children.size(); ++i) {
QTextFrame *frame = children.at(i);
QTextTableCell cell = table->cellAt(frame->firstPosition());
td->childFrameMap.insert(cell.row() + cell.column() * rows, frame);
@@ -2378,7 +2385,7 @@ QRectF QTextDocumentLayoutPrivate::layoutTable(QTextTable *table, int layoutFrom
QList<QTextLength> columnWidthConstraints = fmt.columnWidthConstraints();
if (columnWidthConstraints.size() != columns)
columnWidthConstraints.resize(columns);
- Q_ASSERT(columnWidthConstraints.count() == columns);
+ Q_ASSERT(columnWidthConstraints.size() == columns);
// borderCollapse will disable drawing the html4 style table cell borders
// and draw a 1px grid instead. This also sets a fixed cellspacing
@@ -2553,8 +2560,9 @@ recalc_minmax_widths:
const QFixed allottedPercentage = QFixed::fromReal(columnWidthConstraints.at(i).rawValue());
const QFixed percentWidth = totalPercentagedWidth * allottedPercentage / totalPercentage;
- if (percentWidth >= td->minWidths.at(i)) {
- td->widths[i] = qBound(td->minWidths.at(i), percentWidth, remainingWidth - remainingMinWidths);
+ QFixed maxWidth = remainingWidth - remainingMinWidths;
+ if (percentWidth >= td->minWidths.at(i) && maxWidth > td->minWidths.at(i)) {
+ td->widths[i] = qBound(td->minWidths.at(i), percentWidth, maxWidth);
} else {
td->widths[i] = td->minWidths.at(i);
}
@@ -2575,9 +2583,9 @@ recalc_minmax_widths:
QFixed lastRemainingWidth = remainingWidth;
while (remainingWidth > 0) {
- for (int k = 0; k < columnsWithProperMaxSize.count(); ++k) {
+ for (int k = 0; k < columnsWithProperMaxSize.size(); ++k) {
const int col = columnsWithProperMaxSize[k];
- const int colsLeft = columnsWithProperMaxSize.count() - k;
+ const int colsLeft = columnsWithProperMaxSize.size() - k;
const QFixed w = qMin(td->maxWidths.at(col) - td->widths.at(col), remainingWidth / colsLeft);
td->widths[col] += w;
remainingWidth -= w;
@@ -3112,7 +3120,7 @@ void QTextDocumentLayoutPrivate::layoutFlow(QTextFrame::Iterator it, QTextLayout
QTextBlockFormat previousBlockFormat = previousIt.currentBlock().blockFormat();
QFixed maximumBlockWidth = 0;
- while (!it.atEnd()) {
+ while (!it.atEnd() && layoutStruct->absoluteY() < QFIXED_MAX) {
QTextFrame *c = it.currentFrame();
int docPos;
@@ -3349,7 +3357,7 @@ void QTextDocumentLayoutPrivate::layoutFlow(QTextFrame::Iterator it, QTextLayout
// and not per cell and layoutCell already takes care of doing the same as we do here
if (!qobject_cast<QTextTable *>(layoutStruct->frame)) {
QList<QTextFrame *> children = layoutStruct->frame->childFrames();
- for (int i = 0; i < children.count(); ++i) {
+ for (int i = 0; i < children.size(); ++i) {
QTextFrameData *fd = data(children.at(i));
if (!fd->layoutDirty && children.at(i)->frameFormat().position() != QTextFrameFormat::InFlow)
layoutStruct->y = qMax(layoutStruct->y, fd->position.y + fd->size.height);
@@ -3362,7 +3370,7 @@ void QTextDocumentLayoutPrivate::layoutFlow(QTextFrame::Iterator it, QTextLayout
if (!fd->floats.isEmpty())
contentHasAlignment = true;
- if (it.atEnd()) {
+ if (it.atEnd() || layoutStruct->absoluteY() >= QFIXED_MAX) {
//qDebug("layout done!");
currentLazyLayoutPosition = -1;
QCheckPoint cp;
@@ -3548,6 +3556,11 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi
while (layoutStruct->pageHeight > 0 && layoutStruct->absoluteY() + lineBreakHeight > layoutStruct->pageBottom &&
layoutStruct->contentHeight() >= lineBreakHeight) {
+ if (layoutStruct->pageHeight == QFIXED_MAX) {
+ layoutStruct->y = QFIXED_MAX - layoutStruct->frameY;
+ break;
+ }
+
layoutStruct->newPage();
floatMargins(layoutStruct->y, layoutStruct, &left, &right);
@@ -3631,7 +3644,7 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi
}
}
-void QTextDocumentLayoutPrivate::floatMargins(const QFixed &y, const QTextLayoutStruct *layoutStruct,
+void QTextDocumentLayoutPrivate::floatMargins(QFixed y, const QTextLayoutStruct *layoutStruct,
QFixed *left, QFixed *right) const
{
// qDebug() << "floatMargins y=" << y;
@@ -3850,7 +3863,7 @@ int QTextDocumentLayout::hitTest(const QPointF &point, Qt::HitTestAccuracy accur
// ensure we stay within document bounds
int lastPos = f->lastPosition();
if (l && !l->preeditAreaText().isEmpty())
- lastPos += l->preeditAreaText().length();
+ lastPos += l->preeditAreaText().size();
if (position > lastPos)
position = lastPos;
else if (position < 0)
diff --git a/src/gui/text/qtextdocumentwriter.cpp b/src/gui/text/qtextdocumentwriter.cpp
index a39dc9d942..55f8414bd5 100644
--- a/src/gui/text/qtextdocumentwriter.cpp
+++ b/src/gui/text/qtextdocumentwriter.cpp
@@ -41,7 +41,6 @@ public:
\inmodule QtGui
\ingroup richtext-processing
- \ingroup io
To write a document, construct a QTextDocumentWriter object with either a
file name or a device object, and specify the document format to be
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
index f489933940..a18157ab9b 100644
--- a/src/gui/text/qtextengine.cpp
+++ b/src/gui/text/qtextengine.cpp
@@ -7,6 +7,7 @@
#include "qtextformat_p.h"
#include "qtextengine_p.h"
#include "qabstracttextdocumentlayout.h"
+#include "qabstracttextdocumentlayout_p.h"
#include "qtextlayout.h"
#include "qtextboundaryfinder.h"
#include <QtCore/private/qunicodetables_p.h>
@@ -35,15 +36,10 @@ public:
Itemizer(const QString &string, const QScriptAnalysis *analysis, QScriptItemArray &items)
: m_string(string),
m_analysis(analysis),
- m_items(items),
- m_splitter(nullptr)
+ m_items(items)
{
}
- ~Itemizer()
- {
- delete m_splitter;
- }
-
+ ~Itemizer() = default;
/// generate the script items
/// The caps parameter is used to choose the algorithm of splitting text and assigning roles to the textitems
void generate(int start, int length, QFont::Capitalization caps)
@@ -100,8 +96,8 @@ private:
return;
if (!m_splitter)
- m_splitter = new QTextBoundaryFinder(QTextBoundaryFinder::Word,
- m_string.constData(), m_string.length(),
+ m_splitter = std::make_unique<QTextBoundaryFinder>(QTextBoundaryFinder::Word,
+ m_string.constData(), m_string.size(),
/*buffer*/nullptr, /*buffer size*/0);
m_splitter->setPosition(start);
@@ -170,7 +166,7 @@ private:
const QString &m_string;
const QScriptAnalysis * const m_analysis;
QScriptItemArray &m_items;
- QTextBoundaryFinder *m_splitter;
+ std::unique_ptr<QTextBoundaryFinder> m_splitter;
};
// -----------------------------------------------------------------------------------------------------
@@ -1211,9 +1207,9 @@ enum JustificationClass {
Adds an inter character justification opportunity after the number or letter
character and a space justification opportunity after the space character.
*/
-static inline void qt_getDefaultJustificationOpportunities(const ushort *string, int length, const QGlyphLayout &g, ushort *log_clusters, int spaceAs)
+static inline void qt_getDefaultJustificationOpportunities(const ushort *string, qsizetype length, const QGlyphLayout &g, ushort *log_clusters, int spaceAs)
{
- int str_pos = 0;
+ qsizetype str_pos = 0;
while (str_pos < length) {
int glyph_pos = log_clusters[str_pos];
@@ -1245,7 +1241,7 @@ static inline void qt_getDefaultJustificationOpportunities(const ushort *string,
}
}
-static inline void qt_getJustificationOpportunities(const ushort *string, int length, const QScriptItem &si, const QGlyphLayout &g, ushort *log_clusters)
+static inline void qt_getJustificationOpportunities(const ushort *string, qsizetype length, const QScriptItem &si, const QGlyphLayout &g, ushort *log_clusters)
{
Q_ASSERT(length > 0 && g.numGlyphs > 0);
@@ -1395,8 +1391,7 @@ void QTextEngine::shapeText(int item) const
}
if (Q_UNLIKELY(!ensureSpace(itemLength))) {
- Q_UNREACHABLE(); // ### report OOM error somehow
- return;
+ Q_UNREACHABLE_RETURN(); // ### report OOM error somehow
}
QFontEngine *fontEngine = this->fontEngine(si, &si.ascent, &si.descent, &si.leading);
@@ -1404,6 +1399,7 @@ void QTextEngine::shapeText(int item) const
bool kerningEnabled;
bool letterSpacingIsAbsolute;
bool shapingEnabled = false;
+ QHash<QFont::Tag, quint32> features;
QFixed letterSpacing, wordSpacing;
#ifndef QT_NO_RAWFONT
if (useRawFont) {
@@ -1417,6 +1413,7 @@ void QTextEngine::shapeText(int item) const
wordSpacing = QFixed::fromReal(font.wordSpacing());
letterSpacing = QFixed::fromReal(font.letterSpacing());
letterSpacingIsAbsolute = true;
+ features = font.d->features;
} else
#endif
{
@@ -1429,6 +1426,7 @@ void QTextEngine::shapeText(int item) const
letterSpacingIsAbsolute = font.d->letterSpacingIsAbsolute;
letterSpacing = font.d->letterSpacing;
wordSpacing = font.d->wordSpacing;
+ features = font.d->features;
if (letterSpacingIsAbsolute && letterSpacing.value())
letterSpacing *= font.d->dpi / qt_defaultDpiY();
@@ -1436,8 +1434,7 @@ void QTextEngine::shapeText(int item) const
// split up the item into parts that come from different font engines
// k * 3 entries, array[k] == index in string, array[k + 1] == index in glyphs, array[k + 2] == engine index
- QList<uint> itemBoundaries;
- itemBoundaries.reserve(24);
+ QVarLengthArray<uint, 24> itemBoundaries;
QGlyphLayout initialGlyphs = availableGlyphs(&si);
int nGlyphs = initialGlyphs.numGlyphs;
@@ -1448,7 +1445,7 @@ void QTextEngine::shapeText(int item) const
shapingEnabled
? QFontEngine::GlyphIndicesOnly
: QFontEngine::ShaperFlag(0);
- if (!fontEngine->stringToCMap(reinterpret_cast<const QChar *>(string), itemLength, &initialGlyphs, &nGlyphs, shaperFlags))
+ if (fontEngine->stringToCMap(reinterpret_cast<const QChar *>(string), itemLength, &initialGlyphs, &nGlyphs, shaperFlags) < 0)
Q_UNREACHABLE();
}
@@ -1457,9 +1454,9 @@ void QTextEngine::shapeText(int item) const
for (int i = 0, glyph_pos = 0; i < itemLength; ++i, ++glyph_pos) {
const uint engineIdx = initialGlyphs.glyphs[glyph_pos] >> 24;
if (lastEngine != engineIdx) {
- itemBoundaries.append(i);
- itemBoundaries.append(glyph_pos);
- itemBoundaries.append(engineIdx);
+ itemBoundaries.push_back(i);
+ itemBoundaries.push_back(glyph_pos);
+ itemBoundaries.push_back(engineIdx);
if (engineIdx != 0) {
QFontEngine *actualFontEngine = static_cast<QFontEngineMulti *>(fontEngine)->engine(engineIdx);
@@ -1475,14 +1472,21 @@ void QTextEngine::shapeText(int item) const
++i;
}
} else {
- itemBoundaries.append(0);
- itemBoundaries.append(0);
- itemBoundaries.append(0);
+ itemBoundaries.push_back(0);
+ itemBoundaries.push_back(0);
+ itemBoundaries.push_back(0);
}
#if QT_CONFIG(harfbuzz)
if (Q_LIKELY(shapingEnabled)) {
- si.num_glyphs = shapeTextWithHarfbuzzNG(si, string, itemLength, fontEngine, itemBoundaries, kerningEnabled, letterSpacing != 0);
+ si.num_glyphs = shapeTextWithHarfbuzzNG(si,
+ string,
+ itemLength,
+ fontEngine,
+ itemBoundaries,
+ kerningEnabled,
+ letterSpacing != 0,
+ features);
} else
#endif
{
@@ -1592,9 +1596,10 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
const ushort *string,
int itemLength,
QFontEngine *fontEngine,
- const QList<uint> &itemBoundaries,
+ QSpan<uint> itemBoundaries,
bool kerningEnabled,
- bool hasLetterSpacing) const
+ bool hasLetterSpacing,
+ const QHash<QFont::Tag, quint32> &fontFeatures) const
{
uint glyphs_shaped = 0;
@@ -1613,7 +1618,7 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
// ### TODO get_default_for_script?
props.language = hb_language_get_default(); // use default language from locale
- for (int k = 0; k < itemBoundaries.size(); k += 3) {
+ for (qsizetype k = 0; k < itemBoundaries.size(); k += 3) {
const uint item_pos = itemBoundaries[k];
const uint item_length = (k + 4 < itemBoundaries.size() ? itemBoundaries[k + 3] : itemLength) - item_pos;
const uint engineIdx = itemBoundaries[k + 2];
@@ -1648,14 +1653,24 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
|| script == QChar::Script_Khmer || script == QChar::Script_Nko);
bool dontLigate = hasLetterSpacing && !scriptRequiresOpenType;
- const hb_feature_t features[5] = {
- { HB_TAG('k','e','r','n'), !!kerningEnabled, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
- { HB_TAG('l','i','g','a'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
- { HB_TAG('c','l','i','g'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
- { HB_TAG('d','l','i','g'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
- { HB_TAG('h','l','i','g'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END }
- };
- const int num_features = dontLigate ? 5 : 1;
+
+ QHash<QFont::Tag, quint32> features;
+ features.insert(QFont::Tag("kern"), !!kerningEnabled);
+ if (dontLigate) {
+ features.insert(QFont::Tag("liga"), false);
+ features.insert(QFont::Tag("clig"), false);
+ features.insert(QFont::Tag("dlig"), false);
+ features.insert(QFont::Tag("hlig"), false);
+ }
+ features.insert(fontFeatures);
+
+ QVarLengthArray<hb_feature_t, 16> featureArray;
+ for (auto it = features.constBegin(); it != features.constEnd(); ++it) {
+ featureArray.append({ it.key().value(),
+ it.value(),
+ HB_FEATURE_GLOBAL_START,
+ HB_FEATURE_GLOBAL_END });
+ }
// whitelist cross-platforms shapers only
static const char *shaper_list[] = {
@@ -1665,7 +1680,11 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
nullptr
};
- bool shapedOk = hb_shape_full(hb_font, buffer, features, num_features, shaper_list);
+ bool shapedOk = hb_shape_full(hb_font,
+ buffer,
+ featureArray.constData(),
+ features.size(),
+ shaper_list);
if (Q_UNLIKELY(!shapedOk)) {
hb_buffer_destroy(buffer);
return 0;
@@ -1675,9 +1694,14 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
hb_buffer_reverse(buffer);
}
- const uint num_glyphs = hb_buffer_get_length(buffer);
+ uint num_glyphs = hb_buffer_get_length(buffer);
+ const bool has_glyphs = num_glyphs > 0;
+ // If Harfbuzz returns zero glyphs, we have to manually add a missing glyph
+ if (Q_UNLIKELY(!has_glyphs))
+ num_glyphs = 1;
+
// ensure we have enough space for shaped glyphs and metrics
- if (Q_UNLIKELY(num_glyphs == 0 || !ensureSpace(glyphs_shaped + num_glyphs))) {
+ if (Q_UNLIKELY(!ensureSpace(glyphs_shaped + num_glyphs))) {
hb_buffer_destroy(buffer);
return 0;
}
@@ -1685,35 +1709,44 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
// fetch the shaped glyphs and metrics
QGlyphLayout g = availableGlyphs(&si).mid(glyphs_shaped, num_glyphs);
ushort *log_clusters = logClusters(&si) + item_pos;
-
- hb_glyph_info_t *infos = hb_buffer_get_glyph_infos(buffer, nullptr);
- hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer, nullptr);
- uint str_pos = 0;
- uint last_cluster = ~0u;
- uint last_glyph_pos = glyphs_shaped;
- for (uint i = 0; i < num_glyphs; ++i, ++infos, ++positions) {
- g.glyphs[i] = infos->codepoint;
-
- g.advances[i] = QFixed::fromFixed(positions->x_advance);
- g.offsets[i].x = QFixed::fromFixed(positions->x_offset);
- g.offsets[i].y = QFixed::fromFixed(positions->y_offset);
-
- uint cluster = infos->cluster;
- if (Q_LIKELY(last_cluster != cluster)) {
- g.attributes[i].clusterStart = true;
-
- // fix up clusters so that the cluster indices will be monotonic
- // and thus we never return out-of-order indices
- while (last_cluster++ < cluster && str_pos < item_length)
- log_clusters[str_pos++] = last_glyph_pos;
- last_glyph_pos = i + glyphs_shaped;
- last_cluster = cluster;
-
- applyVisibilityRules(string[item_pos + str_pos], &g, i, actualFontEngine);
+ if (Q_LIKELY(has_glyphs)) {
+ hb_glyph_info_t *infos = hb_buffer_get_glyph_infos(buffer, nullptr);
+ hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer, nullptr);
+ uint str_pos = 0;
+ uint last_cluster = ~0u;
+ uint last_glyph_pos = glyphs_shaped;
+ for (uint i = 0; i < num_glyphs; ++i, ++infos, ++positions) {
+ g.glyphs[i] = infos->codepoint;
+
+ g.advances[i] = QFixed::fromFixed(positions->x_advance);
+ g.offsets[i].x = QFixed::fromFixed(positions->x_offset);
+ g.offsets[i].y = QFixed::fromFixed(positions->y_offset);
+
+ uint cluster = infos->cluster;
+ if (Q_LIKELY(last_cluster != cluster)) {
+ g.attributes[i].clusterStart = true;
+
+ // fix up clusters so that the cluster indices will be monotonic
+ // and thus we never return out-of-order indices
+ while (last_cluster++ < cluster && str_pos < item_length)
+ log_clusters[str_pos++] = last_glyph_pos;
+ last_glyph_pos = i + glyphs_shaped;
+ last_cluster = cluster;
+
+ applyVisibilityRules(string[item_pos + str_pos], &g, i, actualFontEngine);
+ }
}
+ while (str_pos < item_length)
+ log_clusters[str_pos++] = last_glyph_pos;
+ } else { // Harfbuzz did not return a glyph for the character, so we add a placeholder
+ g.glyphs[0] = 0;
+ g.advances[0] = QFixed{};
+ g.offsets[0].x = QFixed{};
+ g.offsets[0].y = QFixed{};
+ g.attributes[0].clusterStart = true;
+ g.attributes[0].dontPrint = true;
+ log_clusters[0] = glyphs_shaped;
}
- while (str_pos < item_length)
- log_clusters[str_pos++] = last_glyph_pos;
if (Q_UNLIKELY(engineIdx != 0)) {
for (quint32 i = 0; i < num_glyphs; ++i)
@@ -1721,8 +1754,10 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
}
if (!actualFontEngine->supportsHorizontalSubPixelPositions()) {
- for (uint i = 0; i < num_glyphs; ++i)
+ for (uint i = 0; i < num_glyphs; ++i) {
g.advances[i] = g.advances[i].round();
+ g.offsets[i].x = g.offsets[i].x.round();
+ }
}
glyphs_shaped += num_glyphs;
@@ -1781,7 +1816,7 @@ const QCharAttributes *QTextEngine::attributes() const
return (QCharAttributes *) layoutData->memory;
itemize();
- if (! ensureSpace(layoutData->string.length()))
+ if (! ensureSpace(layoutData->string.size()))
return nullptr;
QVarLengthArray<QUnicodeTools::ScriptItem> scriptItems(layoutData->items.size());
@@ -1888,7 +1923,7 @@ void QTextEngine::itemize() const
if (layoutData->items.size())
return;
- int length = layoutData->string.length();
+ int length = layoutData->string.size();
if (!length)
return;
@@ -1905,9 +1940,9 @@ void QTextEngine::itemize() const
{
QUnicodeTools::ScriptItemArray scriptItems;
QUnicodeTools::initScripts(layoutData->string, &scriptItems);
- for (int i = 0; i < scriptItems.length(); ++i) {
+ for (int i = 0; i < scriptItems.size(); ++i) {
const auto &item = scriptItems.at(i);
- int end = i < scriptItems.length() - 1 ? scriptItems.at(i + 1).position : length;
+ int end = i < scriptItems.size() - 1 ? scriptItems.at(i + 1).position : length;
for (int j = item.position; j < end; ++j)
analysis[j].script = item.script;
}
@@ -1918,7 +1953,17 @@ void QTextEngine::itemize() const
while (uc < e) {
switch (*uc) {
case QChar::ObjectReplacementCharacter:
- analysis->flags = QScriptAnalysis::Object;
+ {
+ const QTextDocumentPrivate *doc_p = QTextDocumentPrivate::get(block);
+ if (doc_p != nullptr
+ && doc_p->layout() != nullptr
+ && QAbstractTextDocumentLayoutPrivate::get(doc_p->layout()) != nullptr
+ && QAbstractTextDocumentLayoutPrivate::get(doc_p->layout())->hasHandlers()) {
+ analysis->flags = QScriptAnalysis::Object;
+ } else {
+ analysis->flags = QScriptAnalysis::None;
+ }
+ }
break;
case QChar::LineSeparator:
analysis->flags = QScriptAnalysis::LineOrParagraphSeparator;
@@ -1970,7 +2015,7 @@ void QTextEngine::itemize() const
const QTextFragmentData * const frag = it.value();
if (it == end || format != frag->format) {
if (s && position >= preeditPosition) {
- position += s->preeditText.length();
+ position += s->preeditText.size();
preeditPosition = INT_MAX;
}
Q_ASSERT(position <= length);
@@ -1979,7 +2024,7 @@ void QTextEngine::itemize() const
? formatCollection()->charFormat(format).fontCapitalization()
: formatCollection()->defaultFont().capitalization();
if (s) {
- for (const auto &range : qAsConst(s->formats)) {
+ for (const auto &range : std::as_const(s->formats)) {
if (range.start + range.length <= prevPosition || range.start >= position)
continue;
if (range.format.hasProperty(QTextFormat::FontCapitalization)) {
@@ -2399,7 +2444,7 @@ void QTextEngine::justify(const QScriptLine &line)
if (!forceJustification) {
int end = line.from + (int)line.length + line.trailingSpaces;
- if (end == layoutData->string.length())
+ if (end == layoutData->string.size())
return; // no justification at end of paragraph
if (end && layoutData->items.at(findItem(end - 1)).analysis.flags == QScriptAnalysis::LineOrParagraphSeparator)
return; // no justification at the end of an explicitly separated line
@@ -2605,18 +2650,20 @@ QTextEngine::LayoutData::LayoutData()
haveCharAttributes = false;
logClustersPtr = nullptr;
available_glyphs = 0;
+ currentMaxWidth = 0;
}
-QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int _allocated)
+QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, qsizetype _allocated)
: string(str)
{
allocated = _allocated;
- int space_charAttributes = int(sizeof(QCharAttributes) * string.length() / sizeof(void*) + 1);
- int space_logClusters = int(sizeof(unsigned short) * string.length() / sizeof(void*) + 1);
- available_glyphs = ((int)allocated - space_charAttributes - space_logClusters)*(int)sizeof(void*)/(int)QGlyphLayout::SpaceNeeded;
+ constexpr qsizetype voidSize = sizeof(void*);
+ qsizetype space_charAttributes = sizeof(QCharAttributes) * string.size() / voidSize + 1;
+ qsizetype space_logClusters = sizeof(unsigned short) * string.size() / voidSize + 1;
+ available_glyphs = (allocated - space_charAttributes - space_logClusters) * voidSize / QGlyphLayout::SpaceNeeded;
- if (available_glyphs < str.length()) {
+ if (available_glyphs < str.size()) {
// need to allocate on the heap
allocated = 0;
@@ -2629,7 +2676,7 @@ QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int
logClustersPtr = (unsigned short *)(memory + space_charAttributes);
void *m = memory + space_charAttributes + space_logClusters;
- glyphLayout = QGlyphLayout(reinterpret_cast<char *>(m), str.length());
+ glyphLayout = QGlyphLayout(reinterpret_cast<char *>(m), str.size());
glyphLayout.clear();
memset(memory, 0, space_charAttributes*sizeof(void *));
}
@@ -2637,6 +2684,7 @@ QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int
hasBidi = false;
layoutState = LayoutEmpty;
haveCharAttributes = false;
+ currentMaxWidth = 0;
}
QTextEngine::LayoutData::~LayoutData()
@@ -2654,15 +2702,16 @@ bool QTextEngine::LayoutData::reallocate(int totalGlyphs)
return true;
}
- int space_charAttributes = int(sizeof(QCharAttributes) * string.length() / sizeof(void*) + 1);
- int space_logClusters = int(sizeof(unsigned short) * string.length() / sizeof(void*) + 1);
- int space_glyphs = (totalGlyphs * QGlyphLayout::SpaceNeeded) / sizeof(void *) + 2;
+ const qsizetype space_charAttributes = (sizeof(QCharAttributes) * string.size() / sizeof(void*) + 1);
+ const qsizetype space_logClusters = (sizeof(unsigned short) * string.size() / sizeof(void*) + 1);
+ const qsizetype space_glyphs = qsizetype(totalGlyphs) * QGlyphLayout::SpaceNeeded / sizeof(void *) + 2;
- int newAllocated = space_charAttributes + space_glyphs + space_logClusters;
- // These values can be negative if the length of string/glyphs causes overflow,
+ const qsizetype newAllocated = space_charAttributes + space_glyphs + space_logClusters;
+ // Check if the length of string/glyphs causes int overflow,
// we can't layout such a long string all at once, so return false here to
// indicate there is a failure
- if (space_charAttributes < 0 || space_logClusters < 0 || space_glyphs < 0 || newAllocated < allocated) {
+ if (size_t(space_charAttributes) > INT_MAX || size_t(space_logClusters) > INT_MAX || totalGlyphs < 0
+ || size_t(space_glyphs) > INT_MAX || size_t(newAllocated) > INT_MAX || newAllocated < allocated) {
layoutState = LayoutFailed;
return false;
}
@@ -2682,7 +2731,7 @@ bool QTextEngine::LayoutData::reallocate(int totalGlyphs)
logClustersPtr = (unsigned short *) m;
m += space_logClusters;
- const int space_preGlyphLayout = space_charAttributes + space_logClusters;
+ const qsizetype space_preGlyphLayout = space_charAttributes + space_logClusters;
if (allocated < space_preGlyphLayout)
memset(memory + allocated, 0, (space_preGlyphLayout - allocated)*sizeof(void *));
@@ -2692,6 +2741,21 @@ bool QTextEngine::LayoutData::reallocate(int totalGlyphs)
return true;
}
+void QGlyphLayout::copy(QGlyphLayout *oldLayout)
+{
+ Q_ASSERT(offsets != oldLayout->offsets);
+
+ int n = std::min(numGlyphs, oldLayout->numGlyphs);
+
+ memcpy(offsets, oldLayout->offsets, n * sizeof(QFixedPoint));
+ memcpy(attributes, oldLayout->attributes, n * sizeof(QGlyphAttributes));
+ memcpy(justifications, oldLayout->justifications, n * sizeof(QGlyphJustification));
+ memcpy(advances, oldLayout->advances, n * sizeof(QFixed));
+ memcpy(glyphs, oldLayout->glyphs, n * sizeof(glyph_t));
+
+ numGlyphs = n;
+}
+
// grow to the new size, copying the existing data to the new layout
void QGlyphLayout::grow(char *address, int totalGlyphs)
{
@@ -2722,6 +2786,7 @@ void QTextEngine::freeMemory()
layoutData->hasBidi = false;
layoutData->layoutState = LayoutEmpty;
layoutData->haveCharAttributes = false;
+ layoutData->currentMaxWidth = 0;
layoutData->items.clear();
}
if (specialData)
@@ -2745,10 +2810,10 @@ int QTextEngine::formatIndex(const QScriptItem *si) const
return -1;
int pos = si->position;
if (specialData && si->position >= specialData->preeditPosition) {
- if (si->position < specialData->preeditPosition + specialData->preeditText.length())
+ if (si->position < specialData->preeditPosition + specialData->preeditText.size())
pos = qMax(qMin(block.length(), specialData->preeditPosition) - 1, 0);
else
- pos -= specialData->preeditText.length();
+ pos -= specialData->preeditText.size();
}
QTextDocumentPrivate::FragmentIterator it = p->find(block.position() + pos);
return it.value()->format;
@@ -2883,9 +2948,9 @@ void QTextEngine::indexFormats()
*/
static inline bool nextCharJoins(const QString &string, int pos)
{
- while (pos < string.length() && string.at(pos).category() == QChar::Mark_NonSpacing)
+ while (pos < string.size() && string.at(pos).category() == QChar::Mark_NonSpacing)
++pos;
- if (pos == string.length())
+ if (pos == string.size())
return false;
QChar::JoiningType joining = string.at(pos).joiningType();
return joining != QChar::Joining_None && joining != QChar::Joining_Transparent;
@@ -2901,11 +2966,11 @@ static inline bool prevCharJoins(const QString &string, int pos)
return joining == QChar::Joining_Dual || joining == QChar::Joining_Causing;
}
-static inline bool isRetainableControlCode(QChar c)
+static constexpr bool isRetainableControlCode(char16_t c) noexcept
{
- return (c.unicode() >= 0x202a && c.unicode() <= 0x202e) // LRE, RLE, PDF, LRO, RLO
- || (c.unicode() >= 0x200e && c.unicode() <= 0x200f) // LRM, RLM
- || (c.unicode() >= 0x2066 && c.unicode() <= 0x2069); // LRI, RLI, FSI, PDI
+ return (c >= 0x202a && c <= 0x202e) // LRE, RLE, PDF, LRO, RLO
+ || (c >= 0x200e && c <= 0x200f) // LRM, RLM
+ || (c >= 0x2066 && c <= 0x2069); // LRI, RLI, FSI, PDI
}
static QString stringMidRetainingBidiCC(const QString &string,
@@ -2918,14 +2983,14 @@ static QString stringMidRetainingBidiCC(const QString &string,
{
QString prefix;
for (int i=subStringFrom; i<midStart; ++i) {
- QChar c = string.at(i);
+ char16_t c = string.at(i).unicode();
if (isRetainableControlCode(c))
prefix += c;
}
QString suffix;
for (int i=midStart + midLength; i<subStringTo; ++i) {
- QChar c = string.at(i);
+ char16_t c = string.at(i).unicode();
if (isRetainableControlCode(c))
suffix += c;
}
@@ -2933,7 +2998,7 @@ static QString stringMidRetainingBidiCC(const QString &string,
return prefix + ellidePrefix + QStringView{string}.mid(midStart, midLength) + ellideSuffix + suffix;
}
-QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int flags, int from, int count) const
+QString QTextEngine::elidedText(Qt::TextElideMode mode, QFixed width, int flags, int from, int count) const
{
// qDebug() << "elidedText; available width" << width.toReal() << "text width:" << this->width(0, layoutData->string.length()).toReal();
@@ -2968,12 +3033,12 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
validate();
- const int to = count >= 0 && count <= layoutData->string.length() - from
+ const int to = count >= 0 && count <= layoutData->string.size() - from
? from + count
- : layoutData->string.length();
+ : layoutData->string.size();
if (mode == Qt::ElideNone
- || this->width(from, layoutData->string.length()) <= width
+ || this->width(from, layoutData->string.size()) <= width
|| to - from <= 1)
return layoutData->string.mid(from, from - to);
@@ -2982,7 +3047,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
{
QFontEngine *engine = fnt.d->engineForScript(QChar::Script_Common);
- QChar ellipsisChar = u'\x2026';
+ constexpr char16_t ellipsisChar = u'\x2026';
// We only want to use the ellipsis character if it is from the main
// font (not one of the fallbacks), since using a fallback font
@@ -2994,7 +3059,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
engine = multiEngine->engine(0);
}
- glyph_t glyph = engine->glyphIndex(ellipsisChar.unicode());
+ glyph_t glyph = engine->glyphIndex(ellipsisChar);
QGlyphLayout glyphs;
glyphs.numGlyphs = 1;
@@ -3014,7 +3079,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
ellipsisText = QStringLiteral("...");
} else {
engine = fnt.d->engineForScript(QChar::Script_Common);
- glyph = engine->glyphIndex(ellipsisChar.unicode());
+ glyph = engine->glyphIndex(ellipsisChar);
engine->recalcAdvances(&glyphs, { });
ellipsisText = ellipsisChar;
}
@@ -3040,7 +3105,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
pos = nextBreak;
++nextBreak;
- while (nextBreak < layoutData->string.length() && !attributes[nextBreak].graphemeBoundary)
+ while (nextBreak < layoutData->string.size() && !attributes[nextBreak].graphemeBoundary)
++nextBreak;
currentWidth += this->width(pos, nextBreak - pos);
@@ -3092,7 +3157,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
rightPos = nextRightBreak;
++nextLeftBreak;
- while (nextLeftBreak < layoutData->string.length() && !attributes[nextLeftBreak].graphemeBoundary)
+ while (nextLeftBreak < layoutData->string.size() && !attributes[nextLeftBreak].graphemeBoundary)
++nextLeftBreak;
--nextRightBreak;
@@ -3131,7 +3196,7 @@ void QTextEngine::setBoundary(int strPos) const
QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
{
- const QScriptItem &si = layoutData->items[item];
+ const QScriptItem &si = layoutData->items.at(item);
QFixed dpiScale = 1;
if (QTextDocumentPrivate::get(block) != nullptr && QTextDocumentPrivate::get(block)->layout() != nullptr) {
@@ -3165,15 +3230,15 @@ QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
}
}
}
- for (const QTextOption::Tab &tabSpec : qAsConst(tabArray)) {
+ for (const QTextOption::Tab &tabSpec : std::as_const(tabArray)) {
QFixed tab = QFixed::fromReal(tabSpec.position) * dpiScale;
if (tab > x) { // this is the tab we need.
- int tabSectionEnd = layoutData->string.length();
+ int tabSectionEnd = layoutData->string.size();
if (tabSpec.type == QTextOption::RightTab || tabSpec.type == QTextOption::CenterTab) {
// find next tab to calculate the width required.
tab = QFixed::fromReal(tabSpec.position);
- for (int i=item + 1; i < layoutData->items.count(); i++) {
- const QScriptItem &item = layoutData->items[i];
+ for (int i=item + 1; i < layoutData->items.size(); i++) {
+ const QScriptItem &item = layoutData->items.at(i);
if (item.analysis.flags == QScriptAnalysis::TabOrObject) { // found it.
tabSectionEnd = item.position;
break;
@@ -3187,7 +3252,7 @@ QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
if (tabSectionEnd > si.position) {
QFixed length;
// Calculate the length of text between this tab and the tabSectionEnd
- for (int i=item; i < layoutData->items.count(); i++) {
+ for (int i=item; i < layoutData->items.size(); i++) {
const QScriptItem &item = layoutData->items.at(i);
if (item.position > tabSectionEnd || item.position <= si.position)
continue;
@@ -3259,7 +3324,7 @@ void QTextEngine::resolveFormats() const
QTextFormatCollection *collection = formatCollection();
- QList<QTextCharFormat> resolvedFormats(layoutData->items.count());
+ QList<QTextCharFormat> resolvedFormats(layoutData->items.size());
QVarLengthArray<int, 64> formatsSortedByStart;
formatsSortedByStart.reserve(specialData->formats.size());
@@ -3277,7 +3342,7 @@ void QTextEngine::resolveFormats() const
const int *startIt = formatsSortedByStart.constBegin();
const int *endIt = formatsSortedByEnd.constBegin();
- for (int i = 0; i < layoutData->items.count(); ++i) {
+ for (int i = 0; i < layoutData->items.size(); ++i) {
const QScriptItem *si = &layoutData->items.at(i);
int end = si->position + length(si);
@@ -3453,8 +3518,8 @@ int QTextEngine::previousLogicalPosition(int oldPos) const
{
const QCharAttributes *attrs = attributes();
int len = block.isValid() ? block.length() - 1
- : layoutData->string.length();
- Q_ASSERT(len <= layoutData->string.length());
+ : layoutData->string.size();
+ Q_ASSERT(len <= layoutData->string.size());
if (!attrs || oldPos <= 0 || oldPos > len)
return oldPos;
@@ -3468,8 +3533,8 @@ int QTextEngine::nextLogicalPosition(int oldPos) const
{
const QCharAttributes *attrs = attributes();
int len = block.isValid() ? block.length() - 1
- : layoutData->string.length();
- Q_ASSERT(len <= layoutData->string.length());
+ : layoutData->string.size();
+ Q_ASSERT(len <= layoutData->string.size());
if (!attrs || oldPos < 0 || oldPos >= len)
return oldPos;
@@ -3483,7 +3548,7 @@ int QTextEngine::lineNumberForTextPosition(int pos)
{
if (!layoutData)
itemize();
- if (pos == layoutData->string.length() && lines.size())
+ if (pos == layoutData->string.size() && lines.size())
return lines.size() - 1;
for (int i = 0; i < lines.size(); ++i) {
const QScriptLine& line = lines[i];
diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h
index f9a43ecd56..c01d3a0711 100644
--- a/src/gui/text/qtextengine_p.h
+++ b/src/gui/text/qtextengine_p.h
@@ -26,6 +26,7 @@
#include "QtCore/qlist.h"
#include "QtCore/qnamespace.h"
#include "QtCore/qset.h"
+#include <QtCore/qspan.h>
#include "QtCore/qstring.h"
#include "QtCore/qvarlengtharray.h"
@@ -158,10 +159,8 @@ Q_DECLARE_TYPEINFO(QGlyphAttributes, Q_PRIMITIVE_TYPE);
struct QGlyphLayout
{
- enum {
- SpaceNeeded = sizeof(glyph_t) + sizeof(QFixed) + sizeof(QFixedPoint)
- + sizeof(QGlyphAttributes) + sizeof(QGlyphJustification)
- };
+ static constexpr qsizetype SpaceNeeded = sizeof(glyph_t) + sizeof(QFixed) + sizeof(QFixedPoint)
+ + sizeof(QGlyphAttributes) + sizeof(QGlyphJustification);
// init to 0 not needed, done when shaping
QFixedPoint *offsets; // 8 bytes per element
@@ -177,7 +176,7 @@ struct QGlyphLayout
inline explicit QGlyphLayout(char *address, int totalGlyphs)
{
offsets = reinterpret_cast<QFixedPoint *>(address);
- int offset = totalGlyphs * sizeof(QFixedPoint);
+ qsizetype offset = totalGlyphs * sizeof(QFixedPoint);
glyphs = reinterpret_cast<glyph_t *>(address + offset);
offset += totalGlyphs * sizeof(glyph_t);
advances = reinterpret_cast<QFixed *>(address + offset);
@@ -210,7 +209,7 @@ struct QGlyphLayout
last = numGlyphs;
if (first == 0 && last == numGlyphs
&& reinterpret_cast<char *>(offsets + numGlyphs) == reinterpret_cast<char *>(glyphs)) {
- memset(static_cast<void *>(offsets), 0, (numGlyphs * SpaceNeeded));
+ memset(static_cast<void *>(offsets), 0, qsizetype(numGlyphs) * SpaceNeeded);
} else {
const int num = last - first;
memset(static_cast<void *>(offsets + first), 0, num * sizeof(QFixedPoint));
@@ -225,6 +224,7 @@ struct QGlyphLayout
return reinterpret_cast<char *>(offsets);
}
+ void copy(QGlyphLayout *other);
void grow(char *address, int totalGlyphs);
};
@@ -371,12 +371,12 @@ public:
LayoutFailed
};
struct Q_GUI_EXPORT LayoutData {
- LayoutData(const QString &str, void **stack_memory, int mem_size);
+ LayoutData(const QString &str, void **stack_memory, qsizetype mem_size);
LayoutData();
~LayoutData();
mutable QScriptItemArray items;
- int allocated;
- int available_glyphs;
+ qsizetype allocated;
+ qsizetype available_glyphs;
void **memory;
unsigned short *logClustersPtr;
QGlyphLayout glyphLayout;
@@ -385,6 +385,7 @@ public:
uint layoutState : 2;
uint memory_on_stack : 1;
uint haveCharAttributes : 1;
+ QFixed currentMaxWidth;
QString string;
bool reallocate(int totalGlyphs);
};
@@ -434,14 +435,14 @@ public:
const QScriptItem &si = layoutData->items[item];
int from = si.position;
item++;
- return (item < layoutData->items.size() ? layoutData->items[item].position : layoutData->string.length()) - from;
+ return (item < layoutData->items.size() ? layoutData->items[item].position : layoutData->string.size()) - from;
}
int length(const QScriptItem *si) const {
int end;
if (si + 1 < layoutData->items.constData()+ layoutData->items.size())
end = (si+1)->position;
else
- end = layoutData->string.length();
+ end = layoutData->string.size();
return end - si->position;
}
@@ -586,7 +587,7 @@ private:
public:
bool atWordSeparator(int position) const;
- QString elidedText(Qt::TextElideMode mode, const QFixed &width, int flags = 0, int from = 0, int count = -1) const;
+ QString elidedText(Qt::TextElideMode mode, QFixed width, int flags = 0, int from = 0, int count = -1) const;
void shapeLine(const QScriptLine &line);
QFixed leadingSpaceWidth(const QScriptLine &line);
@@ -620,9 +621,14 @@ private:
void addRequiredBoundaries() const;
void shapeText(int item) const;
#if QT_CONFIG(harfbuzz)
- int shapeTextWithHarfbuzzNG(const QScriptItem &si, const ushort *string, int itemLength,
- QFontEngine *fontEngine, const QList<uint> &itemBoundaries,
- bool kerningEnabled, bool hasLetterSpacing) const;
+ int shapeTextWithHarfbuzzNG(const QScriptItem &si,
+ const ushort *string,
+ int itemLength,
+ QFontEngine *fontEngine,
+ QSpan<uint> itemBoundaries,
+ bool kerningEnabled,
+ bool hasLetterSpacing,
+ const QHash<QFont::Tag, quint32> &features) const;
#endif
int endOfLine(int lineNum);
diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp
index fa83212033..3e4bacf78c 100644
--- a/src/gui/text/qtextformat.cpp
+++ b/src/gui/text/qtextformat.cpp
@@ -168,7 +168,7 @@ public:
if (key >= QTextFormat::FirstFontProperty && key <= QTextFormat::LastFontProperty)
fontDirty = true;
- for (int i = 0; i < props.count(); ++i)
+ for (int i = 0; i < props.size(); ++i)
if (props.at(i).key == key) {
props[i].value = value;
return;
@@ -178,7 +178,7 @@ public:
inline void clearProperty(qint32 key)
{
- for (int i = 0; i < props.count(); ++i)
+ for (int i = 0; i < props.size(); ++i)
if (props.at(i).key == key) {
hashDirty = true;
if (key >= QTextFormat::FirstFontProperty && key <= QTextFormat::LastFontProperty)
@@ -190,7 +190,7 @@ public:
inline int propertyIndex(qint32 key) const
{
- for (int i = 0; i < props.count(); ++i)
+ for (int i = 0; i < props.size(); ++i)
if (props.at(i).key == key)
return i;
return -1;
@@ -257,7 +257,7 @@ static inline size_t variantHash(const QVariant &variant)
case QMetaType::Bool: return 0x371818 + variant.toBool();
case QMetaType::QPen: return 0x02020202 + hash(qvariant_cast<QPen>(variant));
case QMetaType::QVariantList:
- return 0x8377U + qvariant_cast<QVariantList>(variant).count();
+ return 0x8377U + qvariant_cast<QVariantList>(variant).size();
case QMetaType::QColor: return hash(qvariant_cast<QColor>(variant));
case QMetaType::QTextLength:
return 0x377 + hash(qvariant_cast<QTextLength>(variant).rawValue());
@@ -318,7 +318,7 @@ void QTextFormatPrivate::recalcFont() const
QFont::SpacingType spacingType = QFont::PercentageSpacing;
qreal letterSpacing = 0.0;
- for (int i = 0; i < props.count(); ++i) {
+ for (int i = 0; i < props.size(); ++i) {
switch (props.at(i).key) {
case QTextFormat::FontFamilies:
f.setFamilies(props.at(i).value.toStringList());
@@ -405,27 +405,27 @@ Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QTextFormat &fmt
{
QMap<int, QVariant> properties = fmt.properties();
if (stream.version() < QDataStream::Qt_6_0) {
- auto it = properties.find(QTextFormat::FontLetterSpacingType);
- if (it != properties.end()) {
+ auto it = properties.constFind(QTextFormat::FontLetterSpacingType);
+ if (it != properties.cend()) {
properties[QTextFormat::OldFontLetterSpacingType] = it.value();
properties.erase(it);
}
- it = properties.find(QTextFormat::FontStretch);
- if (it != properties.end()) {
+ it = properties.constFind(QTextFormat::FontStretch);
+ if (it != properties.cend()) {
properties[QTextFormat::OldFontStretch] = it.value();
properties.erase(it);
}
- it = properties.find(QTextFormat::TextUnderlineColor);
- if (it != properties.end()) {
+ it = properties.constFind(QTextFormat::TextUnderlineColor);
+ if (it != properties.cend()) {
properties[QTextFormat::OldTextUnderlineColor] = it.value();
properties.erase(it);
}
- it = properties.find(QTextFormat::FontFamilies);
- if (it != properties.end()) {
- properties[QTextFormat::FontFamily] = QVariant(it.value().toStringList().first());
+ it = properties.constFind(QTextFormat::FontFamilies);
+ if (it != properties.cend()) {
+ properties[QTextFormat::OldFontFamily] = QVariant(it.value().toStringList().constFirst());
properties.erase(it);
}
}
@@ -453,7 +453,7 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
key = QTextFormat::FontStretch;
else if (key == QTextFormat::OldTextUnderlineColor)
key = QTextFormat::TextUnderlineColor;
- else if (key == QTextFormat::FontFamily)
+ else if (key == QTextFormat::OldFontFamily)
key = QTextFormat::FontFamilies;
fmt.d->insertProperty(key, it.value());
}
@@ -625,6 +625,7 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextTableCellFormat &
Character properties
\value FontFamily e{This property has been deprecated.} Use QTextFormat::FontFamilies instead.
+ \omitvalue OldFontFamily
\value FontFamilies
\value FontStyleName
\value FontPointSize
@@ -683,6 +684,8 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextTableCellFormat &
numeric lists.
\value ListNumberSuffix Defines the text which is appended to item numbers in
numeric lists.
+ \value [since 6.6] ListStart
+ Defines the first value of a list.
Table and frame properties
@@ -950,9 +953,13 @@ void QTextFormat::merge(const QTextFormat &other)
const QList<QT_PREPEND_NAMESPACE(Property)> &otherProps = other.d.constData()->props;
p->props.reserve(p->props.size() + otherProps.size());
- for (int i = 0; i < otherProps.count(); ++i) {
+ for (int i = 0; i < otherProps.size(); ++i) {
const QT_PREPEND_NAMESPACE(Property) &prop = otherProps.at(i);
- p->insertProperty(prop.key, prop.value);
+ if (prop.value.isValid()) {
+ p->insertProperty(prop.key, prop.value);
+ } else {
+ p->clearProperty(prop.key);
+ }
}
}
@@ -1209,10 +1216,8 @@ void QTextFormat::setProperty(int propertyId, const QVariant &value)
{
if (!d)
d = new QTextFormatPrivate;
- if (!value.isValid())
- clearProperty(propertyId);
- else
- d->insertProperty(propertyId, value);
+
+ d->insertProperty(propertyId, value);
}
/*!
@@ -1322,7 +1327,7 @@ QMap<int, QVariant> QTextFormat::properties() const
{
QMap<int, QVariant> map;
if (d) {
- for (int i = 0; i < d->props.count(); ++i)
+ for (int i = 0; i < d->props.size(); ++i)
map.insert(d->props.at(i).key, d->props.at(i).value);
}
return map;
@@ -1334,7 +1339,7 @@ QMap<int, QVariant> QTextFormat::properties() const
*/
int QTextFormat::propertyCount() const
{
- return d ? d->props.count() : 0;
+ return d ? d->props.size() : 0;
}
/*!
@@ -2166,7 +2171,7 @@ QFont QTextCharFormat::font() const
associated QTextBlockFormat that specifies its characteristics.
To cater for left-to-right and right-to-left languages you can set
- a block's direction with setDirection(). Paragraph alignment is
+ a block's direction with setLayoutDirection(). Paragraph alignment is
set with setAlignment(). Margins are controlled by setTopMargin(),
setBottomMargin(), setLeftMargin(), setRightMargin(). Overall
indentation is set with setIndent(), the indentation of the first
@@ -2236,14 +2241,9 @@ QTextBlockFormat::QTextBlockFormat(const QTextFormat &fmt)
void QTextBlockFormat::setTabPositions(const QList<QTextOption::Tab> &tabs)
{
QList<QVariant> list;
- list.reserve(tabs.count());
- QList<QTextOption::Tab>::ConstIterator iter = tabs.constBegin();
- while (iter != tabs.constEnd()) {
- QVariant v;
- v.setValue(*iter);
- list.append(v);
- ++iter;
- }
+ list.reserve(tabs.size());
+ for (const auto &e : tabs)
+ list.append(QVariant::fromValue(e));
setProperty(TabPositions, list);
}
@@ -2259,13 +2259,10 @@ QList<QTextOption::Tab> QTextBlockFormat::tabPositions() const
if (variant.isNull())
return QList<QTextOption::Tab>();
QList<QTextOption::Tab> answer;
- QList<QVariant> variantsList = qvariant_cast<QList<QVariant> >(variant);
- QList<QVariant>::Iterator iter = variantsList.begin();
- answer.reserve(variantsList.count());
- while(iter != variantsList.end()) {
- answer.append( qvariant_cast<QTextOption::Tab>(*iter));
- ++iter;
- }
+ const QList<QVariant> variantsList = qvariant_cast<QList<QVariant> >(variant);
+ answer.reserve(variantsList.size());
+ for (const auto &e: variantsList)
+ answer.append(qvariant_cast<QTextOption::Tab>(e));
return answer;
}
@@ -2609,7 +2606,8 @@ QList<QTextOption::Tab> QTextBlockFormat::tabPositions() const
The style used to decorate each item is set with setStyle() and can be read
with the style() function. The style controls the type of bullet points and
numbering scheme used for items in the list. Note that lists that use the
- decimal numbering scheme begin counting at 1 rather than 0.
+ decimal numbering scheme begin counting at 1 rather than 0, unless it has
+ been overridden via setStart().
Style properties can be set to further configure the appearance of list
items; for example, the ListNumberPrefix and ListNumberSuffix properties
@@ -2646,6 +2644,7 @@ QTextListFormat::QTextListFormat()
: QTextFormat(ListFormat)
{
setIndent(1);
+ setStart(1);
}
/*!
@@ -2750,6 +2749,32 @@ QTextListFormat::QTextListFormat(const QTextFormat &fmt)
*/
/*!
+ \fn void QTextListFormat::setStart(int start)
+ \since 6.6
+
+ Sets the list format's \a start index.
+
+ This allows you to start a list with an index other than 1. This can be
+ used with all sorted list types: for example if the style() is
+ QTextListFormat::ListLowerAlpha and start() is \c 4, the first list item
+ begins with "d". It does not have any effect on unsorted list types.
+
+ The default start is \c 1.
+
+ \sa start()
+*/
+
+/*!
+ \fn int QTextListFormat::start() const
+ \since 6.6
+
+ Returns the number to be shown by the first list item, if the style() is
+ QTextListFormat::ListDecimal, or to offset other sorted list types.
+
+ \sa setStart()
+*/
+
+/*!
\class QTextFrameFormat
\reentrant
@@ -3968,7 +3993,7 @@ bool QTextFormatCollection::hasFormatCached(const QTextFormat &format) const
int QTextFormatCollection::objectFormatIndex(int objectIndex) const
{
- if (objectIndex == -1)
+ if (objectIndex == -1 || objectIndex >= objFormats.size())
return -1;
return objFormats.at(objectIndex);
}
@@ -3987,7 +4012,7 @@ int QTextFormatCollection::createObjectIndex(const QTextFormat &f)
QTextFormat QTextFormatCollection::format(int idx) const
{
- if (idx < 0 || idx >= formats.count())
+ if (idx < 0 || idx >= formats.size())
return QTextFormat();
return formats.at(idx);
@@ -3996,7 +4021,7 @@ QTextFormat QTextFormatCollection::format(int idx) const
void QTextFormatCollection::setDefaultFont(const QFont &f)
{
defaultFnt = f;
- for (int i = 0; i < formats.count(); ++i)
+ for (int i = 0; i < formats.size(); ++i)
if (formats.at(i).d)
formats[i].d->resolveFont(defaultFnt);
}
diff --git a/src/gui/text/qtextformat.h b/src/gui/text/qtextformat.h
index 95bc9da334..c009d328cb 100644
--- a/src/gui/text/qtextformat.h
+++ b/src/gui/text/qtextformat.h
@@ -179,6 +179,7 @@ public:
OldFontLetterSpacingType = 0x2033,
OldFontStretch = 0x2034,
OldTextUnderlineColor = 0x2010,
+ OldFontFamily = 0x2000, // same as FontFamily
ObjectType = 0x2f00,
@@ -187,6 +188,7 @@ public:
ListIndent = 0x3001,
ListNumberPrefix = 0x3002,
ListNumberSuffix = 0x3003,
+ ListStart = 0x3004,
// table and frame properties
FrameBorder = 0x4000,
@@ -752,6 +754,9 @@ public:
inline QString numberSuffix() const
{ return stringProperty(ListNumberSuffix); }
+ inline void setStart(int indent);
+ inline int start() const { return intProperty(ListStart); }
+
protected:
explicit QTextListFormat(const QTextFormat &fmt);
friend class QTextFormat;
@@ -771,6 +776,11 @@ inline void QTextListFormat::setNumberPrefix(const QString &np)
inline void QTextListFormat::setNumberSuffix(const QString &ns)
{ setProperty(ListNumberSuffix, ns); }
+inline void QTextListFormat::setStart(int astart)
+{
+ setProperty(ListStart, astart);
+}
+
class Q_GUI_EXPORT QTextImageFormat : public QTextCharFormat
{
public:
diff --git a/src/gui/text/qtextformat_p.h b/src/gui/text/qtextformat_p.h
index fac7082c1d..1bd9120513 100644
--- a/src/gui/text/qtextformat_p.h
+++ b/src/gui/text/qtextformat_p.h
@@ -55,7 +55,7 @@ public:
inline QTextImageFormat imageFormat(int index) const
{ return format(index).toImageFormat(); }
- inline int numFormats() const { return formats.count(); }
+ inline int numFormats() const { return formats.size(); }
typedef QList<QTextFormat> FormatVector;
diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp
index ded2dd185a..ee92cece78 100644
--- a/src/gui/text/qtexthtmlparser.cpp
+++ b/src/gui/text/qtexthtmlparser.cpp
@@ -413,17 +413,17 @@ static const QTextHtmlElement elements[Html_NumElements]= {
{ "var", Html_var, QTextHtmlElement::DisplayInline },
};
-static bool operator<(const QString &str, const QTextHtmlElement &e)
+static bool operator<(QStringView str, const QTextHtmlElement &e)
{
return str < QLatin1StringView(e.name);
}
-static bool operator<(const QTextHtmlElement &e, const QString &str)
+static bool operator<(const QTextHtmlElement &e, QStringView str)
{
return QLatin1StringView(e.name) < str;
}
-static const QTextHtmlElement *lookupElementHelper(const QString &element)
+static const QTextHtmlElement *lookupElementHelper(QStringView element)
{
const QTextHtmlElement *start = &elements[0];
const QTextHtmlElement *end = &elements[Html_NumElements];
@@ -433,7 +433,7 @@ static const QTextHtmlElement *lookupElementHelper(const QString &element)
return e;
}
-int QTextHtmlParser::lookupElement(const QString &element)
+int QTextHtmlParser::lookupElement(QStringView element)
{
const QTextHtmlElement *e = lookupElementHelper(element);
if (!e)
@@ -488,7 +488,7 @@ QTextHtmlParserNode *QTextHtmlParser::newNode(int parent)
bool reuseLastNode = true;
- if (nodes.count() == 1) {
+ if (nodes.size() == 1) {
reuseLastNode = false;
} else if (lastNode->tag.isEmpty()) {
@@ -496,7 +496,7 @@ QTextHtmlParserNode *QTextHtmlParser::newNode(int parent)
reuseLastNode = true;
} else { // last node is a text node (empty tag) with some text
- if (lastNode->text.length() == 1 && lastNode->text.at(0).isSpace()) {
+ if (lastNode->text.size() == 1 && lastNode->text.at(0).isSpace()) {
int lastSibling = count() - 2;
while (lastSibling
@@ -543,7 +543,7 @@ void QTextHtmlParser::parse(const QString &text, const QTextDocument *_resourceP
nodes.append(new QTextHtmlParserNode);
txt = text;
pos = 0;
- len = txt.length();
+ len = txt.size();
textEditMode = false;
resourceProvider = _resourceProvider;
parse();
@@ -672,7 +672,7 @@ void QTextHtmlParser::parseTag()
resolveNode();
#ifndef QT_NO_CSSPARSER
- const int nodeIndex = nodes.count() - 1; // this new node is always the last
+ const int nodeIndex = nodes.size() - 1; // this new node is always the last
node->applyCssDeclarations(declarationsForNode(nodeIndex), resourceProvider);
#endif
applyAttributes(node->attributes);
@@ -771,7 +771,7 @@ QString QTextHtmlParser::parseEntity(QStringView entity)
if (!resolved.isNull())
return QString(resolved);
- if (entity.length() > 1 && entity.at(0) == u'#') {
+ if (entity.size() > 1 && entity.at(0) == u'#') {
entity = entity.mid(1); // removing leading #
int base = 10;
@@ -838,7 +838,7 @@ QString QTextHtmlParser::parseWord()
while (pos < len) {
QChar c = txt.at(pos++);
// Allow for escaped single quotes as they may be part of the string
- if (c == u'\'' && (txt.length() > 1 && txt.at(pos - 2) != u'\\'))
+ if (c == u'\'' && (txt.size() > 1 && txt.at(pos - 2) != u'\\'))
break;
else
word += c;
@@ -876,21 +876,21 @@ QTextHtmlParserNode *QTextHtmlParser::resolveParent()
n = at(n).parent;
if (!n) {
- nodes.insert(nodes.count() - 1, new QTextHtmlParserNode);
- nodes.insert(nodes.count() - 1, new QTextHtmlParserNode);
+ nodes.insert(nodes.size() - 1, new QTextHtmlParserNode);
+ nodes.insert(nodes.size() - 1, new QTextHtmlParserNode);
- QTextHtmlParserNode *table = nodes[nodes.count() - 3];
+ QTextHtmlParserNode *table = nodes[nodes.size() - 3];
table->parent = p;
table->id = Html_table;
table->tag = "table"_L1;
- table->children.append(nodes.count() - 2); // add row as child
+ table->children.append(nodes.size() - 2); // add row as child
- QTextHtmlParserNode *row = nodes[nodes.count() - 2];
- row->parent = nodes.count() - 3; // table as parent
+ QTextHtmlParserNode *row = nodes[nodes.size() - 2];
+ row->parent = nodes.size() - 3; // table as parent
row->id = Html_tr;
row->tag = "tr"_L1;
- p = nodes.count() - 2;
+ p = nodes.size() - 2;
node = nodes.last(); // re-initialize pointer
}
}
@@ -901,12 +901,12 @@ QTextHtmlParserNode *QTextHtmlParser::resolveParent()
n = at(n).parent;
if (!n) {
- nodes.insert(nodes.count() - 1, new QTextHtmlParserNode);
- QTextHtmlParserNode *table = nodes[nodes.count() - 2];
+ nodes.insert(nodes.size() - 1, new QTextHtmlParserNode);
+ QTextHtmlParserNode *table = nodes[nodes.size() - 2];
table->parent = p;
table->id = Html_table;
table->tag = "table"_L1;
- p = nodes.count() - 2;
+ p = nodes.size() - 2;
node = nodes.last(); // re-initialize pointer
}
}
@@ -954,7 +954,7 @@ QTextHtmlParserNode *QTextHtmlParser::resolveParent()
node->parent = p;
// makes it easier to traverse the tree, later
- nodes[p]->children.append(nodes.count() - 1);
+ nodes[p]->children.append(nodes.size() - 1);
return node;
}
@@ -1029,7 +1029,7 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent
// set element specific attributes
switch (id) {
case Html_a:
- for (int i = 0; i < attributes.count(); i += 2) {
+ for (int i = 0; i < attributes.size(); i += 2) {
const QString key = attributes.at(i);
if (key.compare("href"_L1, Qt::CaseInsensitive) == 0
&& !attributes.at(i + 1).isEmpty()) {
@@ -1117,7 +1117,7 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent
#ifndef QT_NO_CSSPARSER
void QTextHtmlParserNode::setListStyle(const QList<QCss::Value> &cssValues)
{
- for (int i = 0; i < cssValues.count(); ++i) {
+ for (int i = 0; i < cssValues.size(); ++i) {
if (cssValues.at(i).type == QCss::Value::KnownIdentifier) {
switch (static_cast<QCss::KnownValue>(cssValues.at(i).variant.toInt())) {
case QCss::Value_None: hasOwnListStyle = true; listStyle = QTextListFormat::ListStyleUndefined; break;
@@ -1181,7 +1181,7 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
QCss::ValueExtractor extractor(declarations);
extractor.extractBox(margin, padding);
- if (id == Html_td || id == Html_th) {
+ auto getBorderValues = [&extractor](qreal *borderWidth, QBrush *borderBrush, QTextFrameFormat::BorderStyle *borderStyles) {
QCss::BorderStyle cssStyles[4];
int cssBorder[4];
QSize cssRadii[4]; // unused
@@ -1193,20 +1193,24 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
// QCss::BorderWidth parsing below which expects a single value
// will not work as expected - which in this case does not matter
// because tableBorder is not relevant for cells.
- extractor.extractBorder(cssBorder, tableCellBorderBrush, cssStyles, cssRadii);
+ bool hit = extractor.extractBorder(cssBorder, borderBrush, cssStyles, cssRadii);
for (int i = 0; i < 4; ++i) {
- tableCellBorderStyle[i] = toQTextFrameFormat(cssStyles[i]);
- tableCellBorder[i] = static_cast<qreal>(cssBorder[i]);
+ borderStyles[i] = toQTextFrameFormat(cssStyles[i]);
+ borderWidth[i] = static_cast<qreal>(cssBorder[i]);
}
- }
+ return hit;
+ };
+
+ if (id == Html_td || id == Html_th)
+ getBorderValues(tableCellBorder, tableCellBorderBrush, tableCellBorderStyle);
- for (int i = 0; i < declarations.count(); ++i) {
+ for (int i = 0; i < declarations.size(); ++i) {
const QCss::Declaration &decl = declarations.at(i);
if (decl.d->values.isEmpty()) continue;
QCss::KnownValue identifier = QCss::UnknownValue;
- if (decl.d->values.first().type == QCss::Value::KnownIdentifier)
- identifier = static_cast<QCss::KnownValue>(decl.d->values.first().variant.toInt());
+ if (decl.d->values.constFirst().type == QCss::Value::KnownIdentifier)
+ identifier = static_cast<QCss::KnownValue>(decl.d->values.constFirst().variant.toInt());
switch (decl.d->propertyId) {
case QCss::BorderColor: borderBrush = QBrush(decl.colorValue()); break;
@@ -1220,6 +1224,19 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
tableBorder = borders[0];
}
break;
+ case QCss::Border: {
+ qreal tblBorder[4];
+ QBrush tblBorderBrush[4];
+ QTextFrameFormat::BorderStyle tblBorderStyle[4];
+ if (getBorderValues(tblBorder, tblBorderBrush, tblBorderStyle)) {
+ tableBorder = tblBorder[0];
+ if (tblBorderBrush[0].color().isValid())
+ borderBrush = tblBorderBrush[0];
+ if (tblBorderStyle[0] != static_cast<QTextFrameFormat::BorderStyle>(-1))
+ borderStyle = tblBorderStyle[0];
+ }
+ }
+ break;
case QCss::BorderCollapse:
borderCollapse = decl.borderCollapseValue();
break;
@@ -1233,10 +1250,10 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
}
break;
case QCss::QtBlockIndent:
- blockFormat.setIndent(decl.d->values.first().variant.toInt());
+ blockFormat.setIndent(decl.d->values.constFirst().variant.toInt());
break;
case QCss::QtLineHeightType: {
- QString lineHeightTypeName = decl.d->values.first().variant.toString();
+ QString lineHeightTypeName = decl.d->values.constFirst().variant.toString();
QTextBlockFormat::LineHeightTypes lineHeightType;
if (lineHeightTypeName.compare("proportional"_L1, Qt::CaseInsensitive) == 0)
lineHeightType = QTextBlockFormat::ProportionalHeight;
@@ -1265,7 +1282,7 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
lineHeightType = QTextBlockFormat::MinimumHeight;
} else {
bool ok;
- QCss::Value cssValue = decl.d->values.first();
+ QCss::Value cssValue = decl.d->values.constFirst();
QString value = cssValue.toString();
lineHeight = value.toDouble(&ok);
if (ok) {
@@ -1297,19 +1314,19 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
hasCssListIndent = true;
break;
case QCss::QtParagraphType:
- if (decl.d->values.first().variant.toString().compare("empty"_L1, Qt::CaseInsensitive) == 0)
+ if (decl.d->values.constFirst().variant.toString().compare("empty"_L1, Qt::CaseInsensitive) == 0)
isEmptyParagraph = true;
break;
case QCss::QtTableType:
- if (decl.d->values.first().variant.toString().compare("frame"_L1, Qt::CaseInsensitive) == 0)
+ if (decl.d->values.constFirst().variant.toString().compare("frame"_L1, Qt::CaseInsensitive) == 0)
isTextFrame = true;
- else if (decl.d->values.first().variant.toString().compare("root"_L1, Qt::CaseInsensitive) == 0) {
+ else if (decl.d->values.constFirst().variant.toString().compare("root"_L1, Qt::CaseInsensitive) == 0) {
isTextFrame = true;
isRootFrame = true;
}
break;
case QCss::QtUserState:
- userState = decl.d->values.first().variant.toInt();
+ userState = decl.d->values.constFirst().variant.toInt();
break;
case QCss::Whitespace:
switch (identifier) {
@@ -1363,10 +1380,10 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
setListStyle(decl.d->values);
break;
case QCss::QtListNumberPrefix:
- textListNumberPrefix = decl.d->values.first().variant.toString();
+ textListNumberPrefix = decl.d->values.constFirst().variant.toString();
break;
case QCss::QtListNumberSuffix:
- textListNumberSuffix = decl.d->values.first().variant.toString();
+ textListNumberSuffix = decl.d->values.constFirst().variant.toString();
break;
case QCss::TextAlignment:
switch (identifier) {
@@ -1381,12 +1398,36 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
{
if (resourceProvider != nullptr && QTextDocumentPrivate::get(resourceProvider) != nullptr) {
bool ok;
- qint64 searchKey = decl.d->values.first().variant.toLongLong(&ok);
+ qint64 searchKey = decl.d->values.constFirst().variant.toLongLong(&ok);
if (ok)
applyForegroundImage(searchKey, resourceProvider);
}
break;
}
+ case QCss::QtStrokeColor:
+ {
+ QPen pen = charFormat.textOutline();
+ pen.setStyle(Qt::SolidLine);
+ pen.setColor(decl.colorValue());
+ charFormat.setTextOutline(pen);
+ break;
+ }
+ case QCss::QtStrokeWidth:
+ {
+ qreal width;
+ if (decl.realValue(&width, "px")) {
+ QPen pen = charFormat.textOutline();
+ pen.setWidthF(width);
+ charFormat.setTextOutline(pen);
+ }
+ break;
+ }
+ case QCss::QtForeground:
+ {
+ QBrush brush = decl.brushValue();
+ charFormat.setForeground(brush);
+ break;
+ }
default: break;
}
}
@@ -1487,7 +1528,7 @@ void QTextHtmlParserNode::applyBackgroundImage(const QString &url, const QTextDo
bool QTextHtmlParserNode::hasOnlyWhitespace() const
{
- for (int i = 0; i < text.length(); ++i)
+ for (int i = 0; i < text.size(); ++i)
if (!text.at(i).isSpace() || text.at(i) == QChar::LineSeparator)
return false;
return true;
@@ -1537,7 +1578,7 @@ void QTextHtmlParserNode::parseStyleAttribute(const QString &value, const QTextD
QCss::Parser parser(css);
QCss::StyleSheet sheet;
parser.parse(&sheet, Qt::CaseInsensitive);
- if (sheet.styleRules.count() != 1) return;
+ if (sheet.styleRules.size() != 1) return;
applyCssDeclarations(sheet.styleRules.at(0).declarations, resourceProvider);
}
#endif
@@ -1575,12 +1616,12 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes)
QString linkHref;
QString linkType;
- if (attributes.count() % 2 == 1)
+ if (attributes.size() % 2 == 1)
return;
QTextHtmlParserNode *node = nodes.last();
- for (int i = 0; i < attributes.count(); i += 2) {
+ for (int i = 0; i < attributes.size(); i += 2) {
QString key = attributes.at(i);
QString value = attributes.at(i + 1);
@@ -1594,10 +1635,9 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes)
node->charFormat.setProperty(QTextFormat::FontSizeAdjustment, n);
} else if (key == "face"_L1) {
if (value.contains(u',')) {
- const QStringList values = value.split(u',');
QStringList families;
- for (const QString &family : values)
- families << family.trimmed();
+ for (auto family : value.tokenize(u','))
+ families << family.trimmed().toString();
node->charFormat.setFontFamilies(families);
} else {
node->charFormat.setFontFamilies(QStringList(value));
@@ -1634,6 +1674,8 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes)
else if (value == "none"_L1)
node->listStyle = QTextListFormat::ListStyleUndefined;
}
+ } else if (key == "start"_L1) {
+ setIntAttribute(&node->listStart, value);
}
break;
case Html_li:
@@ -1696,7 +1738,8 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes)
}
break;
case Html_table:
- if (key == "border"_L1) {
+ // If table border already set through css style, prefer that one otherwise consider this value
+ if (key == "border"_L1 && !node->tableBorder) {
setFloatAttribute(&node->tableBorder, value);
} else if (key == "bgcolor"_L1) {
QColor c = QColor::fromString(value);
@@ -1900,7 +1943,7 @@ void QTextHtmlStyleSelector::freeNode(NodePtr) const
void QTextHtmlParser::resolveStyleSheetImports(const QCss::StyleSheet &sheet)
{
- for (int i = 0; i < sheet.importRules.count(); ++i) {
+ for (int i = 0; i < sheet.importRules.size(); ++i) {
const QCss::ImportRule &rule = sheet.importRules.at(i);
if (rule.media.isEmpty() || rule.media.contains("screen"_L1, Qt::CaseInsensitive))
importStyleSheet(rule.href);
@@ -1911,7 +1954,7 @@ void QTextHtmlParser::importStyleSheet(const QString &href)
{
if (!resourceProvider)
return;
- for (int i = 0; i < externalStyleSheets.count(); ++i)
+ for (int i = 0; i < externalStyleSheets.size(); ++i)
if (externalStyleSheets.at(i).url == href)
return;
@@ -1942,7 +1985,7 @@ QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &n
case Html_u: {
bool needsUnderline = (node.id == Html_u) ? true : false;
if (node.id == Html_a) {
- for (int i = 0; i < node.attributes.count(); i += 2) {
+ for (int i = 0; i < node.attributes.size(); i += 2) {
const QString key = node.attributes.at(i);
if (key.compare("href"_L1, Qt::CaseInsensitive) == 0
&& !node.attributes.at(i + 1).isEmpty()) {
@@ -2078,7 +2121,7 @@ QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &n
decl.d->propertyId = QCss::FontFamily;
QList<QCss::Value> values;
val.type = QCss::Value::String;
- val.variant = QFontDatabase::systemFont(QFontDatabase::FixedFont).families().first();
+ val.variant = QFontDatabase::systemFont(QFontDatabase::FixedFont).families().constFirst();
values << val;
decl.d->values = values;
decl.d->inheritable = true;
@@ -2117,15 +2160,15 @@ QList<QCss::Declaration> QTextHtmlParser::declarationsForNode(int node) const
int idx = 0;
selector.styleSheets.resize((resourceProvider ? 1 : 0)
- + externalStyleSheets.count()
- + inlineStyleSheets.count());
+ + externalStyleSheets.size()
+ + inlineStyleSheets.size());
if (resourceProvider)
selector.styleSheets[idx++] = QTextDocumentPrivate::get(resourceProvider)->parsedDefaultStyleSheet;
- for (int i = 0; i < externalStyleSheets.count(); ++i, ++idx)
+ for (int i = 0; i < externalStyleSheets.size(); ++i, ++idx)
selector.styleSheets[idx] = externalStyleSheets.at(i).sheet;
- for (int i = 0; i < inlineStyleSheets.count(); ++i, ++idx)
+ for (int i = 0; i < inlineStyleSheets.size(); ++i, ++idx)
selector.styleSheets[idx] = inlineStyleSheets.at(i);
selector.medium = resourceProvider ? resourceProvider->metaInformation(QTextDocument::CssMedia) : "screen"_L1;
diff --git a/src/gui/text/qtexthtmlparser_p.h b/src/gui/text/qtexthtmlparser_p.h
index 5eb2e0683a..dd52baa23e 100644
--- a/src/gui/text/qtexthtmlparser_p.h
+++ b/src/gui/text/qtexthtmlparser_p.h
@@ -26,7 +26,9 @@
#include "private/qtextformat_p.h"
#include "private/qtextdocument_p.h"
+#if QT_CONFIG(cssparser)
#include "private/qcssparser_p.h"
+#endif
#ifndef QT_NO_TEXTHTMLPARSER
@@ -148,6 +150,7 @@ struct QTextHtmlParserNode {
uint displayMode : 3; // QTextHtmlElement::DisplayMode
uint hasHref : 1;
QTextListFormat::Style listStyle;
+ int listStart = 1;
QString textListNumberPrefix;
QString textListNumberSuffix;
QString imageName;
@@ -252,8 +255,8 @@ public:
inline const QTextHtmlParserNode &at(int i) const { return *nodes.at(i); }
inline QTextHtmlParserNode &operator[](int i) { return *nodes[i]; }
- inline int count() const { return nodes.count(); }
- inline int last() const { return nodes.count()-1; }
+ inline int count() const { return nodes.size(); }
+ inline int last() const { return nodes.size()-1; }
int depth(int i) const;
int topMargin(int i) const;
int bottomMargin(int i) const;
@@ -273,7 +276,7 @@ public:
void parse(const QString &text, const QTextDocument *resourceProvider);
- static int lookupElement(const QString &element);
+ static int lookupElement(QStringView element);
Q_GUI_EXPORT static QString parseEntity(QStringView entity);
diff --git a/src/gui/text/qtextimagehandler.cpp b/src/gui/text/qtextimagehandler.cpp
index b21ce6f2f9..5c56c30711 100644
--- a/src/gui/text/qtextimagehandler.cpp
+++ b/src/gui/text/qtextimagehandler.cpp
@@ -17,142 +17,94 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-extern QString qt_findAtNxFile(const QString &baseFileName, qreal targetDevicePixelRatio,
- qreal *sourceDevicePixelRatio);
+static inline QString findAtNxFileOrResource(const QString &baseFileName,
+ qreal targetDevicePixelRatio,
+ qreal *sourceDevicePixelRatio)
+{
+ // qt_findAtNxFile expects a file name that can be tested with QFile::exists.
+ // so if the format.name() is a file:/ or qrc:/ URL, then we need to strip away the schema.
+ QString localFile;
+ const QUrl url(baseFileName);
+ if (url.isLocalFile())
+ localFile = url.toLocalFile();
+ else if (baseFileName.startsWith("qrc:/"_L1))
+ localFile = baseFileName.sliced(3);
+ else
+ localFile = baseFileName;
+ extern QString qt_findAtNxFile(const QString &baseFileName, qreal targetDevicePixelRatio,
+ qreal *sourceDevicePixelRatio);
+ return qt_findAtNxFile(localFile, targetDevicePixelRatio, sourceDevicePixelRatio);
+}
static inline QUrl fromLocalfileOrResources(QString path)
{
if (path.startsWith(":/"_L1)) // auto-detect resources and convert them to url
- path.prepend("qrc"_L1);
+ path = path.prepend("qrc"_L1);
return QUrl(path);
}
-static QPixmap getPixmap(QTextDocument *doc, const QTextImageFormat &format, const qreal devicePixelRatio = 1.0)
+template<typename T>
+static T getAs(QTextDocument *doc, const QTextImageFormat &format, const qreal devicePixelRatio = 1.0)
{
qreal sourcePixelRatio = 1.0;
- const QString name = qt_findAtNxFile(format.name(), devicePixelRatio, &sourcePixelRatio);
+ const QString name = findAtNxFileOrResource(format.name(), devicePixelRatio, &sourcePixelRatio);
const QUrl url = fromLocalfileOrResources(name);
- QPixmap pm;
const QVariant data = doc->resource(QTextDocument::ImageResource, url);
- if (data.userType() == QMetaType::QPixmap || data.userType() == QMetaType::QImage) {
- pm = qvariant_cast<QPixmap>(data);
- } else if (data.userType() == QMetaType::QByteArray) {
- pm.loadFromData(data.toByteArray());
- }
-
- if (pm.isNull()) {
- QImage img;
- if (name.isEmpty() || !img.load(name))
- return QPixmap(":/qt-project.org/styles/commonstyle/images/file-16.png"_L1);
-
- pm = QPixmap::fromImage(img);
- doc->addResource(QTextDocument::ImageResource, url, pm);
+ T result;
+ if (data.userType() == QMetaType::QPixmap || data.userType() == QMetaType::QImage)
+ result = data.value<T>();
+ else if (data.metaType() == QMetaType::fromType<QByteArray>())
+ result.loadFromData(data.toByteArray());
+
+ if (result.isNull()) {
+ if (name.isEmpty() || !result.load(name))
+ return T(":/qt-project.org/styles/commonstyle/images/file-16.png"_L1);
+ doc->addResource(QTextDocument::ImageResource, url, result);
}
- if (name.contains("@2x"_L1))
- pm.setDevicePixelRatio(sourcePixelRatio);
-
- return pm;
+ if (sourcePixelRatio != 1.0)
+ result.setDevicePixelRatio(sourcePixelRatio);
+ return result;
}
-static QSize getPixmapSize(QTextDocument *doc, const QTextImageFormat &format)
+template<typename T>
+static QSize getSize(QTextDocument *doc, const QTextImageFormat &format)
{
- QPixmap pm;
-
const bool hasWidth = format.hasProperty(QTextFormat::ImageWidth);
const int width = qRound(format.width());
const bool hasHeight = format.hasProperty(QTextFormat::ImageHeight);
const int height = qRound(format.height());
+ T source;
QSize size(width, height);
if (!hasWidth || !hasHeight) {
- pm = getPixmap(doc, format);
- const QSizeF pmSize = pm.deviceIndependentSize();
+ source = getAs<T>(doc, format);
+ const QSizeF sourceSize = source.deviceIndependentSize();
if (!hasWidth) {
if (!hasHeight)
- size.setWidth(pmSize.width());
+ size.setWidth(sourceSize.width());
else
- size.setWidth(qRound(height * (pmSize.width() / (qreal) pmSize.height())));
+ size.setWidth(qRound(height * (sourceSize.width() / qreal(sourceSize.height()))));
}
if (!hasHeight) {
if (!hasWidth)
- size.setHeight(pmSize.height());
+ size.setHeight(sourceSize.height());
else
- size.setHeight(qRound(width * (pmSize.height() / (qreal) pmSize.width())));
+ size.setHeight(qRound(width * (sourceSize.height() / qreal(sourceSize.width()))));
}
}
qreal scale = 1.0;
QPaintDevice *pdev = doc->documentLayout()->paintDevice();
if (pdev) {
- if (pm.isNull())
- pm = getPixmap(doc, format);
- if (!pm.isNull())
- scale = qreal(pdev->logicalDpiY()) / qreal(qt_defaultDpi());
- }
- size *= scale;
-
- return size;
-}
-
-static QImage getImage(QTextDocument *doc, const QTextImageFormat &format, const qreal devicePixelRatio = 1.0)
-{
- qreal sourcePixelRatio = 1.0;
- const QString name = qt_findAtNxFile(format.name(), devicePixelRatio, &sourcePixelRatio);
- const QUrl url = fromLocalfileOrResources(name);
-
- QImage image;
- const QVariant data = doc->resource(QTextDocument::ImageResource, url);
- if (data.userType() == QMetaType::QImage) {
- image = qvariant_cast<QImage>(data);
- } else if (data.userType() == QMetaType::QByteArray) {
- image.loadFromData(data.toByteArray());
- }
-
- if (image.isNull()) {
- if (name.isEmpty() || !image.load(name))
- return QImage(":/qt-project.org/styles/commonstyle/images/file-16.png"_L1);
-
- doc->addResource(QTextDocument::ImageResource, url, image);
- }
-
- if (sourcePixelRatio != 1.0)
- image.setDevicePixelRatio(sourcePixelRatio);
-
- return image;
-}
-
-static QSize getImageSize(QTextDocument *doc, const QTextImageFormat &format)
-{
- QImage image;
-
- const bool hasWidth = format.hasProperty(QTextFormat::ImageWidth);
- const int width = qRound(format.width());
- const bool hasHeight = format.hasProperty(QTextFormat::ImageHeight);
- const int height = qRound(format.height());
-
- QSize size(width, height);
- if (!hasWidth || !hasHeight) {
- image = getImage(doc, format);
- QSizeF imageSize = image.deviceIndependentSize();
- if (!hasWidth)
- size.setWidth(imageSize.width());
- if (!hasHeight)
- size.setHeight(imageSize.height());
- }
-
- qreal scale = 1.0;
- QPaintDevice *pdev = doc->documentLayout()->paintDevice();
- if (pdev) {
- if (image.isNull())
- image = getImage(doc, format);
- if (!image.isNull())
+ if (source.isNull())
+ source = getAs<T>(doc, format);
+ if (!source.isNull())
scale = qreal(pdev->logicalDpiY()) / qreal(qt_defaultDpi());
}
size *= scale;
-
return size;
}
@@ -167,15 +119,15 @@ QSizeF QTextImageHandler::intrinsicSize(QTextDocument *doc, int posInDocument, c
const QTextImageFormat imageFormat = format.toImageFormat();
if (QCoreApplication::instance()->thread() != QThread::currentThread())
- return getImageSize(doc, imageFormat);
- return getPixmapSize(doc, imageFormat);
+ return getSize<QImage>(doc, imageFormat);
+ return getSize<QPixmap>(doc, imageFormat);
}
QImage QTextImageHandler::image(QTextDocument *doc, const QTextImageFormat &imageFormat)
{
Q_ASSERT(doc != nullptr);
- return getImage(doc, imageFormat);
+ return getAs<QImage>(doc, imageFormat);
}
void QTextImageHandler::drawObject(QPainter *p, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format)
@@ -184,10 +136,10 @@ void QTextImageHandler::drawObject(QPainter *p, const QRectF &rect, QTextDocumen
const QTextImageFormat imageFormat = format.toImageFormat();
if (QCoreApplication::instance()->thread() != QThread::currentThread()) {
- const QImage image = getImage(doc, imageFormat, p->device()->devicePixelRatio());
+ const QImage image = getAs<QImage>(doc, imageFormat, p->device()->devicePixelRatio());
p->drawImage(rect, image, image.rect());
} else {
- const QPixmap pixmap = getPixmap(doc, imageFormat, p->device()->devicePixelRatio());
+ const QPixmap pixmap = getAs<QPixmap>(doc, imageFormat, p->device()->devicePixelRatio());
p->drawPixmap(rect, pixmap, pixmap.rect());
}
}
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
index eb71b46526..f0c7dd24e5 100644
--- a/src/gui/text/qtextlayout.cpp
+++ b/src/gui/text/qtextlayout.cpp
@@ -265,6 +265,10 @@ Qt::LayoutDirection QTextInlineObject::textDirection() const
The text can then be rendered by calling the layout's draw() function:
\snippet code/src_gui_text_qtextlayout.cpp 1
+ It is also possible to draw each line individually, for instance to draw
+ the last line that fits into a widget elided:
+ \snippet code/src_gui_text_qtextlayout.cpp elided
+
For a given position in the text you can find a valid cursor position with
isValidCursorPosition(), nextCursorPosition(), and previousCursorPosition().
@@ -282,6 +286,26 @@ Qt::LayoutDirection QTextInlineObject::textDirection() const
*/
/*!
+ \enum QTextLayout::GlyphRunRetrievalFlag
+ \since 6.5
+
+ GlyphRunRetrievalFlag specifies flags passed to the glyphRuns() functions to determine
+ which properties of the layout are returned in the QGlyphRun objects. Since each property
+ will consume memory and may require additional allocations, it is a good practice to only
+ request the properties you will need to access later.
+
+ \value RetrieveGlyphIndexes Retrieves the indexes in the font which correspond to the glyphs.
+ \value RetrieveGlyphPositions Retrieves the relative positions of the glyphs in the layout.
+ \value RetrieveStringIndexes Retrieves the indexes in the original string that correspond to
+ each of the glyphs.
+ \value RetrieveString Retrieves the original source string from the layout.
+ \value RetrieveAll Retrieves all available properties of the layout.
+ \omitvalue DefaultRetrievalFlags
+
+ \sa glyphRuns(), QTextLine::glyphRuns()
+*/
+
+/*!
\fn QTextEngine *QTextLayout::engine() const
\internal
@@ -627,8 +651,8 @@ int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
{
const QCharAttributes *attributes = d->attributes();
int len = d->block.isValid() ? d->block.length() - 1
- : d->layoutData->string.length();
- Q_ASSERT(len <= d->layoutData->string.length());
+ : d->layoutData->string.size();
+ Q_ASSERT(len <= d->layoutData->string.size());
if (!attributes || oldPos < 0 || oldPos >= len)
return oldPos;
@@ -663,8 +687,8 @@ int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const
{
const QCharAttributes *attributes = d->attributes();
int len = d->block.isValid() ? d->block.length() - 1
- : d->layoutData->string.length();
- Q_ASSERT(len <= d->layoutData->string.length());
+ : d->layoutData->string.size();
+ Q_ASSERT(len <= d->layoutData->string.size());
if (!attributes || oldPos <= 0 || oldPos > len)
return oldPos;
@@ -717,7 +741,7 @@ int QTextLayout::leftCursorPosition(int oldPos) const
return newPos;
}
-/*!/
+/*!
Returns \c true if position \a pos is a valid cursor position.
In a Unicode context some positions in the text are not valid
@@ -735,7 +759,7 @@ int QTextLayout::leftCursorPosition(int oldPos) const
bool QTextLayout::isValidCursorPosition(int pos) const
{
const QCharAttributes *attributes = d->attributes();
- if (!attributes || pos < 0 || pos > (int)d->layoutData->string.length())
+ if (!attributes || pos < 0 || pos > (int)d->layoutData->string.size())
return false;
return attributes[pos].graphemeBoundary;
}
@@ -776,7 +800,7 @@ QTextLine QTextLayout::createLine()
}
}
int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length + d->lines.at(l-1).trailingSpaces : 0;
- int strlen = d->layoutData->string.length();
+ int strlen = d->layoutData->string.size();
if (l && from >= strlen) {
if (!d->lines.at(l-1).length || d->layoutData->string.at(strlen - 1) != QChar::LineSeparator)
return QTextLine();
@@ -961,7 +985,9 @@ static inline QRectF clipIfValid(const QRectF &rect, const QRectF &clip)
}
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
/*!
+ \overload
Returns the glyph indexes and positions for all glyphs corresponding to the \a length characters
starting at the position \a from in this QTextLayout. This is an expensive function, and should
not be called in a time sensitive context.
@@ -969,27 +995,57 @@ static inline QRectF clipIfValid(const QRectF &rect, const QRectF &clip)
If \a from is less than zero, then the glyph run will begin at the first character in the
layout. If \a length is less than zero, it will span the entire string from the start position.
+ \note This is equivalent to calling
+ glyphRuns(from,
+ length,
+ QTextLayout::GlyphRunRetrievalFlag::GlyphIndexes |
+ QTextLayout::GlyphRunRetrievalFlag::GlyphPositions).
+
\since 4.8
\sa draw(), QPainter::drawGlyphRun()
*/
-#if !defined(QT_NO_RAWFONT)
+# if !defined(QT_NO_RAWFONT)
QList<QGlyphRun> QTextLayout::glyphRuns(int from, int length) const
{
+ return glyphRuns(from, length, QTextLayout::GlyphRunRetrievalFlag::DefaultRetrievalFlags);
+}
+# endif
+#endif
+
+/*!
+ \overload
+ Returns the glyph indexes and positions for all glyphs corresponding to the \a length characters
+ starting at the position \a from in this QTextLayout. This is an expensive function, and should
+ not be called in a time sensitive context.
+
+ If \a from is less than zero, then the glyph run will begin at the first character in the
+ layout. If \a length is less than zero, it will span the entire string from the start position.
+
+ The \a retrievalFlags specifies which properties of the QGlyphRun will be retrieved from the
+ layout. To minimize allocations and memory consumption, this should be set to include only the
+ properties that you need to access later.
+
+ \since 6.5
+ \sa draw(), QPainter::drawGlyphRun()
+*/
+#if !defined(QT_NO_RAWFONT)
+QList<QGlyphRun> QTextLayout::glyphRuns(int from,
+ int length,
+ QTextLayout::GlyphRunRetrievalFlags retrievalFlags) const
+{
if (from < 0)
from = 0;
if (length < 0)
- length = text().length();
+ length = text().size();
QHash<QPair<QFontEngine *, int>, QGlyphRun> glyphRunHash;
for (int i=0; i<d->lines.size(); ++i) {
if (d->lines.at(i).from > from + length)
break;
- else if (d->lines.at(i).from + d->lines[i].length >= from) {
- QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length);
-
- for (int j = 0; j < glyphRuns.size(); j++) {
- const QGlyphRun &glyphRun = glyphRuns.at(j);
+ else if (d->lines.at(i).from + d->lines.at(i).length >= from) {
+ const QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length, retrievalFlags);
+ for (const QGlyphRun &glyphRun : glyphRuns) {
QRawFont rawFont = glyphRun.rawFont();
QFontEngine *fontEngine = rawFont.d->fontEngine;
@@ -1002,14 +1058,17 @@ QList<QGlyphRun> QTextLayout::glyphRuns(int from, int length) const
} else {
QList<quint32> indexes = oldGlyphRun.glyphIndexes();
QList<QPointF> positions = oldGlyphRun.positions();
+ QList<qsizetype> stringIndexes = oldGlyphRun.stringIndexes();
QRectF boundingRect = oldGlyphRun.boundingRect();
indexes += glyphRun.glyphIndexes();
positions += glyphRun.positions();
+ stringIndexes += glyphRun.stringIndexes();
boundingRect = boundingRect.united(glyphRun.boundingRect());
oldGlyphRun.setGlyphIndexes(indexes);
oldGlyphRun.setPositions(positions);
+ oldGlyphRun.setStringIndexes(stringIndexes);
oldGlyphRun.setBoundingRect(boundingRect);
}
}
@@ -1045,7 +1104,6 @@ void QTextLayout::draw(QPainter *p, const QPointF &pos, const QList<FormatRange>
int firstLine = 0;
int lastLine = d->lines.size();
for (int i = 0; i < d->lines.size(); ++i) {
- QTextLine l(i, d);
const QScriptLine &sl = d->lines.at(i);
if (sl.y > clipe) {
@@ -1232,7 +1290,7 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
QPointF position = pos + d->position;
- cursorPosition = qBound(0, cursorPosition, d->layoutData->string.length());
+ cursorPosition = qBound(0, cursorPosition, d->layoutData->string.size());
int line = d->lineNumberForTextPosition(cursorPosition);
if (line < 0)
line = 0;
@@ -1246,7 +1304,6 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
QFixed base = sl.base();
QFixed descent = sl.descent;
- QFixed cursorDescent = descent;
bool rightToLeft = d->isRightToLeft();
const int realCursorPosition = cursorPosition;
@@ -1266,7 +1323,7 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
int neighborItem = itm;
if (neighborItem > 0 && si->position == realCursorPosition)
--neighborItem;
- else if (neighborItem < d->layoutData->items.count() - 1 && si->position + si->num_glyphs == realCursorPosition)
+ else if (neighborItem < d->layoutData->items.size() - 1 && si->position + si->num_glyphs == realCursorPosition)
++neighborItem;
const bool onBoundary = neighborItem != itm
&& si->analysis.bidiLevel != d->layoutData->items[neighborItem].analysis.bidiLevel;
@@ -1275,15 +1332,16 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
si = &d->layoutData->items[itm];
}
}
- if (si->ascent >= 0)
- base = si->ascent;
- if (si->descent == 0)
- descent = si->descent;
- else if (si->descent > 0 && si->descent < descent)
- cursorDescent = si->descent;
+ // objects need some special treatment as they can have special alignment or be floating
+ if (si->analysis.flags != QScriptAnalysis::Object) {
+ if (si->ascent > 0)
+ base = si->ascent;
+ if (si->descent > 0)
+ descent = si->descent;
+ }
rightToLeft = si->analysis.bidiLevel % 2;
}
- qreal y = position.y() + (sl.y + sl.base() + sl.descent - base - descent).toReal();
+ qreal y = position.y() + (sl.y + sl.base() - base).toReal();
bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
&& (p->transform().type() > QTransform::TxTranslate);
if (toggleAntialiasing)
@@ -1291,7 +1349,20 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
QPainter::CompositionMode origCompositionMode = p->compositionMode();
if (p->paintEngine()->hasFeature(QPaintEngine::RasterOpModes))
p->setCompositionMode(QPainter::RasterOp_NotDestination);
- p->fillRect(QRectF(x, y, qreal(width), (base + cursorDescent).toReal()), p->pen().brush());
+ const QTransform &deviceTransform = p->deviceTransform();
+ const qreal xScale = deviceTransform.m11();
+ if (deviceTransform.type() != QTransform::TxScale || std::trunc(xScale) == xScale) {
+ p->fillRect(QRectF(x, y, qreal(width), (base + descent).toReal()), p->pen().brush());
+ } else {
+ // Ensure consistently rendered cursor width under fractional scaling
+ const QPen origPen = p->pen();
+ QPen pen(origPen.brush(), qRound(width * xScale), Qt::SolidLine, Qt::FlatCap);
+ pen.setCosmetic(true);
+ const qreal center = x + qreal(width) / 2;
+ p->setPen(pen);
+ p->drawLine(QPointF(center, y), QPointF(center, qCeil(y + (base + descent).toReal())));
+ p->setPen(origPen);
+ }
p->setCompositionMode(origCompositionMode);
if (toggleAntialiasing)
p->setRenderHint(QPainter::Antialiasing, false);
@@ -1541,13 +1612,10 @@ void QTextLine::setLineWidth(qreal width)
return;
}
- if (width > QFIXED_MAX)
- width = QFIXED_MAX;
-
- line.width = QFixed::fromReal(width);
+ line.width = QFixed::fromReal(qBound(0.0, width, qreal(QFIXED_MAX)));
if (line.length
&& line.textWidth <= line.width
- && line.from + line.length == eng->layoutData->string.length())
+ && line.from + line.length == eng->layoutData->string.size())
// no need to do anything if the line is already layouted and the last one. This optimization helps
// when using things in a single line layout.
return;
@@ -1584,7 +1652,7 @@ void QTextLine::setNumColumns(int numColumns)
void QTextLine::setNumColumns(int numColumns, qreal alignmentWidth)
{
QScriptLine &line = eng->lines[index];
- line.width = QFixed::fromReal(alignmentWidth);
+ line.width = QFixed::fromReal(qBound(0.0, alignmentWidth, qreal(QFIXED_MAX)));
line.length = 0;
line.textWidth = 0;
layout_helper(numColumns);
@@ -1600,23 +1668,18 @@ namespace {
struct LineBreakHelper
{
- LineBreakHelper()
- : glyphCount(0), maxGlyphs(0), currentPosition(0), fontEngine(nullptr), logClusters(nullptr),
- manualWrap(false), whiteSpaceOrObject(true)
- {
- }
-
+ LineBreakHelper() = default;
QScriptLine tmpData;
QScriptLine spaceData;
QGlyphLayout glyphs;
- int glyphCount;
- int maxGlyphs;
- int currentPosition;
- glyph_t previousGlyph;
- QFontEngine *previousGlyphFontEngine;
+ int glyphCount = 0;
+ int maxGlyphs = 0;
+ int currentPosition = 0;
+ glyph_t previousGlyph = 0;
+ QExplicitlySharedDataPointer<QFontEngine> previousGlyphFontEngine;
QFixed minw;
QFixed currentSoftHyphenWidth;
@@ -1624,11 +1687,11 @@ namespace {
QFixed rightBearing;
QFixed minimumRightBearing;
- QFontEngine *fontEngine;
- const unsigned short *logClusters;
+ QExplicitlySharedDataPointer<QFontEngine> fontEngine;
+ const unsigned short *logClusters = nullptr;
- bool manualWrap;
- bool whiteSpaceOrObject;
+ bool manualWrap = false;
+ bool whiteSpaceOrObject = true;
bool checkFullOtherwiseExtend(QScriptLine &line);
@@ -1672,13 +1735,13 @@ namespace {
{
if (currentPosition <= 0)
return;
- calculateRightBearing(fontEngine, currentGlyph());
+ calculateRightBearing(fontEngine.data(), currentGlyph());
}
inline void calculateRightBearingForPreviousGlyph()
{
if (previousGlyph > 0)
- calculateRightBearing(previousGlyphFontEngine, previousGlyph);
+ calculateRightBearing(previousGlyphFontEngine.data(), previousGlyph);
}
static const QFixed RightBearingNotCalculated;
@@ -1699,7 +1762,7 @@ namespace {
}
};
-const QFixed LineBreakHelper::RightBearingNotCalculated = QFixed(1);
+Q_CONSTINIT const QFixed LineBreakHelper::RightBearingNotCalculated = QFixed(1);
inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
{
@@ -1763,12 +1826,12 @@ void QTextLine::layout_helper(int maxGlyphs)
line.textWidth = 0;
line.hasTrailingSpaces = false;
- if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.length()) {
+ if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.size()) {
line.setDefaultHeight(eng);
return;
}
- Q_ASSERT(line.from < eng->layoutData->string.length());
+ Q_ASSERT(line.from < eng->layoutData->string.size());
LineBreakHelper lbh;
@@ -1795,6 +1858,7 @@ void QTextLine::layout_helper(int maxGlyphs)
lbh.logClusters = eng->layoutData->logClustersPtr;
lbh.previousGlyph = 0;
+ bool manuallyWrapped = false;
bool hasInlineObject = false;
QFixed maxInlineObjectHeight = 0;
@@ -1870,6 +1934,7 @@ void QTextLine::layout_helper(int maxGlyphs)
lbh.calculateRightBearingForPreviousGlyph();
}
line += lbh.tmpData;
+ manuallyWrapped = true;
goto found;
} else if (current.analysis.flags == QScriptAnalysis::Object) {
lbh.whiteSpaceOrObject = true;
@@ -1904,12 +1969,10 @@ void QTextLine::layout_helper(int maxGlyphs)
addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
current, lbh.logClusters, lbh.glyphs);
}
-
- if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
- lbh.spaceData.textWidth = line.width; // ignore spaces that fall out of the line.
- goto found;
- }
} else {
+ if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width)
+ goto found;
+
lbh.whiteSpaceOrObject = false;
bool sb_or_ws = false;
lbh.saveCurrentGlyph();
@@ -1923,11 +1986,11 @@ void QTextLine::layout_helper(int maxGlyphs)
// spaces to behave as in previous Qt versions in the line breaking algorithm.
// The line breaks do not currently follow the Unicode specs, but fixing this would
// require refactoring the code and would cause behavioral regressions.
- const bool isBreakableSpace = lbh.currentPosition < eng->layoutData->string.length()
+ const bool isBreakableSpace = lbh.currentPosition < eng->layoutData->string.size()
&& attributes[lbh.currentPosition].whiteSpace
&& eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak;
- if (lbh.currentPosition >= eng->layoutData->string.length()
+ if (lbh.currentPosition >= eng->layoutData->string.size()
|| isBreakableSpace
|| attributes[lbh.currentPosition].lineBreak
|| lbh.tmpData.textWidth >= QFIXED_MAX) {
@@ -2092,12 +2155,17 @@ found:
eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
} else {
eng->minWidth = qMax(eng->minWidth, lbh.minw);
- eng->maxWidth += line.textWidth;
+ if (qAddOverflow(eng->layoutData->currentMaxWidth, line.textWidth, &eng->layoutData->currentMaxWidth))
+ eng->layoutData->currentMaxWidth = QFIXED_MAX;
+ if (!manuallyWrapped) {
+ if (qAddOverflow(eng->layoutData->currentMaxWidth, lbh.spaceData.textWidth, &eng->layoutData->currentMaxWidth))
+ eng->layoutData->currentMaxWidth = QFIXED_MAX;
+ }
+ eng->maxWidth = qMax(eng->maxWidth, eng->layoutData->currentMaxWidth);
+ if (manuallyWrapped)
+ eng->layoutData->currentMaxWidth = 0;
}
- if (line.textWidth > 0 && item < eng->layoutData->items.size())
- eng->maxWidth += lbh.spaceData.textWidth;
-
line.textWidth += trailingSpace;
if (lbh.spaceData.length) {
line.trailingSpaces = lbh.spaceData.length;
@@ -2149,35 +2217,37 @@ int QTextLine::textStart() const
int QTextLine::textLength() const
{
if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
- && eng->block.isValid() && index == eng->lines.count()-1) {
+ && eng->block.isValid() && index == eng->lines.size()-1) {
return eng->lines.at(index).length - 1;
}
return eng->lines.at(index).length + eng->lines.at(index).trailingSpaces;
}
-static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf, const QRectF &r)
+static void drawBackground(QPainter *p, const QTextCharFormat &chf, const QRectF &r)
{
- QBrush c = chf.foreground();
- if (c.style() == Qt::NoBrush) {
- p->setPen(defaultPen);
- }
-
QBrush bg = chf.background();
if (bg.style() != Qt::NoBrush && !chf.property(SuppressBackground).toBool())
p->fillRect(r.toAlignedRect(), bg);
- if (c.style() != Qt::NoBrush) {
- p->setPen(QPen(c, 0));
- }
+}
+static void setPen(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf)
+{
+ QBrush c = chf.foreground();
+ if (c.style() == Qt::NoBrush)
+ p->setPen(defaultPen);
+ else
+ p->setPen(QPen(c, 0));
}
#if !defined(QT_NO_RAWFONT)
static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine,
+ const QString &text,
const QGlyphLayout &glyphLayout,
const QPointF &pos,
const QGlyphRun::GlyphRunFlags &flags,
- const QFixed &selectionX,
- const QFixed &selectionWidth,
+ QTextLayout::GlyphRunRetrievalFlags retrievalFlags,
+ QFixed selectionX,
+ QFixed selectionWidth,
int glyphsStart,
int glyphsEnd,
unsigned short *logClusters,
@@ -2191,14 +2261,15 @@ static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine,
QGlyphRunPrivate *d = QGlyphRunPrivate::get(glyphRun);
int rangeStart = textPosition;
- while (*logClusters != glyphsStart && rangeStart < textPosition + textLength) {
- ++logClusters;
+ int logClusterIndex = 0;
+ while (logClusters[logClusterIndex] != glyphsStart && rangeStart < textPosition + textLength) {
+ ++logClusterIndex;
++rangeStart;
}
int rangeEnd = rangeStart;
- while (*logClusters != glyphsEnd && rangeEnd < textPosition + textLength) {
- ++logClusters;
+ while (logClusters[logClusterIndex] != glyphsEnd && rangeEnd < textPosition + textLength) {
+ ++logClusterIndex;
++rangeEnd;
}
@@ -2231,14 +2302,43 @@ static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine,
qreal minY = 0;
qreal maxY = 0;
QList<quint32> glyphs;
- glyphs.reserve(glyphsArray.size());
+ if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
+ glyphs.reserve(glyphsArray.size());
QList<QPointF> positions;
- positions.reserve(glyphsArray.size());
- for (int i=0; i<glyphsArray.size(); ++i) {
- glyphs.append(glyphsArray.at(i) & 0xffffff);
+ if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
+ positions.reserve(glyphsArray.size());
+ QList<qsizetype> stringIndexes;
+ if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
+ stringIndexes.reserve(glyphsArray.size());
+
+ int nextClusterIndex = 0;
+ int currentClusterIndex = 0;
+ for (int i = 0; i < glyphsArray.size(); ++i) {
+ const int glyphArrayIndex = i + glyphsStart;
+ // Search for the next cluster in the string (or the end of string if there are no
+ // more clusters)
+ if (retrievalFlags & QTextLayout::RetrieveStringIndexes) {
+ if (nextClusterIndex < textLength && logClusters[nextClusterIndex] == glyphArrayIndex) {
+ currentClusterIndex = nextClusterIndex; // Store current cluster
+ while (logClusters[nextClusterIndex] == glyphArrayIndex && nextClusterIndex < textLength)
+ ++nextClusterIndex;
+ }
+
+ // We are now either at end of string (no more clusters) or we are not yet at the
+ // next cluster in glyph array. We fill in current cluster so that there is always one
+ // entry in stringIndexes for each glyph.
+ Q_ASSERT(nextClusterIndex == textLength || logClusters[nextClusterIndex] != glyphArrayIndex);
+ stringIndexes.append(textPosition + currentClusterIndex);
+ }
+
+ if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes) {
+ glyph_t glyphIndex = glyphsArray.at(i) & 0xffffff;
+ glyphs.append(glyphIndex);
+ }
QPointF position = positionsArray.at(i).toPointF() + pos;
- positions.append(position);
+ if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
+ positions.append(position);
if (i == 0) {
maxY = minY = position.y();
@@ -2250,8 +2350,14 @@ static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine,
qreal height = maxY + fontHeight - minY;
- glyphRun.setGlyphIndexes(glyphs);
- glyphRun.setPositions(positions);
+ if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
+ glyphRun.setGlyphIndexes(glyphs);
+ if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
+ glyphRun.setPositions(positions);
+ if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
+ glyphRun.setStringIndexes(stringIndexes);
+ if (retrievalFlags & QTextLayout::RetrieveString)
+ glyphRun.setSourceString(text);
glyphRun.setFlags(flags);
glyphRun.setRawFont(font);
@@ -2261,7 +2367,9 @@ static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine,
return glyphRun;
}
+# if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
/*!
+ \overload
Returns the glyph indexes and positions for all glyphs in this QTextLine for characters
in the range defined by \a from and \a length. The \a from index is relative to the beginning
of the text in the containing QTextLayout, and the range must be within the range of QTextLine
@@ -2270,12 +2378,43 @@ static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine,
If \a from is negative, it will default to textStart(), and if \a length is negative it will
default to the return value of textLength().
+ \note This is equivalent to calling
+ glyphRuns(from,
+ length,
+ QTextLayout::GlyphRunRetrievalFlag::GlyphIndexes |
+ QTextLayout::GlyphRunRetrievalFlag::GlyphPositions).
+
\since 5.0
\sa QTextLayout::glyphRuns()
*/
QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const
{
+ return glyphRuns(from, length, QTextLayout::GlyphRunRetrievalFlag::DefaultRetrievalFlags);
+}
+# endif
+
+/*!
+ Returns the glyph indexes and positions for all glyphs in this QTextLine for characters
+ in the range defined by \a from and \a length. The \a from index is relative to the beginning
+ of the text in the containing QTextLayout, and the range must be within the range of QTextLine
+ as given by functions textStart() and textLength().
+
+ The \a retrievalFlags specifies which properties of the QGlyphRun will be retrieved from the
+ layout. To minimize allocations and memory consumption, this should be set to include only the
+ properties that you need to access later.
+
+ If \a from is negative, it will default to textStart(), and if \a length is negative it will
+ default to the return value of textLength().
+
+ \since 6.5
+
+ \sa QTextLayout::glyphRuns()
+*/
+QList<QGlyphRun> QTextLine::glyphRuns(int from,
+ int length,
+ QTextLayout::GlyphRunRetrievalFlags retrievalFlags) const
+{
const QScriptLine &line = eng->lines.at(index);
if (line.length == 0)
@@ -2347,14 +2486,18 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const
// when we're breaking a RTL script item, since the expected position passed into
// getGlyphPositions() is the left-most edge of the left-most glyph in an RTL run.
if (relativeFrom != (iterator.itemStart - si.position) && !rtl) {
- for (int i=itemGlyphsStart; i<glyphsStart; ++i) {
- QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
- pos.rx() += (glyphLayout.advances[i] + justification).toReal();
+ for (int i = itemGlyphsStart; i < glyphsStart; ++i) {
+ if (!glyphLayout.attributes[i].dontPrint) {
+ QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
+ pos.rx() += (glyphLayout.advances[i] + justification).toReal();
+ }
}
} else if (relativeTo != (iterator.itemEnd - si.position - 1) && rtl) {
- for (int i=itemGlyphsEnd; i>glyphsEnd; --i) {
- QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
- pos.rx() += (glyphLayout.advances[i] + justification).toReal();
+ for (int i = itemGlyphsEnd; i > glyphsEnd; --i) {
+ if (!glyphLayout.attributes[i].dontPrint) {
+ QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
+ pos.rx() += (glyphLayout.advances[i] + justification).toReal();
+ }
}
}
@@ -2391,20 +2534,28 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const
if (start == 0 && startsInsideLigature)
subFlags |= QGlyphRun::SplitLigature;
- glyphRuns.append(glyphRunWithInfo(multiFontEngine->engine(which),
- subLayout,
- pos,
- subFlags,
- x,
- width,
- glyphsStart + start,
- glyphsStart + end,
- logClusters + relativeFrom,
- relativeFrom + si.position,
- relativeTo - relativeFrom + 1));
+ {
+ QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
+ eng->text,
+ subLayout,
+ pos,
+ subFlags,
+ retrievalFlags,
+ x,
+ width,
+ glyphsStart + start,
+ glyphsStart + end,
+ logClusters + relativeFrom,
+ relativeFrom + si.position,
+ relativeTo - relativeFrom + 1);
+ if (!glyphRun.isEmpty())
+ glyphRuns.append(glyphRun);
+ }
for (int i = 0; i < subLayout.numGlyphs; ++i) {
- QFixed justification = QFixed::fromFixed(subLayout.justifications[i].space_18d6);
- pos.rx() += (subLayout.advances[i] + justification).toReal();
+ if (!subLayout.attributes[i].dontPrint) {
+ QFixed justification = QFixed::fromFixed(subLayout.justifications[i].space_18d6);
+ pos.rx() += (subLayout.advances[i] + justification).toReal();
+ }
}
if (rtl)
@@ -2422,9 +2573,11 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const
subFlags |= QGlyphRun::SplitLigature;
QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
+ eng->text,
subLayout,
pos,
subFlags,
+ retrievalFlags,
x,
width,
glyphsStart + start,
@@ -2438,9 +2591,11 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const
if (startsInsideLigature || endsInsideLigature)
flags |= QGlyphRun::SplitLigature;
QGlyphRun glyphRun = glyphRunWithInfo(mainFontEngine,
+ eng->text,
glyphLayout,
pos,
flags,
+ retrievalFlags,
x,
width,
glyphsStart,
@@ -2476,7 +2631,6 @@ void QTextLine::draw_internal(QPainter *p, const QPointF &origPos,
Q_ASSERT(!eng->useRawFont);
#endif
const QScriptLine &line = eng->lines[index];
- QPen pen = p->pen();
bool noText = (selection && selection->format.property(SuppressText).toBool());
@@ -2488,13 +2642,12 @@ void QTextLine::draw_internal(QPainter *p, const QPointF &origPos,
const qreal lineHeight = line.height().toReal();
QRectF r(origPos.x() + line.x.toReal(), origPos.y() + line.y.toReal(),
lineHeight / 2, QFontMetrics(eng->font()).horizontalAdvance(u' '));
- setPenAndDrawBackground(p, QPen(), selection->format, r);
- p->setPen(pen);
+ drawBackground(p, selection->format, r);
}
return;
}
- static QRectF maxFixedRect(-QFIXED_MAX / 2, -QFIXED_MAX / 2, QFIXED_MAX, QFIXED_MAX);
+ Q_CONSTINIT static QRectF maxFixedRect(-QFIXED_MAX / 2, -QFIXED_MAX / 2, QFIXED_MAX, QFIXED_MAX);
const bool xlateToFixedRange = !maxFixedRect.contains(origPos);
QPointF pos;
if (Q_LIKELY(!xlateToFixedRange))
@@ -2502,7 +2655,7 @@ void QTextLine::draw_internal(QPainter *p, const QPointF &origPos,
else
p->translate(origPos);
- QTextLineItemIterator iterator(eng, index, pos, selection);
+
QFixed lineBase = line.base();
eng->clearDecorations();
eng->enableDelayDecorations();
@@ -2512,183 +2665,207 @@ void QTextLine::draw_internal(QPainter *p, const QPointF &origPos,
const QTextFormatCollection *formatCollection = eng->formatCollection();
bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
- while (!iterator.atEnd()) {
- QScriptItem &si = iterator.next();
-
- if (selection && selection->start >= 0 && iterator.isOutsideSelection())
- continue;
-
- if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
- && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
- continue;
- QFixed itemBaseLine = y;
- QFont f = eng->font(si);
- QTextCharFormat format;
- if (formatCollection != nullptr)
- format = formatCollection->defaultTextFormat();
+ auto prepareFormat = [suppressColors, selection, this](QTextCharFormat &format,
+ QScriptItem *si) {
+ format.merge(eng->format(si));
- if (eng->hasFormats() || selection || formatCollection) {
- format.merge(eng->format(&si));
+ if (suppressColors) {
+ format.clearForeground();
+ format.clearBackground();
+ format.clearProperty(QTextFormat::TextUnderlineColor);
+ }
+ if (selection)
+ format.merge(selection->format);
+ };
- if (suppressColors) {
- format.clearForeground();
- format.clearBackground();
- format.clearProperty(QTextFormat::TextUnderlineColor);
- }
- if (selection)
- format.merge(selection->format);
-
- setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
- iterator.itemWidth.toReal(), line.height().toReal()));
-
- const qreal baseLineOffset = format.baselineOffset() / 100.0;
- QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
- if (valign == QTextCharFormat::AlignSuperScript
- || valign == QTextCharFormat::AlignSubScript
- || !qFuzzyIsNull(baseLineOffset))
- {
- QFontEngine *fe = f.d->engineForScript(si.analysis.script);
- QFixed height = fe->ascent() + fe->descent();
- itemBaseLine -= height * QFixed::fromReal(baseLineOffset);
-
- if (valign == QTextCharFormat::AlignSubScript)
- itemBaseLine += height * QFixed::fromReal(format.subScriptBaseline() / 100.0);
- else if (valign == QTextCharFormat::AlignSuperScript)
- itemBaseLine -= height * QFixed::fromReal(format.superScriptBaseline() / 100.0);
+ {
+ QTextLineItemIterator iterator(eng, index, pos, selection);
+ while (!iterator.atEnd()) {
+ QScriptItem &si = iterator.next();
+
+ if (eng->hasFormats() || selection || formatCollection) {
+ QTextCharFormat format;
+ if (formatCollection != nullptr)
+ format = formatCollection->defaultTextFormat();
+ prepareFormat(format, &si);
+ drawBackground(p, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
+ iterator.itemWidth.toReal(), line.height().toReal()));
}
}
+ }
- if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
+ QPen pen = p->pen();
+ {
+ QTextLineItemIterator iterator(eng, index, pos, selection);
+ while (!iterator.atEnd()) {
+ QScriptItem &si = iterator.next();
- if (eng->hasFormats()) {
- p->save();
- if (si.analysis.flags == QScriptAnalysis::Object && QTextDocumentPrivate::get(eng->block)) {
- QFixed itemY = y - si.ascent;
- switch (format.verticalAlignment()) {
- case QTextCharFormat::AlignTop:
- itemY = y - lineBase;
- break;
- case QTextCharFormat::AlignMiddle:
- itemY = y - lineBase + (line.height() - si.height()) / 2;
- break;
- case QTextCharFormat::AlignBottom:
- itemY = y - lineBase + line.height() - si.height();
- break;
- default:
- break;
- }
+ if (selection && selection->start >= 0 && iterator.isOutsideSelection())
+ continue;
- QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
-
- eng->docLayout()->drawInlineObject(p, itemRect,
- QTextInlineObject(iterator.item, eng),
- si.position + eng->block.position(),
- format);
- if (selection) {
- QBrush bg = format.brushProperty(ObjectSelectionBrush);
- if (bg.style() != Qt::NoBrush) {
- QColor c = bg.color();
- c.setAlpha(128);
- p->fillRect(itemRect, c);
- }
- }
- } else { // si.isTab
- QFont f = eng->font(si);
- QTextItemInt gf(si, &f, format);
- gf.chars = nullptr;
- gf.num_chars = 0;
- gf.width = iterator.itemWidth;
- QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng);
- if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
- const QChar visualTab = QChar(QChar::VisualTabCharacter);
- int w = QFontMetrics(f).horizontalAdvance(visualTab);
- qreal x = iterator.itemWidth.toReal() - w; // Right-aligned
- if (x < 0)
- p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
- iterator.itemWidth.toReal(), line.height().toReal()),
- Qt::IntersectClip);
- else
- x /= 2; // Centered
- p->setFont(f);
- p->drawText(QPointF(iterator.x.toReal() + x,
- y.toReal()), visualTab);
- }
+ if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
+ && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
+ continue;
+ QFixed itemBaseLine = y;
+ QFont f = eng->font(si);
+ QTextCharFormat format;
+ if (formatCollection != nullptr)
+ format = formatCollection->defaultTextFormat();
+
+ if (eng->hasFormats() || selection || formatCollection) {
+ prepareFormat(format, &si);
+ setPen(p, pen, format);
+
+ const qreal baseLineOffset = format.baselineOffset() / 100.0;
+ QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
+ if (valign == QTextCharFormat::AlignSuperScript
+ || valign == QTextCharFormat::AlignSubScript
+ || !qFuzzyIsNull(baseLineOffset))
+ {
+ QFontEngine *fe = f.d->engineForScript(si.analysis.script);
+ QFixed height = fe->ascent() + fe->descent();
+ itemBaseLine -= height * QFixed::fromReal(baseLineOffset);
+
+ if (valign == QTextCharFormat::AlignSubScript)
+ itemBaseLine += height * QFixed::fromReal(format.subScriptBaseline() / 100.0);
+ else if (valign == QTextCharFormat::AlignSuperScript)
+ itemBaseLine -= height * QFixed::fromReal(format.superScriptBaseline() / 100.0);
}
- p->restore();
}
- continue;
- }
+ if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
- unsigned short *logClusters = eng->logClusters(&si);
- QGlyphLayout glyphs = eng->shapedGlyphs(&si);
+ if (eng->hasFormats()) {
+ p->save();
+ if (si.analysis.flags == QScriptAnalysis::Object && QTextDocumentPrivate::get(eng->block)) {
+ QFixed itemY = y - si.ascent;
+ switch (format.verticalAlignment()) {
+ case QTextCharFormat::AlignTop:
+ itemY = y - lineBase;
+ break;
+ case QTextCharFormat::AlignMiddle:
+ itemY = y - lineBase + (line.height() - si.height()) / 2;
+ break;
+ case QTextCharFormat::AlignBottom:
+ itemY = y - lineBase + line.height() - si.height();
+ break;
+ default:
+ break;
+ }
- QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart),
- &f, eng->layoutData->string.unicode() + iterator.itemStart,
- iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format);
- gf.logClusters = logClusters + iterator.itemStart - si.position;
- gf.width = iterator.itemWidth;
- gf.justified = line.justified;
- gf.initWithScriptItem(si);
-
- Q_ASSERT(gf.fontEngine);
-
- QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
- if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
- QPainterPath path;
- path.setFillRule(Qt::WindingFill);
-
- if (gf.glyphs.numGlyphs)
- gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
- if (gf.flags) {
- const QFontEngine *fe = gf.fontEngine;
- const qreal lw = fe->lineThickness().toReal();
- if (gf.flags & QTextItem::Underline) {
- qreal offs = fe->underlinePosition().toReal();
- path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
- }
- if (gf.flags & QTextItem::Overline) {
- qreal offs = fe->ascent().toReal() + 1;
- path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
- }
- if (gf.flags & QTextItem::StrikeOut) {
- qreal offs = fe->ascent().toReal() / 3;
- path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
+ QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
+
+ eng->docLayout()->drawInlineObject(p, itemRect,
+ QTextInlineObject(iterator.item, eng),
+ si.position + eng->block.position(),
+ format);
+ if (selection) {
+ QBrush bg = format.brushProperty(ObjectSelectionBrush);
+ if (bg.style() != Qt::NoBrush) {
+ QColor c = bg.color();
+ c.setAlpha(128);
+ p->fillRect(itemRect, c);
+ }
+ }
+ } else { // si.isTab
+ QFont f = eng->font(si);
+ QTextItemInt gf(si, &f, format);
+ gf.chars = nullptr;
+ gf.num_chars = 0;
+ gf.width = iterator.itemWidth;
+ QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng);
+ if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
+ const QChar visualTab = QChar(QChar::VisualTabCharacter);
+ int w = QFontMetrics(f).horizontalAdvance(visualTab);
+ qreal x = iterator.itemWidth.toReal() - w; // Right-aligned
+ if (x < 0)
+ p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
+ iterator.itemWidth.toReal(), line.height().toReal()),
+ Qt::IntersectClip);
+ else
+ x /= 2; // Centered
+ p->setFont(f);
+ p->drawText(QPointF(iterator.x.toReal() + x,
+ y.toReal()), visualTab);
+ }
+
+ }
+ p->restore();
}
+
+ continue;
}
- p->save();
- p->setRenderHint(QPainter::Antialiasing);
- //Currently QPen with a Qt::NoPen style still returns a default
- //QBrush which != Qt::NoBrush so we need this specialcase to reset it
- if (p->pen().style() == Qt::NoPen)
- p->setBrush(Qt::NoBrush);
- else
- p->setBrush(p->pen().brush());
+ unsigned short *logClusters = eng->logClusters(&si);
+ QGlyphLayout glyphs = eng->shapedGlyphs(&si);
- p->setPen(format.textOutline());
- p->drawPath(path);
- p->restore();
- } else {
- if (noText)
- gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be
- QPainterPrivate::get(p)->drawTextItem(pos, gf, eng);
- }
+ QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart),
+ &f, eng->layoutData->string.unicode() + iterator.itemStart,
+ iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format);
+ gf.logClusters = logClusters + iterator.itemStart - si.position;
+ gf.width = iterator.itemWidth;
+ gf.justified = line.justified;
+ gf.initWithScriptItem(si);
+
+ Q_ASSERT(gf.fontEngine);
+
+ QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
+ if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
+ QPainterPath path;
+ path.setFillRule(Qt::WindingFill);
+
+ if (gf.glyphs.numGlyphs)
+ gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
+ if (gf.flags) {
+ const QFontEngine *fe = gf.fontEngine;
+ const qreal lw = fe->lineThickness().toReal();
+ if (gf.flags & QTextItem::Underline) {
+ qreal offs = fe->underlinePosition().toReal();
+ path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
+ }
+ if (gf.flags & QTextItem::Overline) {
+ qreal offs = fe->ascent().toReal() + 1;
+ path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
+ }
+ if (gf.flags & QTextItem::StrikeOut) {
+ qreal offs = fe->ascent().toReal() / 3;
+ path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
+ }
+ }
- if ((si.analysis.flags == QScriptAnalysis::Space
- || si.analysis.flags == QScriptAnalysis::Nbsp)
- && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
- QBrush c = format.foreground();
- if (c.style() != Qt::NoBrush)
- p->setPen(c.color());
- const QChar visualSpace = si.analysis.flags == QScriptAnalysis::Space ? u'\xb7' : u'\xb0';
- QFont oldFont = p->font();
- p->setFont(eng->font(si));
- p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
- p->setPen(pen);
- p->setFont(oldFont);
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+ //Currently QPen with a Qt::NoPen style still returns a default
+ //QBrush which != Qt::NoBrush so we need this specialcase to reset it
+ if (p->pen().style() == Qt::NoPen)
+ p->setBrush(Qt::NoBrush);
+ else
+ p->setBrush(p->pen().brush());
+
+ p->setPen(format.textOutline());
+ p->drawPath(path);
+ p->restore();
+ } else {
+ if (noText)
+ gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be
+ QPainterPrivate::get(p)->drawTextItem(pos, gf, eng);
+ }
+
+ if ((si.analysis.flags == QScriptAnalysis::Space
+ || si.analysis.flags == QScriptAnalysis::Nbsp)
+ && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
+ QBrush c = format.foreground();
+ if (c.style() != Qt::NoBrush)
+ p->setPen(c.color());
+ const QChar visualSpace = si.analysis.flags == QScriptAnalysis::Space ? u'\xb7' : u'\xb0';
+ QFont oldFont = p->font();
+ p->setFont(eng->font(si));
+ p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
+ p->setPen(pen);
+ p->setFont(oldFont);
+ }
}
}
eng->drawDecorations(p);
@@ -2757,7 +2934,7 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
int neighborItem = itm;
if (neighborItem > 0 && scriptItem->position == pos)
--neighborItem;
- else if (neighborItem < eng->layoutData->items.count() - 1 && scriptItem->position + scriptItem->num_glyphs == pos)
+ else if (neighborItem < eng->layoutData->items.size() - 1 && scriptItem->position + scriptItem->num_glyphs == pos)
++neighborItem;
const bool onBoundary = neighborItem != itm && scriptItem->analysis.bidiLevel != eng->layoutData->items[neighborItem].analysis.bidiLevel;
// If we are, prioritise the neighbor item that has the same direction as the engine
@@ -3074,7 +3251,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
// character between lines is a space and we want
// to position the cursor to the left of that
// character.
- if (index < eng->lines.count() - 1)
+ if (index < eng->lines.size() - 1)
pos = qMin(eng->previousLogicalPosition(pos), pos);
return pos;
diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h
index 98054b8902..5b1b64d7ee 100644
--- a/src/gui/text/qtextlayout.h
+++ b/src/gui/text/qtextlayout.h
@@ -69,6 +69,17 @@ class QTextOption;
class Q_GUI_EXPORT QTextLayout
{
public:
+ enum GlyphRunRetrievalFlag : quint16 {
+ RetrieveGlyphIndexes = 0x1,
+ RetrieveGlyphPositions = 0x2,
+ RetrieveStringIndexes = 0x4,
+ RetrieveString = 0x8,
+
+ DefaultRetrievalFlags = RetrieveGlyphIndexes | RetrieveGlyphPositions,
+ RetrieveAll = 0xffff
+ };
+ Q_DECLARE_FLAGS(GlyphRunRetrievalFlags, GlyphRunRetrievalFlag)
+
// does itemization
QTextLayout();
QTextLayout(const QString& text);
@@ -148,7 +159,15 @@ public:
qreal maximumWidth() const;
#if !defined(QT_NO_RAWFONT)
+
+# if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ QList<QGlyphRun> glyphRuns(int from, int length, GlyphRunRetrievalFlags flags) const;
QList<QGlyphRun> glyphRuns(int from = -1, int length = -1) const;
+# else
+ QList<QGlyphRun> glyphRuns(int from = -1,
+ int length = -1,
+ GlyphRunRetrievalFlags flags = DefaultRetrievalFlags) const;
+# endif
#endif
QTextEngine *engine() const { return d; }
@@ -166,7 +185,7 @@ private:
QTextEngine *d;
};
Q_DECLARE_TYPEINFO(QTextLayout::FormatRange, Q_RELOCATABLE_TYPE);
-
+Q_DECLARE_OPERATORS_FOR_FLAGS(QTextLayout::GlyphRunRetrievalFlags)
class Q_GUI_EXPORT QTextLine
{
@@ -219,7 +238,14 @@ public:
void draw(QPainter *painter, const QPointF &position) const;
#if !defined(QT_NO_RAWFONT)
+# if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ QList<QGlyphRun> glyphRuns(int from, int length, QTextLayout::GlyphRunRetrievalFlags flags) const;
QList<QGlyphRun> glyphRuns(int from = -1, int length = -1) const;
+# else
+ QList<QGlyphRun> glyphRuns(int from = -1,
+ int length = -1,
+ QTextLayout::GlyphRunRetrievalFlags flags = QTextLayout::DefaultRetrievalFlags) const;
+# endif
#endif
private:
diff --git a/src/gui/text/qtextlist.cpp b/src/gui/text/qtextlist.cpp
index fc61e54cda..7ec8b6215e 100644
--- a/src/gui/text/qtextlist.cpp
+++ b/src/gui/text/qtextlist.cpp
@@ -86,7 +86,7 @@ QTextList::~QTextList()
int QTextList::count() const
{
Q_D(const QTextList);
- return d->blocks.count();
+ return d->blocks.size();
}
/*!
@@ -145,7 +145,10 @@ QString QTextList::itemText(const QTextBlock &blockIt) const
const int style = format().style();
QString numberPrefix;
- QString numberSuffix = "."_L1;
+ QString numberSuffix = u"."_s;
+
+ // the number of the item might be offset by start, which defaults to 1
+ const int itemNumber = item + format().start() - 1;
if (format().hasProperty(QTextFormat::ListNumberPrefix))
numberPrefix = format().numberPrefix();
@@ -154,15 +157,21 @@ QString QTextList::itemText(const QTextBlock &blockIt) const
switch (style) {
case QTextListFormat::ListDecimal:
- result = QString::number(item);
+ result = QString::number(itemNumber);
break;
// from the old richtext
case QTextListFormat::ListLowerAlpha:
case QTextListFormat::ListUpperAlpha:
{
+ // match the html default behavior of falling back to decimal numbers
+ if (itemNumber < 1) {
+ result = QString::number(itemNumber);
+ break;
+ }
+
const char baseChar = style == QTextListFormat::ListUpperAlpha ? 'A' : 'a';
- int c = item;
+ int c = itemNumber;
while (c > 0) {
c--;
result.prepend(QChar::fromUcs2(baseChar + (c % 26)));
@@ -173,20 +182,21 @@ QString QTextList::itemText(const QTextBlock &blockIt) const
case QTextListFormat::ListLowerRoman:
case QTextListFormat::ListUpperRoman:
{
- if (item < 5000) {
- QByteArray romanNumeral;
+ // match the html default behavior of falling back to decimal numbers
+ if (itemNumber < 1) {
+ result = QString::number(itemNumber);
+ } else if (itemNumber < 5000) {
+ QString romanNumeral;
// works for up to 4999 items
- static const char romanSymbolsLower[] = "iiivixxxlxcccdcmmmm";
- static const char romanSymbolsUpper[] = "IIIVIXXXLXCCCDCMMMM";
- QByteArray romanSymbols; // wrap to have "mid"
+ QLatin1StringView romanSymbols;
if (style == QTextListFormat::ListLowerRoman)
- romanSymbols = QByteArray::fromRawData(romanSymbolsLower, sizeof(romanSymbolsLower));
+ romanSymbols = "iiivixxxlxcccdcmmmm"_L1;
else
- romanSymbols = QByteArray::fromRawData(romanSymbolsUpper, sizeof(romanSymbolsUpper));
+ romanSymbols = "IIIVIXXXLXCCCDCMMMM"_L1;
int c[] = { 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000 };
- int n = item;
+ int n = itemNumber;
for (int i = 12; i >= 0; n %= c[i], i--) {
int q = n / c[i];
if (q > 0) {
@@ -208,13 +218,12 @@ QString QTextList::itemText(const QTextBlock &blockIt) const
numDigits = q;
}
- romanNumeral.append(romanSymbols.mid(startDigit, numDigits));
+ romanNumeral.append(romanSymbols.sliced(startDigit, numDigits));
}
}
- result = QString::fromLatin1(romanNumeral);
- }
- else {
- result = "?"_L1;
+ result = std::move(romanNumeral);
+ } else {
+ result = u"?"_s;
}
}
diff --git a/src/gui/text/qtextmarkdownimporter.cpp b/src/gui/text/qtextmarkdownimporter.cpp
index a839ca9623..e7fcad67b5 100644
--- a/src/gui/text/qtextmarkdownimporter.cpp
+++ b/src/gui/text/qtextmarkdownimporter.cpp
@@ -24,11 +24,14 @@ using namespace Qt::StringLiterals;
Q_LOGGING_CATEGORY(lcMD, "qt.text.markdown")
-static const QChar Newline = u'\n';
-static const QChar Space = u' ';
+static const QChar qtmi_Newline = u'\n';
+static const QChar qtmi_Space = u' ';
+
+static constexpr auto markerString() noexcept { return "---"_L1; }
// TODO maybe eliminate the margins after all views recognize BlockQuoteLevel, CSS can format it, etc.
-static const int BlockQuoteIndent = 40; // pixels, same as in QTextHtmlParserNode::initializeProperties
+static const int qtmi_BlockQuoteIndent =
+ 40; // pixels, same as in QTextHtmlParserNode::initializeProperties
static_assert(int(QTextMarkdownImporter::FeatureCollapseWhitespace) == MD_FLAG_COLLAPSEWHITESPACE);
static_assert(int(QTextMarkdownImporter::FeaturePermissiveATXHeaders) == MD_FLAG_PERMISSIVEATXHEADERS);
@@ -45,7 +48,8 @@ static_assert(int(QTextMarkdownImporter::FeaturePermissiveAutoLinks) == MD_FLAG_
static_assert(int(QTextMarkdownImporter::FeatureTasklists) == MD_FLAG_TASKLISTS);
static_assert(int(QTextMarkdownImporter::FeatureNoHTML) == MD_FLAG_NOHTML);
static_assert(int(QTextMarkdownImporter::DialectCommonMark) == MD_DIALECT_COMMONMARK);
-static_assert(int(QTextMarkdownImporter::DialectGitHub) == (MD_DIALECT_GITHUB | MD_FLAG_UNDERLINE));
+static_assert(int(QTextMarkdownImporter::DialectGitHub) ==
+ (MD_DIALECT_GITHUB | MD_FLAG_UNDERLINE | QTextMarkdownImporter::FeatureFrontMatter));
// --------------------------------------------------------
// MD4C callback function wrappers
@@ -103,18 +107,19 @@ static Qt::Alignment MdAlignment(MD_ALIGN a, Qt::Alignment defaultAlignment = Qt
}
}
-QTextMarkdownImporter::QTextMarkdownImporter(QTextMarkdownImporter::Features features)
- : m_monoFont(QFontDatabase::systemFont(QFontDatabase::FixedFont))
+QTextMarkdownImporter::QTextMarkdownImporter(QTextDocument *doc, QTextMarkdownImporter::Features features)
+ : m_cursor(doc)
+ , m_monoFont(QFontDatabase::systemFont(QFontDatabase::FixedFont))
, m_features(features)
{
}
-QTextMarkdownImporter::QTextMarkdownImporter(QTextDocument::MarkdownFeatures features)
- : QTextMarkdownImporter(static_cast<QTextMarkdownImporter::Features>(int(features)))
+QTextMarkdownImporter::QTextMarkdownImporter(QTextDocument *doc, QTextDocument::MarkdownFeatures features)
+ : QTextMarkdownImporter(doc, static_cast<QTextMarkdownImporter::Features>(int(features)))
{
}
-void QTextMarkdownImporter::import(QTextDocument *doc, const QString &markdown)
+void QTextMarkdownImporter::import(const QString &markdown)
{
MD_PARSER callbacks = {
0, // abi_version
@@ -127,19 +132,36 @@ void QTextMarkdownImporter::import(QTextDocument *doc, const QString &markdown)
&CbDebugLog,
nullptr // syntax
};
- m_doc = doc;
- m_paragraphMargin = m_doc->defaultFont().pointSize() * 2 / 3;
- m_cursor = new QTextCursor(doc);
+ QTextDocument *doc = m_cursor.document();
+ const auto defaultFont = doc->defaultFont();
+ m_paragraphMargin = defaultFont.pointSize() * 2 / 3;
doc->clear();
- if (doc->defaultFont().pointSize() != -1)
- m_monoFont.setPointSize(doc->defaultFont().pointSize());
+ if (defaultFont.pointSize() != -1)
+ m_monoFont.setPointSize(defaultFont.pointSize());
else
- m_monoFont.setPixelSize(doc->defaultFont().pixelSize());
- qCDebug(lcMD) << "default font" << doc->defaultFont() << "mono font" << m_monoFont;
- QByteArray md = markdown.toUtf8();
- md_parse(md.constData(), MD_SIZE(md.size()), &callbacks, this);
- delete m_cursor;
- m_cursor = nullptr;
+ m_monoFont.setPixelSize(defaultFont.pixelSize());
+ qCDebug(lcMD) << "default font" << defaultFont << "mono font" << m_monoFont;
+ QStringView md = markdown;
+
+ if (m_features.testFlag(QTextMarkdownImporter::FeatureFrontMatter) && md.startsWith(markerString())) {
+ qsizetype endMarkerPos = md.indexOf(markerString(), markerString().size() + 1);
+ if (endMarkerPos > 4) {
+ qsizetype firstLinePos = 4; // first line of yaml
+ while (md.at(firstLinePos) == '\n'_L1 || md.at(firstLinePos) == '\r'_L1)
+ ++firstLinePos;
+ auto frontMatter = md.sliced(firstLinePos, endMarkerPos - firstLinePos);
+ firstLinePos = endMarkerPos + 4; // first line of markdown after yaml
+ while (md.size() > firstLinePos && (md.at(firstLinePos) == '\n'_L1 || md.at(firstLinePos) == '\r'_L1))
+ ++firstLinePos;
+ md = md.sliced(firstLinePos);
+ doc->setMetaInformation(QTextDocument::FrontMatter, frontMatter.toString());
+ qCDebug(lcMD) << "extracted FrontMatter: size" << frontMatter.size();
+ }
+ }
+ const auto mdUtf8 = md.toUtf8();
+ m_cursor.beginEditBlock();
+ md_parse(mdUtf8.constData(), MD_SIZE(mdUtf8.size()), &callbacks, this);
+ m_cursor.endEditBlock();
}
int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
@@ -148,7 +170,7 @@ int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
switch (blockType) {
case MD_BLOCK_P:
if (!m_listStack.isEmpty())
- qCDebug(lcMD, m_listItem ? "P of LI at level %d" : "P continuation inside LI at level %d", int(m_listStack.count()));
+ qCDebug(lcMD, m_listItem ? "P of LI at level %d" : "P continuation inside LI at level %d", int(m_listStack.size()));
else
qCDebug(lcMD, "P");
m_needsInsertBlock = true;
@@ -178,11 +200,11 @@ int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
charFmt.setFontWeight(QFont::Bold);
blockFmt.setHeadingLevel(int(detail->level));
m_needsInsertBlock = false;
- if (m_doc->isEmpty()) {
- m_cursor->setBlockFormat(blockFmt);
- m_cursor->setCharFormat(charFmt);
+ if (m_cursor.document()->isEmpty()) {
+ m_cursor.setBlockFormat(blockFmt);
+ m_cursor.setCharFormat(charFmt);
} else {
- m_cursor->insertBlock(blockFmt, charFmt);
+ m_cursor.insertBlock(blockFmt, charFmt);
}
qCDebug(lcMD, "H%d", detail->level);
} break;
@@ -197,12 +219,12 @@ int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
} break;
case MD_BLOCK_UL: {
if (m_needsInsertList) // list nested in an empty list
- m_listStack.push(m_cursor->insertList(m_listFormat));
+ m_listStack.push(m_cursor.insertList(m_listFormat));
else
m_needsInsertList = true;
MD_BLOCK_UL_DETAIL *detail = static_cast<MD_BLOCK_UL_DETAIL *>(det);
m_listFormat = QTextListFormat();
- m_listFormat.setIndent(m_listStack.count() + 1);
+ m_listFormat.setIndent(m_listStack.size() + 1);
switch (detail->mark) {
case '*':
m_listFormat.setStyle(QTextListFormat::ListCircle);
@@ -214,19 +236,20 @@ int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
m_listFormat.setStyle(QTextListFormat::ListDisc);
break;
}
- qCDebug(lcMD, "UL %c level %d", detail->mark, int(m_listStack.count()) + 1);
+ qCDebug(lcMD, "UL %c level %d", detail->mark, int(m_listStack.size()) + 1);
} break;
case MD_BLOCK_OL: {
if (m_needsInsertList) // list nested in an empty list
- m_listStack.push(m_cursor->insertList(m_listFormat));
+ m_listStack.push(m_cursor.insertList(m_listFormat));
else
m_needsInsertList = true;
MD_BLOCK_OL_DETAIL *detail = static_cast<MD_BLOCK_OL_DETAIL *>(det);
m_listFormat = QTextListFormat();
- m_listFormat.setIndent(m_listStack.count() + 1);
+ m_listFormat.setIndent(m_listStack.size() + 1);
m_listFormat.setNumberSuffix(QChar::fromLatin1(detail->mark_delimiter));
m_listFormat.setStyle(QTextListFormat::ListDecimal);
- qCDebug(lcMD, "OL xx%d level %d", detail->mark_delimiter, int(m_listStack.count()) + 1);
+ m_listFormat.setStart(detail->start);
+ qCDebug(lcMD, "OL xx%d level %d start %d", detail->mark_delimiter, int(m_listStack.size()) + 1, detail->start);
} break;
case MD_BLOCK_TD: {
MD_BLOCK_TD_DETAIL *detail = static_cast<MD_BLOCK_TD_DETAIL *>(det);
@@ -238,10 +261,10 @@ int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
qWarning("malformed table in Markdown input");
return 1;
}
- *m_cursor = cell.firstCursorPosition();
- QTextBlockFormat blockFmt = m_cursor->blockFormat();
+ m_cursor = cell.firstCursorPosition();
+ QTextBlockFormat blockFmt = m_cursor.blockFormat();
blockFmt.setAlignment(MdAlignment(detail->align));
- m_cursor->setBlockFormat(blockFmt);
+ m_cursor.setBlockFormat(blockFmt);
qCDebug(lcMD) << "TD; align" << detail->align << MdAlignment(detail->align) << "col" << m_tableCol;
} break;
case MD_BLOCK_TH: {
@@ -269,13 +292,13 @@ int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
case MD_BLOCK_TABLE:
m_tableColumnCount = 0;
m_tableRowCount = 0;
- m_currentTable = m_cursor->insertTable(1, 1); // we don't know the dimensions yet
+ m_currentTable = m_cursor.insertTable(1, 1); // we don't know the dimensions yet
break;
case MD_BLOCK_HR: {
qCDebug(lcMD, "HR");
QTextBlockFormat blockFmt;
blockFmt.setProperty(QTextFormat::BlockTrailingHorizontalRulerWidth, 1);
- m_cursor->insertBlock(blockFmt, QTextCharFormat());
+ m_cursor.insertBlock(blockFmt, QTextCharFormat());
} break;
default:
break; // nothing to do for now
@@ -293,11 +316,11 @@ int QTextMarkdownImporter::cbLeaveBlock(int blockType, void *detail)
case MD_BLOCK_UL:
case MD_BLOCK_OL:
if (Q_UNLIKELY(m_needsInsertList))
- m_listStack.push(m_cursor->createList(m_listFormat));
+ m_listStack.push(m_cursor.createList(m_listFormat));
if (Q_UNLIKELY(m_listStack.isEmpty())) {
qCWarning(lcMD, "list ended unexpectedly");
} else {
- qCDebug(lcMD, "list at level %d ended", int(m_listStack.count()));
+ qCDebug(lcMD, "list at level %d ended", int(m_listStack.size()));
m_listStack.pop();
}
break;
@@ -331,10 +354,10 @@ int QTextMarkdownImporter::cbLeaveBlock(int blockType, void *detail)
case MD_BLOCK_TABLE:
qCDebug(lcMD) << "table ended with" << m_currentTable->columns() << "cols and" << m_currentTable->rows() << "rows";
m_currentTable = nullptr;
- m_cursor->movePosition(QTextCursor::End);
+ m_cursor.movePosition(QTextCursor::End);
break;
case MD_BLOCK_LI:
- qCDebug(lcMD, "LI at level %d ended", int(m_listStack.count()));
+ qCDebug(lcMD, "LI at level %d ended", int(m_listStack.size()));
m_listItem = false;
break;
case MD_BLOCK_CODE: {
@@ -348,7 +371,7 @@ int QTextMarkdownImporter::cbLeaveBlock(int blockType, void *detail)
m_needsInsertBlock = true;
} break;
case MD_BLOCK_H:
- m_cursor->setCharFormat(QTextCharFormat());
+ m_cursor.setCharFormat(QTextCharFormat());
break;
default:
break;
@@ -399,10 +422,10 @@ int QTextMarkdownImporter::cbEnterSpan(int spanType, void *det)
break;
}
m_spanFormatStack.push(charFmt);
- qCDebug(lcMD) << spanType << "setCharFormat" << charFmt.font().families().first()
+ qCDebug(lcMD) << spanType << "setCharFormat" << charFmt.font().families().constFirst()
<< charFmt.fontWeight() << (charFmt.fontItalic() ? "italic" : "")
<< charFmt.foreground().color().name();
- m_cursor->setCharFormat(charFmt);
+ m_cursor.setCharFormat(charFmt);
return 0; // no error
}
@@ -415,8 +438,8 @@ int QTextMarkdownImporter::cbLeaveSpan(int spanType, void *detail)
if (!m_spanFormatStack.isEmpty())
charFmt = m_spanFormatStack.top();
}
- m_cursor->setCharFormat(charFmt);
- qCDebug(lcMD) << spanType << "setCharFormat" << charFmt.font().families().first()
+ m_cursor.setCharFormat(charFmt);
+ qCDebug(lcMD) << spanType << "setCharFormat" << charFmt.font().families().constFirst()
<< charFmt.fontWeight() << (charFmt.fontItalic() ? "italic" : "")
<< charFmt.foreground().color().name();
if (spanType == int(MD_SPAN_IMG))
@@ -447,10 +470,10 @@ int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size)
s = QString(QChar(u'\xFFFD')); // CommonMark-required replacement for null
break;
case MD_TEXT_BR:
- s = QString(Newline);
+ s = QString(qtmi_Newline);
break;
case MD_TEXT_SOFTBR:
- s = QString(Space);
+ s = QString(qtmi_Space);
break;
case MD_TEXT_CODE:
// We'll see MD_SPAN_CODE too, which will set the char format, and that's enough.
@@ -460,7 +483,7 @@ int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size)
if (m_htmlTagDepth)
m_htmlAccumulator += s;
else
- m_cursor->insertHtml(s);
+ m_cursor.insertHtml(s);
s = QString();
break;
#endif
@@ -482,11 +505,11 @@ int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size)
m_htmlAccumulator += s;
if (!m_htmlTagDepth) { // all open tags are now closed
qCDebug(lcMD) << "HTML" << m_htmlAccumulator;
- m_cursor->insertHtml(m_htmlAccumulator);
+ m_cursor.insertHtml(m_htmlAccumulator);
if (m_spanFormatStack.isEmpty())
- m_cursor->setCharFormat(QTextCharFormat());
+ m_cursor.setCharFormat(QTextCharFormat());
else
- m_cursor->setCharFormat(m_spanFormatStack.top());
+ m_cursor.setCharFormat(m_spanFormatStack.top());
m_htmlAccumulator = QString();
}
#endif
@@ -499,7 +522,7 @@ int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size)
m_nonEmptyTableCells.append(m_tableCol);
break;
case MD_BLOCK_CODE:
- if (s == Newline) {
+ if (s == qtmi_Newline) {
// defer a blank line until we see something else in the code block,
// to avoid ending every code block with a gratuitous blank line
m_needsInsertBlock = true;
@@ -516,24 +539,24 @@ int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size)
m_imageFormat.setProperty(QTextFormat::ImageAltText, s);
qCDebug(lcMD) << "image" << m_imageFormat.name()
<< "title" << m_imageFormat.stringProperty(QTextFormat::ImageTitle)
- << "alt" << s << "relative to" << m_doc->baseUrl();
- m_cursor->insertImage(m_imageFormat);
+ << "alt" << s << "relative to" << m_cursor.document()->baseUrl();
+ m_cursor.insertImage(m_imageFormat);
return 0; // no error
}
if (!s.isEmpty())
- m_cursor->insertText(s);
- if (m_cursor->currentList()) {
+ m_cursor.insertText(s);
+ if (m_cursor.currentList()) {
// The list item will indent the list item's text, so we don't need indentation on the block.
- QTextBlockFormat bfmt = m_cursor->blockFormat();
+ QTextBlockFormat bfmt = m_cursor.blockFormat();
bfmt.setIndent(0);
- m_cursor->setBlockFormat(bfmt);
+ m_cursor.setBlockFormat(bfmt);
}
if (lcMD().isEnabled(QtDebugMsg)) {
- QTextBlockFormat bfmt = m_cursor->blockFormat();
+ QTextBlockFormat bfmt = m_cursor.blockFormat();
QString debugInfo;
- if (m_cursor->currentList())
- debugInfo = "in list at depth "_L1 + QString::number(m_cursor->currentList()->format().indent());
+ if (m_cursor.currentList())
+ debugInfo = "in list at depth "_L1 + QString::number(m_cursor.currentList()->format().indent());
if (bfmt.hasProperty(QTextFormat::BlockQuoteLevel))
debugInfo += "in blockquote at depth "_L1 +
QString::number(bfmt.intProperty(QTextFormat::BlockQuoteLevel));
@@ -550,7 +573,7 @@ int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size)
Insert a new block based on stored state.
m_cursor cannot store the state for the _next_ block ahead of time, because
- m_cursor->setBlockFormat() controls the format of the block that the cursor
+ m_cursor.setBlockFormat() controls the format of the block that the cursor
is already in; so cbLeaveBlock() cannot call setBlockFormat() without
altering the block that was just added. Therefore cbLeaveBlock() and the
following cbEnterBlock() set variables to remember what formatting should
@@ -572,8 +595,8 @@ void QTextMarkdownImporter::insertBlock()
}
if (m_blockQuoteDepth) {
blockFormat.setProperty(QTextFormat::BlockQuoteLevel, m_blockQuoteDepth);
- blockFormat.setLeftMargin(BlockQuoteIndent * m_blockQuoteDepth);
- blockFormat.setRightMargin(BlockQuoteIndent);
+ blockFormat.setLeftMargin(qtmi_BlockQuoteIndent * m_blockQuoteDepth);
+ blockFormat.setRightMargin(qtmi_BlockQuoteIndent);
}
if (m_codeBlock) {
blockFormat.setProperty(QTextFormat::BlockCodeLanguage, m_blockCodeLanguage);
@@ -591,20 +614,20 @@ void QTextMarkdownImporter::insertBlock()
else
blockFormat.setMarker(m_markerType);
if (!m_listStack.isEmpty())
- blockFormat.setIndent(m_listStack.count());
- if (m_doc->isEmpty()) {
- m_cursor->setBlockFormat(blockFormat);
- m_cursor->setCharFormat(charFormat);
+ blockFormat.setIndent(m_listStack.size());
+ if (m_cursor.document()->isEmpty()) {
+ m_cursor.setBlockFormat(blockFormat);
+ m_cursor.setCharFormat(charFormat);
} else if (m_listItem) {
- m_cursor->insertBlock(blockFormat, QTextCharFormat());
- m_cursor->setCharFormat(charFormat);
+ m_cursor.insertBlock(blockFormat, QTextCharFormat());
+ m_cursor.setCharFormat(charFormat);
} else {
- m_cursor->insertBlock(blockFormat, charFormat);
+ m_cursor.insertBlock(blockFormat, charFormat);
}
if (m_needsInsertList) {
- m_listStack.push(m_cursor->createList(m_listFormat));
+ m_listStack.push(m_cursor.createList(m_listFormat));
} else if (!m_listStack.isEmpty() && m_listItem && m_listStack.top()) {
- m_listStack.top()->add(m_cursor->block());
+ m_listStack.top()->add(m_cursor.block());
}
m_needsInsertList = false;
m_needsInsertBlock = false;
diff --git a/src/gui/text/qtextmarkdownimporter_p.h b/src/gui/text/qtextmarkdownimporter_p.h
index b7974da56e..8b8f4ec9bb 100644
--- a/src/gui/text/qtextmarkdownimporter_p.h
+++ b/src/gui/text/qtextmarkdownimporter_p.h
@@ -46,6 +46,7 @@ public:
FeaturePermissiveWWWAutoLinks = 0x0400,
FeatureTasklists = 0x0800,
FeatureUnderline = 0x4000,
+ FeatureFrontMatter = 0x100000, // Qt feature, not yet in MD4C
// composite flags
FeaturePermissiveAutoLinks = FeaturePermissiveMailAutoLinks
| FeaturePermissiveURLAutoLinks | FeaturePermissiveWWWAutoLinks,
@@ -55,10 +56,10 @@ public:
};
Q_DECLARE_FLAGS(Features, Feature)
- QTextMarkdownImporter(Features features);
- QTextMarkdownImporter(QTextDocument::MarkdownFeatures features);
+ QTextMarkdownImporter(QTextDocument *doc, Features features);
+ QTextMarkdownImporter(QTextDocument *doc, QTextDocument::MarkdownFeatures features);
- void import(QTextDocument *doc, const QString &markdown);
+ void import(const QString &markdown);
public:
// MD4C callbacks
@@ -72,8 +73,7 @@ private:
void insertBlock();
private:
- QTextDocument *m_doc = nullptr;
- QTextCursor *m_cursor = nullptr;
+ QTextCursor m_cursor;
QTextTable *m_currentTable = nullptr; // because m_cursor->currentTable() doesn't work
#if QT_CONFIG(regularexpression)
QString m_htmlAccumulator;
diff --git a/src/gui/text/qtextmarkdownwriter.cpp b/src/gui/text/qtextmarkdownwriter.cpp
index cda1f209ad..361158e722 100644
--- a/src/gui/text/qtextmarkdownwriter.cpp
+++ b/src/gui/text/qtextmarkdownwriter.cpp
@@ -10,7 +10,9 @@
#include "qtexttable.h"
#include "qtextcursor.h"
#include "qtextimagehandler_p.h"
+#include "qtextmarkdownimporter_p.h"
#include "qloggingcategory.h"
+#include <QtCore/QRegularExpression>
#if QT_CONFIG(itemmodel)
#include "qabstractitemmodel.h"
#endif
@@ -21,15 +23,15 @@ using namespace Qt::StringLiterals;
Q_LOGGING_CATEGORY(lcMDW, "qt.text.markdown.writer")
-static const QChar Space = u' ';
-static const QChar Tab = u'\t';
-static const QChar Newline = u'\n';
-static const QChar CarriageReturn = u'\r';
-static const QChar LineBreak = u'\x2028';
-static const QChar DoubleQuote = u'"';
-static const QChar Backtick = u'`';
-static const QChar Backslash = u'\\';
-static const QChar Period = u'.';
+static const QChar qtmw_Space = u' ';
+static const QChar qtmw_Tab = u'\t';
+static const QChar qtmw_Newline = u'\n';
+static const QChar qtmw_CarriageReturn = u'\r';
+static const QChar qtmw_LineBreak = u'\x2028';
+static const QChar qtmw_DoubleQuote = u'"';
+static const QChar qtmw_Backtick = u'`';
+static const QChar qtmw_Backslash = u'\\';
+static const QChar qtmw_Period = u'.';
QTextMarkdownWriter::QTextMarkdownWriter(QTextStream &stream, QTextDocument::MarkdownFeatures features)
: m_stream(stream), m_features(features)
@@ -38,6 +40,7 @@ QTextMarkdownWriter::QTextMarkdownWriter(QTextStream &stream, QTextDocument::Mar
bool QTextMarkdownWriter::writeAll(const QTextDocument *document)
{
+ writeFrontMatter(document->metaInformation(QTextDocument::FrontMatter));
writeFrame(document->rootFrame());
return true;
}
@@ -47,20 +50,20 @@ void QTextMarkdownWriter::writeTable(const QAbstractItemModel *table)
{
QList<int> tableColumnWidths(table->columnCount());
for (int col = 0; col < table->columnCount(); ++col) {
- tableColumnWidths[col] = table->headerData(col, Qt::Horizontal).toString().length();
+ tableColumnWidths[col] = table->headerData(col, Qt::Horizontal).toString().size();
for (int row = 0; row < table->rowCount(); ++row) {
tableColumnWidths[col] = qMax(tableColumnWidths[col],
- table->data(table->index(row, col)).toString().length());
+ table->data(table->index(row, col)).toString().size());
}
}
// write the header and separator
for (int col = 0; col < table->columnCount(); ++col) {
QString s = table->headerData(col, Qt::Horizontal).toString();
- m_stream << "|" << s << QString(tableColumnWidths[col] - s.length(), Space);
+ m_stream << '|' << s << QString(tableColumnWidths[col] - s.size(), qtmw_Space);
}
m_stream << "|" << Qt::endl;
- for (int col = 0; col < tableColumnWidths.length(); ++col)
+ for (int col = 0; col < tableColumnWidths.size(); ++col)
m_stream << '|' << QString(tableColumnWidths[col], u'-');
m_stream << '|'<< Qt::endl;
@@ -68,7 +71,7 @@ void QTextMarkdownWriter::writeTable(const QAbstractItemModel *table)
for (int row = 0; row < table->rowCount(); ++row) {
for (int col = 0; col < table->columnCount(); ++col) {
QString s = table->data(table->index(row, col)).toString();
- m_stream << "|" << s << QString(tableColumnWidths[col] - s.length(), Space);
+ m_stream << '|' << s << QString(tableColumnWidths[col] - s.size(), qtmw_Space);
}
m_stream << '|'<< Qt::endl;
}
@@ -76,6 +79,19 @@ void QTextMarkdownWriter::writeTable(const QAbstractItemModel *table)
}
#endif
+void QTextMarkdownWriter::writeFrontMatter(const QString &fm)
+{
+ const bool featureEnabled = m_features.testFlag(
+ static_cast<QTextDocument::MarkdownFeature>(QTextMarkdownImporter::FeatureFrontMatter));
+ qCDebug(lcMDW) << "writing FrontMatter?" << featureEnabled << "size" << fm.size();
+ if (fm.isEmpty() || !featureEnabled)
+ return;
+ m_stream << "---\n"_L1 << fm;
+ if (!fm.endsWith(qtmw_Newline))
+ m_stream << qtmw_Newline;
+ m_stream << "---\n"_L1;
+}
+
void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
{
Q_ASSERT(frame);
@@ -95,7 +111,7 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
while (it != cell.end()) {
QTextBlock block = it.currentBlock();
if (block.isValid())
- cellTextLen += block.text().length();
+ cellTextLen += block.text().size();
++it;
}
if (cell.columnSpan() == 1 && tableColumnWidths[col] < cellTextLen)
@@ -112,17 +128,22 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
// suppress needless blank lines, when there will be a big change in block format
bool nextIsDifferent = false;
bool ending = false;
+ int blockQuoteIndent = 0;
+ int nextBlockQuoteIndent = 0;
{
QTextFrame::iterator next = iterator;
++next;
+ QTextBlockFormat format = iterator.currentBlock().blockFormat();
+ QTextBlockFormat nextFormat = next.currentBlock().blockFormat();
+ blockQuoteIndent = format.intProperty(QTextFormat::BlockQuoteLevel);
+ nextBlockQuoteIndent = nextFormat.intProperty(QTextFormat::BlockQuoteLevel);
if (next.atEnd()) {
nextIsDifferent = true;
ending = true;
} else {
- QTextBlockFormat format = iterator.currentBlock().blockFormat();
- QTextBlockFormat nextFormat = next.currentBlock().blockFormat();
if (nextFormat.indent() != format.indent() ||
- nextFormat.property(QTextFormat::BlockCodeLanguage) != format.property(QTextFormat::BlockCodeLanguage))
+ nextFormat.property(QTextFormat::BlockCodeLanguage) !=
+ format.property(QTextFormat::BlockCodeLanguage))
nextIsDifferent = true;
}
}
@@ -130,17 +151,19 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
QTextTableCell cell = table->cellAt(block.position());
if (tableRow < cell.row()) {
if (tableRow == 0) {
- m_stream << Newline;
- for (int col = 0; col < tableColumnWidths.length(); ++col)
+ m_stream << qtmw_Newline;
+ for (int col = 0; col < tableColumnWidths.size(); ++col)
m_stream << '|' << QString(tableColumnWidths[col], u'-');
m_stream << '|';
}
- m_stream << Newline << "|";
+ m_stream << qtmw_Newline << '|';
tableRow = cell.row();
}
} else if (!block.textList()) {
- if (lastWasList)
- m_stream << Newline;
+ if (lastWasList) {
+ m_stream << qtmw_Newline;
+ m_linePrefixWritten = false;
+ }
}
int endingCol = writeBlock(block, !table, table && tableRow == 0,
nextIsDifferent && !block.textList());
@@ -152,20 +175,28 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
for (int col = cell.column(); col < spanEndCol; ++col)
paddingLen += tableColumnWidths[col];
if (paddingLen > 0)
- m_stream << QString(paddingLen, Space);
+ m_stream << QString(paddingLen, qtmw_Space);
for (int col = cell.column(); col < spanEndCol; ++col)
m_stream << "|";
} else if (m_fencedCodeBlock && ending) {
- m_stream << Newline << m_linePrefix << QString(m_wrappedLineIndent, Space)
- << m_codeBlockFence << Newline << Newline;
+ m_stream << qtmw_Newline << m_linePrefix << QString(m_wrappedLineIndent, qtmw_Space)
+ << m_codeBlockFence << qtmw_Newline << qtmw_Newline;
m_codeBlockFence.clear();
} else if (m_indentedCodeBlock && nextIsDifferent) {
- m_stream << Newline << Newline;
+ m_stream << qtmw_Newline << qtmw_Newline;
} else if (endingCol > 0) {
if (block.textList() || block.blockFormat().hasProperty(QTextFormat::BlockCodeLanguage)) {
- m_stream << Newline;
+ m_stream << qtmw_Newline;
+ if (block.textList()) {
+ m_stream << m_linePrefix;
+ m_linePrefixWritten = true;
+ }
} else {
- m_stream << Newline << Newline;
+ m_stream << qtmw_Newline;
+ if (nextBlockQuoteIndent < blockQuoteIndent)
+ setLinePrefixForBlockQuote(nextBlockQuoteIndent);
+ m_stream << m_linePrefix;
+ m_stream << qtmw_Newline;
m_doubleNewlineWritten = true;
}
}
@@ -175,7 +206,7 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
++iterator;
}
if (table) {
- m_stream << Newline << Newline;
+ m_stream << qtmw_Newline << qtmw_Newline;
m_doubleNewlineWritten = true;
}
m_listInfo.clear();
@@ -211,18 +242,28 @@ QTextMarkdownWriter::ListInfo QTextMarkdownWriter::listInfo(QTextList *list)
return m_listInfo.value(list);
}
+void QTextMarkdownWriter::setLinePrefixForBlockQuote(int level)
+{
+ m_linePrefix.clear();
+ if (level > 0) {
+ m_linePrefix.reserve(level * 2);
+ for (int i = 0; i < level; ++i)
+ m_linePrefix += u"> ";
+ }
+}
+
static int nearestWordWrapIndex(const QString &s, int before)
{
- before = qMin(before, s.length());
+ before = qMin(before, s.size());
int fragBegin = qMax(before - 15, 0);
if (lcMDW().isDebugEnabled()) {
QString frag = s.mid(fragBegin, 30);
qCDebug(lcMDW) << frag << before;
- qCDebug(lcMDW) << QString(before - fragBegin, Period) + u'<';
+ qCDebug(lcMDW) << QString(before - fragBegin, qtmw_Period) + u'<';
}
for (int i = before - 1; i >= 0; --i) {
if (s.at(i).isSpace()) {
- qCDebug(lcMDW) << QString(i - fragBegin, Period) + u'^' << i;
+ qCDebug(lcMDW) << QString(i - fragBegin, qtmw_Period) + u'^' << i;
return i;
}
}
@@ -232,10 +273,10 @@ static int nearestWordWrapIndex(const QString &s, int before)
static int adjacentBackticksCount(const QString &s)
{
- int start = -1, len = s.length();
+ int start = -1, len = s.size();
int ret = 0;
for (int i = 0; i < len; ++i) {
- if (s.at(i) == Backtick) {
+ if (s.at(i) == qtmw_Backtick) {
if (start < 0)
start = i;
} else if (start >= 0) {
@@ -243,20 +284,58 @@ static int adjacentBackticksCount(const QString &s)
start = -1;
}
}
- if (s.at(len - 1) == Backtick)
+ if (s.at(len - 1) == qtmw_Backtick)
ret = qMax(ret, len - start);
return ret;
}
+/*! \internal
+ Escape anything at the beginning of a line of markdown that would be
+ misinterpreted by a markdown parser, including any period that follows a
+ number (to avoid misinterpretation as a numbered list item).
+ https://spec.commonmark.org/0.31.2/#backslash-escapes
+*/
static void maybeEscapeFirstChar(QString &s)
{
+ static const QRegularExpression numericListRe(uR"(\d+([\.)])\s)"_s);
+ static const QLatin1StringView specialFirstCharacters("#*+-");
+
QString sTrimmed = s.trimmed();
if (sTrimmed.isEmpty())
return;
- char firstChar = sTrimmed.at(0).toLatin1();
- if (firstChar == '*' || firstChar == '+' || firstChar == '-') {
- int i = s.indexOf(QLatin1Char(firstChar));
+ QChar firstChar = sTrimmed.at(0);
+ if (specialFirstCharacters.contains(firstChar)) {
+ int i = s.indexOf(firstChar); // == 0 unless s got trimmed
s.insert(i, u'\\');
+ } else {
+ auto match = numericListRe.match(s, 0, QRegularExpression::NormalMatch,
+ QRegularExpression::AnchorAtOffsetMatchOption);
+ if (match.hasMatch())
+ s.insert(match.capturedStart(1), qtmw_Backslash);
+ }
+}
+
+/*! \internal
+ Escape all backslashes. Then escape any special character that stands
+ alone or prefixes a "word", including the \c < that starts an HTML tag.
+ https://spec.commonmark.org/0.31.2/#backslash-escapes
+*/
+static void escapeSpecialCharacters(QString &s)
+{
+ static const QRegularExpression spaceRe(uR"(\s+)"_s);
+ static const QRegularExpression specialRe(uR"([<!*[`&]+[/\w])"_s);
+
+ s.replace("\\"_L1, "\\\\"_L1);
+
+ int i = 0;
+ while (i >= 0) {
+ if (int j = s.indexOf(specialRe, i); j >= 0) {
+ s.insert(j, qtmw_Backslash);
+ i = j + 3;
+ }
+ i = s.indexOf(spaceRe, i);
+ if (i >= 0)
+ ++i; // past the whitespace, if found
}
}
@@ -270,14 +349,14 @@ static LineEndPositions findLineEnd(const QChar *begin, const QChar *end)
LineEndPositions result{ end, end };
while (begin < end) {
- if (*begin == Newline) {
+ if (*begin == qtmw_Newline) {
result.lineEnd = begin;
result.nextLineBegin = begin + 1;
break;
- } else if (*begin == CarriageReturn) {
+ } else if (*begin == qtmw_CarriageReturn) {
result.lineEnd = begin;
result.nextLineBegin = begin + 1;
- if (((begin + 1) < end) && begin[1] == Newline)
+ if (((begin + 1) < end) && begin[1] == qtmw_Newline)
++result.nextLineBegin;
break;
}
@@ -291,7 +370,7 @@ static LineEndPositions findLineEnd(const QChar *begin, const QChar *end)
static bool isBlankLine(const QChar *begin, const QChar *end)
{
while (begin < end) {
- if (*begin != Space && *begin != Tab)
+ if (*begin != qtmw_Space && *begin != qtmw_Tab)
return false;
++begin;
}
@@ -302,7 +381,7 @@ static QString createLinkTitle(const QString &title)
{
QString result;
result.reserve(title.size() + 2);
- result += DoubleQuote;
+ result += qtmw_DoubleQuote;
const QChar *data = title.data();
const QChar *end = data + title.size();
@@ -312,8 +391,8 @@ static QString createLinkTitle(const QString &title)
if (!isBlankLine(data, lineEndPositions.lineEnd)) {
while (data < lineEndPositions.nextLineBegin) {
- if (*data == DoubleQuote)
- result += Backslash;
+ if (*data == qtmw_DoubleQuote)
+ result += qtmw_Backslash;
result += *data;
++data;
}
@@ -322,7 +401,7 @@ static QString createLinkTitle(const QString &title)
data = lineEndPositions.nextLineBegin;
}
- result += DoubleQuote;
+ result += qtmw_DoubleQuote;
return result;
}
@@ -334,17 +413,29 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
QTextBlockFormat blockFmt = block.blockFormat();
bool missedBlankCodeBlockLine = false;
const bool codeBlock = blockFmt.hasProperty(QTextFormat::BlockCodeFence) ||
- blockFmt.stringProperty(QTextFormat::BlockCodeLanguage).length() > 0 ||
+ blockFmt.stringProperty(QTextFormat::BlockCodeLanguage).size() > 0 ||
blockFmt.nonBreakableLines();
+ const int blockQuoteLevel = blockFmt.intProperty(QTextFormat::BlockQuoteLevel);
if (m_fencedCodeBlock && !codeBlock) {
- m_stream << m_linePrefix << m_codeBlockFence << Newline;
+ m_stream << m_linePrefix << m_codeBlockFence << qtmw_Newline;
m_fencedCodeBlock = false;
m_codeBlockFence.clear();
+ m_linePrefixWritten = m_linePrefix.size() > 0;
+ }
+ m_linePrefix.clear();
+ if (!blockFmt.headingLevel() && blockQuoteLevel > 0) {
+ setLinePrefixForBlockQuote(blockQuoteLevel);
+ if (!m_linePrefixWritten) {
+ m_stream << m_linePrefix;
+ m_linePrefixWritten = true;
+ }
}
if (block.textList()) { // it's a list-item
auto fmt = block.textList()->format();
const int listLevel = fmt.indent();
- const int number = block.textList()->itemNumber(block) + 1;
+ // Negative numbers don't start a list in Markdown, so ignore them.
+ const int start = fmt.start() >= 0 ? fmt.start() : 1;
+ const int number = block.textList()->itemNumber(block) + start;
QByteArray bullet = " ";
bool numeric = false;
switch (fmt.style()) {
@@ -383,19 +474,19 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
int indentFirstLine = (listLevel - 1) * (numeric ? 4 : 2);
m_wrappedLineIndent += indentFirstLine;
if (m_lastListIndent != listLevel && !m_doubleNewlineWritten && listInfo(block.textList()).loose)
- m_stream << Newline;
+ m_stream << qtmw_Newline;
m_lastListIndent = listLevel;
- QString prefix(indentFirstLine, Space);
+ QString prefix(indentFirstLine, qtmw_Space);
if (numeric) {
QString suffix = fmt.numberSuffix();
if (suffix.isEmpty())
- suffix = QString(Period);
- QString numberStr = QString::number(number) + suffix + Space;
- if (numberStr.length() == 3)
- numberStr += Space;
+ suffix = QString(qtmw_Period);
+ QString numberStr = QString::number(number) + suffix + qtmw_Space;
+ if (numberStr.size() == 3)
+ numberStr += qtmw_Space;
prefix += numberStr;
} else {
- prefix += QLatin1StringView(bullet) + Space;
+ prefix += QLatin1StringView(bullet) + qtmw_Space;
}
m_stream << prefix;
} else if (blockFmt.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
@@ -412,60 +503,63 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
fenceChar = "`"_L1;
m_codeBlockFence = QString(3, fenceChar.at(0));
if (blockFmt.hasProperty(QTextFormat::BlockIndent))
- m_codeBlockFence = QString(m_wrappedLineIndent, Space) + m_codeBlockFence;
+ m_codeBlockFence = QString(m_wrappedLineIndent, qtmw_Space) + m_codeBlockFence;
// A block quote can contain an indented code block, but not vice-versa.
- m_stream << m_linePrefix << m_codeBlockFence
- << blockFmt.stringProperty(QTextFormat::BlockCodeLanguage) << Newline;
+ m_stream << m_codeBlockFence << blockFmt.stringProperty(QTextFormat::BlockCodeLanguage)
+ << qtmw_Newline << m_linePrefix;
m_fencedCodeBlock = true;
}
wrap = false;
} else if (!blockFmt.indent()) {
m_wrappedLineIndent = 0;
- m_linePrefix.clear();
- if (blockFmt.hasProperty(QTextFormat::BlockQuoteLevel)) {
- int level = blockFmt.intProperty(QTextFormat::BlockQuoteLevel);
- QString quoteMarker = QStringLiteral("> ");
- m_linePrefix.reserve(level * 2);
- for (int i = 0; i < level; ++i)
- m_linePrefix += quoteMarker;
- }
if (blockFmt.hasProperty(QTextFormat::BlockCodeLanguage)) {
// A block quote can contain an indented code block, but not vice-versa.
- m_linePrefix += QString(4, Space);
+ m_linePrefix += QString(4, qtmw_Space);
m_indentedCodeBlock = true;
}
+ if (!m_linePrefixWritten) {
+ m_stream << m_linePrefix;
+ m_linePrefixWritten = true;
+ }
}
- if (blockFmt.headingLevel())
+ if (blockFmt.headingLevel()) {
m_stream << QByteArray(blockFmt.headingLevel(), '#') << ' ';
- else
- m_stream << m_linePrefix;
+ wrap = false;
+ }
- QString wrapIndentString = m_linePrefix + QString(m_wrappedLineIndent, Space);
+ QString wrapIndentString = m_linePrefix + QString(m_wrappedLineIndent, qtmw_Space);
// It would be convenient if QTextStream had a lineCharPos() accessor,
// to keep track of how many characters (not bytes) have been written on the current line,
// but it doesn't. So we have to keep track with this col variable.
- int col = wrapIndentString.length();
+ int col = wrapIndentString.size();
bool mono = false;
bool startsOrEndsWithBacktick = false;
bool bold = false;
bool italic = false;
bool underline = false;
bool strikeOut = false;
- QString backticks(Backtick);
+ bool endingMarkers = false;
+ QString backticks(qtmw_Backtick);
for (QTextBlock::Iterator frag = block.begin(); !frag.atEnd(); ++frag) {
missedBlankCodeBlockLine = false;
QString fragmentText = frag.fragment().text();
- while (fragmentText.endsWith(Newline))
+ while (fragmentText.endsWith(qtmw_Newline))
fragmentText.chop(1);
+ if (!(m_fencedCodeBlock || m_indentedCodeBlock)) {
+ escapeSpecialCharacters(fragmentText);
+ maybeEscapeFirstChar(fragmentText);
+ }
if (block.textList()) { // <li>first line</br>continuation</li>
- QString newlineIndent = QString(Newline) + QString(m_wrappedLineIndent, Space);
- fragmentText.replace(QString(LineBreak), newlineIndent);
+ QString newlineIndent =
+ QString(qtmw_Newline) + QString(m_wrappedLineIndent, qtmw_Space);
+ fragmentText.replace(QString(qtmw_LineBreak), newlineIndent);
} else if (blockFmt.indent() > 0) { // <li>first line<p>continuation</p></li>
- m_stream << QString(m_wrappedLineIndent, Space);
+ m_stream << QString(m_wrappedLineIndent, qtmw_Space);
} else {
- fragmentText.replace(LineBreak, Newline);
+ fragmentText.replace(qtmw_LineBreak, qtmw_Newline);
}
- startsOrEndsWithBacktick |= fragmentText.startsWith(Backtick) || fragmentText.endsWith(Backtick);
+ startsOrEndsWithBacktick |=
+ fragmentText.startsWith(qtmw_Backtick) || fragmentText.endsWith(qtmw_Backtick);
QTextCharFormat fmt = frag.fragment().charFormat();
if (fmt.isImageFormat()) {
QTextImageFormat ifmt = fmt.toImageFormat();
@@ -475,14 +569,14 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
QString s = "!["_L1 + desc + "]("_L1 + ifmt.name();
QString title = ifmt.stringProperty(QTextFormat::ImageTitle);
if (!title.isEmpty())
- s += Space + DoubleQuote + title + DoubleQuote;
+ s += qtmw_Space + qtmw_DoubleQuote + title + qtmw_DoubleQuote;
s += u')';
- if (wrap && col + s.length() > ColumnLimit) {
- m_stream << Newline << wrapIndentString;
+ if (wrap && col + s.size() > ColumnLimit) {
+ m_stream << qtmw_Newline << wrapIndentString;
col = m_wrappedLineIndent;
}
m_stream << s;
- col += s.length();
+ col += s.size();
} else if (fmt.hasProperty(QTextFormat::AnchorHref)) {
const auto href = fmt.property(QTextFormat::AnchorHref).toString();
const bool hasToolTip = fmt.hasProperty(QTextFormat::TextToolTip);
@@ -492,17 +586,17 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
} else {
s = u'[' + fragmentText + "]("_L1 + href;
if (hasToolTip) {
- s += Space;
+ s += qtmw_Space;
s += createLinkTitle(fmt.property(QTextFormat::TextToolTip).toString());
}
s += u')';
}
- if (wrap && col + s.length() > ColumnLimit) {
- m_stream << Newline << wrapIndentString;
+ if (wrap && col + s.size() > ColumnLimit) {
+ m_stream << qtmw_Newline << wrapIndentString;
col = m_wrappedLineIndent;
}
m_stream << s;
- col += s.length();
+ col += s.size();
} else {
QFontInfo fontInfo(fmt.font());
bool monoFrag = fontInfo.fixedPitch() || fmt.fontFixedPitch();
@@ -510,43 +604,55 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
if (!ignoreFormat) {
if (monoFrag != mono && !m_indentedCodeBlock && !m_fencedCodeBlock) {
if (monoFrag)
- backticks = QString(adjacentBackticksCount(fragmentText) + 1, Backtick);
+ backticks =
+ QString(adjacentBackticksCount(fragmentText) + 1, qtmw_Backtick);
markers += backticks;
if (startsOrEndsWithBacktick)
- markers += Space;
+ markers += qtmw_Space;
mono = monoFrag;
+ if (!mono)
+ endingMarkers = true;
}
if (!blockFmt.headingLevel() && !mono) {
if (fontInfo.bold() != bold) {
markers += "**"_L1;
bold = fontInfo.bold();
+ if (!bold)
+ endingMarkers = true;
}
if (fontInfo.italic() != italic) {
markers += u'*';
italic = fontInfo.italic();
+ if (!italic)
+ endingMarkers = true;
}
if (fontInfo.strikeOut() != strikeOut) {
markers += "~~"_L1;
strikeOut = fontInfo.strikeOut();
+ if (!strikeOut)
+ endingMarkers = true;
}
if (fontInfo.underline() != underline) {
- // Markdown doesn't support underline, but the parser will treat a single underline
- // the same as a single asterisk, and the marked fragment will be rendered in italics.
- // That will have to do.
+ // CommonMark specifies underline as another way to get emphasis (italics):
+ // https://spec.commonmark.org/0.31.2/#example-148
+ // but md4c allows us to distinguish them; so we support underlining (in GitHub dialect).
markers += u'_';
underline = fontInfo.underline();
+ if (!underline)
+ endingMarkers = true;
}
}
}
- if (wrap && col + markers.length() * 2 + fragmentText.length() > ColumnLimit) {
+ if (wrap && col + markers.size() * 2 + fragmentText.size() > ColumnLimit) {
int i = 0;
- int fragLen = fragmentText.length();
+ const int fragLen = fragmentText.size();
bool breakingLine = false;
while (i < fragLen) {
if (col >= ColumnLimit) {
- m_stream << Newline << wrapIndentString;
+ m_stream << markers << qtmw_Newline << wrapIndentString;
+ markers.clear();
col = m_wrappedLineIndent;
- while (fragmentText[i].isSpace())
+ while (i < fragLen && fragmentText[i].isSpace())
++i;
}
int j = i + ColumnLimit - col;
@@ -554,6 +660,13 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
int wi = nearestWordWrapIndex(fragmentText, j);
if (wi < 0) {
j = fragLen;
+ // can't break within the fragment: we need to break already _before_ it
+ if (endingMarkers) {
+ m_stream << markers;
+ markers.clear();
+ }
+ m_stream << qtmw_Newline << wrapIndentString;
+ col = m_wrappedLineIndent;
} else if (wi >= i) {
j = wi;
breakingLine = true;
@@ -565,28 +678,32 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
QString subfrag = fragmentText.mid(i, j - i);
if (!i) {
m_stream << markers;
- col += markers.length();
+ col += markers.size();
}
if (col == m_wrappedLineIndent)
maybeEscapeFirstChar(subfrag);
m_stream << subfrag;
if (breakingLine) {
- m_stream << Newline << wrapIndentString;
+ m_stream << qtmw_Newline << wrapIndentString;
col = m_wrappedLineIndent;
} else {
- col += subfrag.length();
+ col += subfrag.size();
}
i = j + 1;
- }
+ } // loop over fragment characters (we know we need to break somewhere)
} else {
+ if (!m_linePrefixWritten && col == wrapIndentString.size()) {
+ m_stream << m_linePrefix;
+ col += m_linePrefix.size();
+ }
m_stream << markers << fragmentText;
- col += markers.length() + fragmentText.length();
+ col += markers.size() + fragmentText.size();
}
}
}
if (mono) {
if (startsOrEndsWithBacktick) {
- m_stream << Space;
+ m_stream << qtmw_Space;
col += 1;
}
m_stream << backticks;
@@ -609,7 +726,8 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
col += 2;
}
if (missedBlankCodeBlockLine)
- m_stream << Newline;
+ m_stream << qtmw_Newline;
+ m_linePrefixWritten = false;
return col;
}
diff --git a/src/gui/text/qtextmarkdownwriter_p.h b/src/gui/text/qtextmarkdownwriter_p.h
index b940e37ddc..c0989b8f72 100644
--- a/src/gui/text/qtextmarkdownwriter_p.h
+++ b/src/gui/text/qtextmarkdownwriter_p.h
@@ -36,6 +36,7 @@ public:
int writeBlock(const QTextBlock &block, bool table, bool ignoreFormat, bool ignoreEmpty);
void writeFrame(const QTextFrame *frame);
+ void writeFrontMatter(const QString &fm);
private:
struct ListInfo {
@@ -43,6 +44,7 @@ private:
};
ListInfo listInfo(QTextList *list);
+ void setLinePrefixForBlockQuote(int level);
private:
QTextStream &m_stream;
@@ -53,6 +55,7 @@ private:
int m_wrappedLineIndent = 0;
int m_lastListIndent = 1;
bool m_doubleNewlineWritten = false;
+ bool m_linePrefixWritten = false;
bool m_indentedCodeBlock = false;
bool m_fencedCodeBlock = false;
};
diff --git a/src/gui/text/qtextobject.cpp b/src/gui/text/qtextobject.cpp
index 7cf9d36a89..6aafdc1a25 100644
--- a/src/gui/text/qtextobject.cpp
+++ b/src/gui/text/qtextobject.cpp
@@ -42,7 +42,7 @@ QT_BEGIN_NAMESPACE
objects, you will also need to reimplement QTextDocument::createObject()
which acts as a factory method for creating text objects.
- \sa QTextDocument, {Text Object Example}
+ \sa QTextDocument
*/
/*!
@@ -165,7 +165,7 @@ QTextDocument *QTextObject::document() const
void QTextBlockGroupPrivate::markBlocksDirty()
{
- for (int i = 0; i < blocks.count(); ++i) {
+ for (int i = 0; i < blocks.size(); ++i) {
const QTextBlock &block = blocks.at(i);
pieceTable->documentChange(block.position(), block.length());
}
@@ -174,7 +174,7 @@ void QTextBlockGroupPrivate::markBlocksDirty()
/*!
\fn QTextBlockGroup::QTextBlockGroup(QTextDocument *document)
- Creates a new new block group for the given \a document.
+ Creates a new block group for the given \a document.
\warning This function should only be called from
QTextDocument::createObject().
@@ -337,14 +337,14 @@ QTextFrameLayoutData::~QTextFrameLayoutData()
/*!
\fn QTextFrame::iterator QTextFrame::iterator::operator++(int)
- The postfix ++ operator (\c{i++}) advances the iterator to the
+ The postfix \c{++} operator (\c{i++}) advances the iterator to the
next item in the text frame, and returns an iterator to the old item.
*/
/*!
\fn QTextFrame::iterator QTextFrame::iterator::operator--(int)
- The postfix -- operator (\c{i--}) makes the preceding item in the
+ The postfix \c{--} operator (\c{i--}) makes the preceding item in the
current frame, and returns an iterator to the old item.
*/
diff --git a/src/gui/text/qtextodfwriter.cpp b/src/gui/text/qtextodfwriter.cpp
index 4214dd11dc..b50771c12f 100644
--- a/src/gui/text/qtextodfwriter.cpp
+++ b/src/gui/text/qtextodfwriter.cpp
@@ -18,9 +18,10 @@
#include "qtexttable.h"
#include "qtextcursor.h"
#include "qtextimagehandler_p.h"
-#include "qzipwriter_p.h"
#include <QDebug>
+#include <QtCore/private/qzipwriter_p.h>
+
QT_BEGIN_NAMESPACE
@@ -262,17 +263,17 @@ void QTextOdfWriter::writeBlock(QXmlStreamWriter &writer, const QTextBlock &bloc
const int listLevel = block.textList()->format().indent();
if (m_listStack.isEmpty() || m_listStack.top() != block.textList()) {
// not the same list we were in.
- while (m_listStack.count() >= listLevel && !m_listStack.isEmpty() && m_listStack.top() != block.textList() ) { // we need to close tags
+ while (m_listStack.size() >= listLevel && !m_listStack.isEmpty() && m_listStack.top() != block.textList() ) { // we need to close tags
m_listStack.pop();
writer.writeEndElement(); // list
- if (m_listStack.count())
+ if (m_listStack.size())
writer.writeEndElement(); // list-item
}
- while (m_listStack.count() < listLevel) {
- if (m_listStack.count())
+ while (m_listStack.size() < listLevel) {
+ if (m_listStack.size())
writer.writeStartElement(textNS, QString::fromLatin1("list-item"));
writer.writeStartElement(textNS, QString::fromLatin1("list"));
- if (m_listStack.count() == listLevel - 1) {
+ if (m_listStack.size() == listLevel - 1) {
m_listStack.push(block.textList());
writer.writeAttribute(textNS, QString::fromLatin1("style-name"), QString::fromLatin1("L%1")
.arg(block.textList()->formatIndex()));
@@ -288,7 +289,7 @@ void QTextOdfWriter::writeBlock(QXmlStreamWriter &writer, const QTextBlock &bloc
while (! m_listStack.isEmpty()) {
m_listStack.pop();
writer.writeEndElement(); // list
- if (m_listStack.count())
+ if (m_listStack.size())
writer.writeEndElement(); // list-item
}
}
@@ -315,7 +316,7 @@ void QTextOdfWriter::writeBlock(QXmlStreamWriter &writer, const QTextBlock &bloc
writer.writeStartElement(textNS, QString::fromLatin1("span"));
QString fragmentText = frag.fragment().text();
- if (fragmentText.length() == 1 && fragmentText[0] == u'\xFFFC') { // its an inline character.
+ if (fragmentText.size() == 1 && fragmentText[0] == u'\xFFFC') { // its an inline character.
writeInlineCharacter(writer, frag.fragment());
writer.writeEndElement(); // span
continue;
@@ -326,8 +327,8 @@ void QTextOdfWriter::writeBlock(QXmlStreamWriter &writer, const QTextBlock &bloc
bool escapeNextSpace = true;
int precedingSpaces = 0;
int exportedIndex = 0;
- for (int i=0; i <= fragmentText.length(); ++i) {
- QChar character = (i == fragmentText.length() ? QChar() : fragmentText.at(i));
+ for (int i=0; i <= fragmentText.size(); ++i) {
+ QChar character = (i == fragmentText.size() ? QChar() : fragmentText.at(i));
bool isSpace = character.unicode() == ' ';
// find more than one space. -> <text:s text:c="2" />
@@ -343,7 +344,7 @@ void QTextOdfWriter::writeBlock(QXmlStreamWriter &writer, const QTextBlock &bloc
exportedIndex = i;
}
- if (i < fragmentText.length()) {
+ if (i < fragmentText.size()) {
if (character.unicode() == 0x2028) { // soft-return
//if (exportedIndex < i)
writer.writeCharacters(fragmentText.mid(exportedIndex, i - exportedIndex));
@@ -644,7 +645,7 @@ void QTextOdfWriter::writeCharacterFormat(QXmlStreamWriter &writer, QTextCharFor
value = QString::number(format.fontWeight());
writer.writeAttribute(foNS, QString::fromLatin1("font-weight"), value);
}
- if (format.hasProperty(QTextFormat::FontFamily))
+ if (format.hasProperty(QTextFormat::OldFontFamily))
writer.writeAttribute(foNS, QString::fromLatin1("font-family"), format.fontFamilies().toStringList().value(0, QString()));
else
writer.writeAttribute(foNS, QString::fromLatin1("font-family"), QString::fromLatin1("Sans")); // Qt default
diff --git a/src/gui/text/qtextoption.cpp b/src/gui/text/qtextoption.cpp
index c5d4874132..b6beadbe91 100644
--- a/src/gui/text/qtextoption.cpp
+++ b/src/gui/text/qtextoption.cpp
@@ -109,7 +109,7 @@ void QTextOption::setTabArray(const QList<qreal> &tabStops)
d = new QTextOptionPrivate;
QList<QTextOption::Tab> tabs;
QTextOption::Tab tab;
- tabs.reserve(tabStops.count());
+ tabs.reserve(tabStops.size());
for (qreal pos : tabStops) {
tab.position = pos;
tabs.append(tab);
@@ -142,7 +142,7 @@ QList<qreal> QTextOption::tabArray() const
if (!d)
return answer;
- answer.reserve(d->tabStops.count());
+ answer.reserve(d->tabStops.size());
QList<QTextOption::Tab>::ConstIterator iter = d->tabStops.constBegin();
while(iter != d->tabStops.constEnd()) {
answer.append( (*iter).position);
@@ -329,7 +329,7 @@ QList<QTextOption::Tab> QTextOption::tabs() const
*/
/*!
- \variable Tab::position
+ \variable QTextOption::Tab::position
Distance from the start of the paragraph.
The position of a tab is from the start of the paragraph which implies that when
the alignment of the paragraph is set to centered, the tab is interpreted to be
diff --git a/src/gui/text/qtexttable.cpp b/src/gui/text/qtexttable.cpp
index 7728abccb7..64b7aa6765 100644
--- a/src/gui/text/qtexttable.cpp
+++ b/src/gui/text/qtexttable.cpp
@@ -939,7 +939,7 @@ void QTextTable::removeColumns(int pos, int num)
QTextTableFormat tfmt = format();
tfmt.setColumns(tfmt.columns()-num);
QList<QTextLength> columnWidths = tfmt.columnWidthConstraints();
- if (columnWidths.count() > pos) {
+ if (columnWidths.size() > pos) {
columnWidths.remove(pos, num);
tfmt.setColumnWidthConstraints (columnWidths);
}
diff --git a/src/gui/text/qzip.cpp b/src/gui/text/qzip.cpp
deleted file mode 100644
index a3ef172902..0000000000
--- a/src/gui/text/qzip.cpp
+++ /dev/null
@@ -1,1352 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include <qglobal.h>
-
-#ifndef QT_NO_TEXTODFWRITER
-
-#include "qzipreader_p.h"
-#include "qzipwriter_p.h"
-#include <qdatetime.h>
-#include <qendian.h>
-#include <qdebug.h>
-#include <qdir.h>
-
-#include <memory>
-
-#include <zlib.h>
-
-// Zip standard version for archives handled by this API
-// (actually, the only basic support of this version is implemented but it is enough for now)
-#define ZIP_VERSION 20
-
-#if 0
-#define ZDEBUG qDebug
-#else
-#define ZDEBUG if (0) qDebug
-#endif
-
-QT_BEGIN_NAMESPACE
-
-static inline uint readUInt(const uchar *data)
-{
- return (data[0]) + (data[1]<<8) + (data[2]<<16) + (data[3]<<24);
-}
-
-static inline ushort readUShort(const uchar *data)
-{
- return (data[0]) + (data[1]<<8);
-}
-
-static inline void writeUInt(uchar *data, uint i)
-{
- data[0] = i & 0xff;
- data[1] = (i>>8) & 0xff;
- data[2] = (i>>16) & 0xff;
- data[3] = (i>>24) & 0xff;
-}
-
-static inline void writeUShort(uchar *data, ushort i)
-{
- data[0] = i & 0xff;
- data[1] = (i>>8) & 0xff;
-}
-
-static inline void copyUInt(uchar *dest, const uchar *src)
-{
- dest[0] = src[0];
- dest[1] = src[1];
- dest[2] = src[2];
- dest[3] = src[3];
-}
-
-static inline void copyUShort(uchar *dest, const uchar *src)
-{
- dest[0] = src[0];
- dest[1] = src[1];
-}
-
-static void writeMSDosDate(uchar *dest, const QDateTime& dt)
-{
- if (dt.isValid()) {
- quint16 time =
- (dt.time().hour() << 11) // 5 bit hour
- | (dt.time().minute() << 5) // 6 bit minute
- | (dt.time().second() >> 1); // 5 bit double seconds
-
- dest[0] = time & 0xff;
- dest[1] = time >> 8;
-
- quint16 date =
- ((dt.date().year() - 1980) << 9) // 7 bit year 1980-based
- | (dt.date().month() << 5) // 4 bit month
- | (dt.date().day()); // 5 bit day
-
- dest[2] = char(date);
- dest[3] = char(date >> 8);
- } else {
- dest[0] = 0;
- dest[1] = 0;
- dest[2] = 0;
- dest[3] = 0;
- }
-}
-
-static int inflate(Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
-{
- z_stream stream;
- int err;
-
- stream.next_in = const_cast<Bytef*>(source);
- stream.avail_in = (uInt)sourceLen;
- if ((uLong)stream.avail_in != sourceLen)
- return Z_BUF_ERROR;
-
- stream.next_out = dest;
- stream.avail_out = (uInt)*destLen;
- if ((uLong)stream.avail_out != *destLen)
- return Z_BUF_ERROR;
-
- stream.zalloc = (alloc_func)nullptr;
- stream.zfree = (free_func)nullptr;
-
- err = inflateInit2(&stream, -MAX_WBITS);
- if (err != Z_OK)
- return err;
-
- err = inflate(&stream, Z_FINISH);
- if (err != Z_STREAM_END) {
- inflateEnd(&stream);
- if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
- return Z_DATA_ERROR;
- return err;
- }
- *destLen = stream.total_out;
-
- err = inflateEnd(&stream);
- return err;
-}
-
-static int deflate (Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
-{
- z_stream stream;
- int err;
-
- stream.next_in = const_cast<Bytef*>(source);
- stream.avail_in = (uInt)sourceLen;
- stream.next_out = dest;
- stream.avail_out = (uInt)*destLen;
- if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
-
- stream.zalloc = (alloc_func)nullptr;
- stream.zfree = (free_func)nullptr;
- stream.opaque = (voidpf)nullptr;
-
- err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
- if (err != Z_OK) return err;
-
- err = deflate(&stream, Z_FINISH);
- if (err != Z_STREAM_END) {
- deflateEnd(&stream);
- return err == Z_OK ? Z_BUF_ERROR : err;
- }
- *destLen = stream.total_out;
-
- err = deflateEnd(&stream);
- return err;
-}
-
-
-namespace WindowsFileAttributes {
-enum {
- Dir = 0x10, // FILE_ATTRIBUTE_DIRECTORY
- File = 0x80, // FILE_ATTRIBUTE_NORMAL
- TypeMask = 0x90,
-
- ReadOnly = 0x01, // FILE_ATTRIBUTE_READONLY
- PermMask = 0x01
-};
-}
-
-namespace UnixFileAttributes {
-enum {
- Dir = 0040000, // __S_IFDIR
- File = 0100000, // __S_IFREG
- SymLink = 0120000, // __S_IFLNK
- TypeMask = 0170000, // __S_IFMT
-
- ReadUser = 0400, // __S_IRUSR
- WriteUser = 0200, // __S_IWUSR
- ExeUser = 0100, // __S_IXUSR
- ReadGroup = 0040, // __S_IRGRP
- WriteGroup = 0020, // __S_IWGRP
- ExeGroup = 0010, // __S_IXGRP
- ReadOther = 0004, // __S_IROTH
- WriteOther = 0002, // __S_IWOTH
- ExeOther = 0001, // __S_IXOTH
- PermMask = 0777
-};
-}
-
-static QFile::Permissions modeToPermissions(quint32 mode)
-{
- QFile::Permissions ret;
- if (mode & UnixFileAttributes::ReadUser)
- ret |= QFile::ReadOwner | QFile::ReadUser;
- if (mode & UnixFileAttributes::WriteUser)
- ret |= QFile::WriteOwner | QFile::WriteUser;
- if (mode & UnixFileAttributes::ExeUser)
- ret |= QFile::ExeOwner | QFile::ExeUser;
- if (mode & UnixFileAttributes::ReadGroup)
- ret |= QFile::ReadGroup;
- if (mode & UnixFileAttributes::WriteGroup)
- ret |= QFile::WriteGroup;
- if (mode & UnixFileAttributes::ExeGroup)
- ret |= QFile::ExeGroup;
- if (mode & UnixFileAttributes::ReadOther)
- ret |= QFile::ReadOther;
- if (mode & UnixFileAttributes::WriteOther)
- ret |= QFile::WriteOther;
- if (mode & UnixFileAttributes::ExeOther)
- ret |= QFile::ExeOther;
- return ret;
-}
-
-static quint32 permissionsToMode(QFile::Permissions perms)
-{
- quint32 mode = 0;
- if (perms & (QFile::ReadOwner | QFile::ReadUser))
- mode |= UnixFileAttributes::ReadUser;
- if (perms & (QFile::WriteOwner | QFile::WriteUser))
- mode |= UnixFileAttributes::WriteUser;
- if (perms & (QFile::ExeOwner | QFile::ExeUser))
- mode |= UnixFileAttributes::WriteUser;
- if (perms & QFile::ReadGroup)
- mode |= UnixFileAttributes::ReadGroup;
- if (perms & QFile::WriteGroup)
- mode |= UnixFileAttributes::WriteGroup;
- if (perms & QFile::ExeGroup)
- mode |= UnixFileAttributes::ExeGroup;
- if (perms & QFile::ReadOther)
- mode |= UnixFileAttributes::ReadOther;
- if (perms & QFile::WriteOther)
- mode |= UnixFileAttributes::WriteOther;
- if (perms & QFile::ExeOther)
- mode |= UnixFileAttributes::ExeOther;
- return mode;
-}
-
-static QDateTime readMSDosDate(const uchar *src)
-{
- uint dosDate = readUInt(src);
- quint64 uDate;
- uDate = (quint64)(dosDate >> 16);
- uint tm_mday = (uDate & 0x1f);
- uint tm_mon = ((uDate & 0x1E0) >> 5);
- uint tm_year = (((uDate & 0x0FE00) >> 9) + 1980);
- uint tm_hour = ((dosDate & 0xF800) >> 11);
- uint tm_min = ((dosDate & 0x7E0) >> 5);
- uint tm_sec = ((dosDate & 0x1f) << 1);
-
- return QDateTime(QDate(tm_year, tm_mon, tm_mday), QTime(tm_hour, tm_min, tm_sec));
-}
-
-// for details, see http://www.pkware.com/documents/casestudies/APPNOTE.TXT
-
-enum HostOS {
- HostFAT = 0,
- HostAMIGA = 1,
- HostVMS = 2, // VAX/VMS
- HostUnix = 3,
- HostVM_CMS = 4,
- HostAtari = 5, // what if it's a minix filesystem? [cjh]
- HostHPFS = 6, // filesystem used by OS/2 (and NT 3.x)
- HostMac = 7,
- HostZ_System = 8,
- HostCPM = 9,
- HostTOPS20 = 10, // pkzip 2.50 NTFS
- HostNTFS = 11, // filesystem used by Windows NT
- HostQDOS = 12, // SMS/QDOS
- HostAcorn = 13, // Archimedes Acorn RISC OS
- HostVFAT = 14, // filesystem used by Windows 95, NT
- HostMVS = 15,
- HostBeOS = 16, // hybrid POSIX/database filesystem
- HostTandem = 17,
- HostOS400 = 18,
- HostOSX = 19
-};
-Q_DECLARE_TYPEINFO(HostOS, Q_PRIMITIVE_TYPE);
-
-enum GeneralPurposeFlag {
- Encrypted = 0x01,
- AlgTune1 = 0x02,
- AlgTune2 = 0x04,
- HasDataDescriptor = 0x08,
- PatchedData = 0x20,
- StrongEncrypted = 0x40,
- Utf8Names = 0x0800,
- CentralDirectoryEncrypted = 0x2000
-};
-Q_DECLARE_TYPEINFO(GeneralPurposeFlag, Q_PRIMITIVE_TYPE);
-
-enum CompressionMethod {
- CompressionMethodStored = 0,
- CompressionMethodShrunk = 1,
- CompressionMethodReduced1 = 2,
- CompressionMethodReduced2 = 3,
- CompressionMethodReduced3 = 4,
- CompressionMethodReduced4 = 5,
- CompressionMethodImploded = 6,
- CompressionMethodReservedTokenizing = 7, // reserved for tokenizing
- CompressionMethodDeflated = 8,
- CompressionMethodDeflated64 = 9,
- CompressionMethodPKImploding = 10,
-
- CompressionMethodBZip2 = 12,
-
- CompressionMethodLZMA = 14,
-
- CompressionMethodTerse = 18,
- CompressionMethodLz77 = 19,
-
- CompressionMethodJpeg = 96,
- CompressionMethodWavPack = 97,
- CompressionMethodPPMd = 98,
- CompressionMethodWzAES = 99
-};
-Q_DECLARE_TYPEINFO(CompressionMethod, Q_PRIMITIVE_TYPE);
-
-struct LocalFileHeader
-{
- uchar signature[4]; // 0x04034b50
- uchar version_needed[2];
- uchar general_purpose_bits[2];
- uchar compression_method[2];
- uchar last_mod_file[4];
- uchar crc_32[4];
- uchar compressed_size[4];
- uchar uncompressed_size[4];
- uchar file_name_length[2];
- uchar extra_field_length[2];
-};
-Q_DECLARE_TYPEINFO(LocalFileHeader, Q_PRIMITIVE_TYPE);
-
-struct DataDescriptor
-{
- uchar crc_32[4];
- uchar compressed_size[4];
- uchar uncompressed_size[4];
-};
-Q_DECLARE_TYPEINFO(DataDescriptor, Q_PRIMITIVE_TYPE);
-
-struct CentralFileHeader
-{
- uchar signature[4]; // 0x02014b50
- uchar version_made[2];
- uchar version_needed[2];
- uchar general_purpose_bits[2];
- uchar compression_method[2];
- uchar last_mod_file[4];
- uchar crc_32[4];
- uchar compressed_size[4];
- uchar uncompressed_size[4];
- uchar file_name_length[2];
- uchar extra_field_length[2];
- uchar file_comment_length[2];
- uchar disk_start[2];
- uchar internal_file_attributes[2];
- uchar external_file_attributes[4];
- uchar offset_local_header[4];
-};
-Q_DECLARE_TYPEINFO(CentralFileHeader, Q_PRIMITIVE_TYPE);
-
-struct EndOfDirectory
-{
- uchar signature[4]; // 0x06054b50
- uchar this_disk[2];
- uchar start_of_directory_disk[2];
- uchar num_dir_entries_this_disk[2];
- uchar num_dir_entries[2];
- uchar directory_size[4];
- uchar dir_start_offset[4];
- uchar comment_length[2];
-};
-Q_DECLARE_TYPEINFO(EndOfDirectory, Q_PRIMITIVE_TYPE);
-
-struct FileHeader
-{
- CentralFileHeader h;
- QByteArray file_name;
- QByteArray extra_field;
- QByteArray file_comment;
-};
-Q_DECLARE_TYPEINFO(FileHeader, Q_RELOCATABLE_TYPE);
-
-class QZipPrivate
-{
-public:
- QZipPrivate(QIODevice *device, bool ownDev)
- : device(device), ownDevice(ownDev), dirtyFileTree(true), start_of_directory(0)
- {
- }
-
- ~QZipPrivate()
- {
- if (ownDevice)
- delete device;
- }
-
- QZipReader::FileInfo fillFileInfo(int index) const;
-
- QIODevice *device;
- bool ownDevice;
- bool dirtyFileTree;
- QList<FileHeader> fileHeaders;
- QByteArray comment;
- uint start_of_directory;
-};
-
-QZipReader::FileInfo QZipPrivate::fillFileInfo(int index) const
-{
- QZipReader::FileInfo fileInfo;
- FileHeader header = fileHeaders.at(index);
- quint32 mode = readUInt(header.h.external_file_attributes);
- const HostOS hostOS = HostOS(readUShort(header.h.version_made) >> 8);
- switch (hostOS) {
- case HostUnix:
- mode = (mode >> 16) & 0xffff;
- switch (mode & UnixFileAttributes::TypeMask) {
- case UnixFileAttributes::SymLink:
- fileInfo.isSymLink = true;
- break;
- case UnixFileAttributes::Dir:
- fileInfo.isDir = true;
- break;
- case UnixFileAttributes::File:
- default: // ### just for the case; should we warn?
- fileInfo.isFile = true;
- break;
- }
- fileInfo.permissions = modeToPermissions(mode);
- break;
- case HostFAT:
- case HostNTFS:
- case HostHPFS:
- case HostVFAT:
- switch (mode & WindowsFileAttributes::TypeMask) {
- case WindowsFileAttributes::Dir:
- fileInfo.isDir = true;
- break;
- case WindowsFileAttributes::File:
- default:
- fileInfo.isFile = true;
- break;
- }
- fileInfo.permissions |= QFile::ReadOwner | QFile::ReadUser | QFile::ReadGroup | QFile::ReadOther;
- if ((mode & WindowsFileAttributes::ReadOnly) == 0)
- fileInfo.permissions |= QFile::WriteOwner | QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther;
- if (fileInfo.isDir)
- fileInfo.permissions |= QFile::ExeOwner | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther;
- break;
- default:
- qWarning("QZip: Zip entry format at %d is not supported.", index);
- return fileInfo; // we don't support anything else
- }
-
- ushort general_purpose_bits = readUShort(header.h.general_purpose_bits);
- // if bit 11 is set, the filename and comment fields must be encoded using UTF-8
- const bool inUtf8 = (general_purpose_bits & Utf8Names) != 0;
- fileInfo.filePath = inUtf8 ? QString::fromUtf8(header.file_name) : QString::fromLocal8Bit(header.file_name);
- fileInfo.crc = readUInt(header.h.crc_32);
- fileInfo.size = readUInt(header.h.uncompressed_size);
- fileInfo.lastModified = readMSDosDate(header.h.last_mod_file);
-
- // fix the file path, if broken (convert separators, eat leading and trailing ones)
- fileInfo.filePath = QDir::fromNativeSeparators(fileInfo.filePath);
- QStringView filePathRef(fileInfo.filePath);
- while (filePathRef.startsWith(u'.') || filePathRef.startsWith(u'/'))
- filePathRef = filePathRef.mid(1);
- while (filePathRef.endsWith(u'/'))
- filePathRef.chop(1);
-
- fileInfo.filePath = filePathRef.toString();
- return fileInfo;
-}
-
-class QZipReaderPrivate : public QZipPrivate
-{
-public:
- QZipReaderPrivate(QIODevice *device, bool ownDev)
- : QZipPrivate(device, ownDev), status(QZipReader::NoError)
- {
- }
-
- void scanFiles();
-
- QZipReader::Status status;
-};
-
-class QZipWriterPrivate : public QZipPrivate
-{
-public:
- QZipWriterPrivate(QIODevice *device, bool ownDev)
- : QZipPrivate(device, ownDev),
- status(QZipWriter::NoError),
- permissions(QFile::ReadOwner | QFile::WriteOwner),
- compressionPolicy(QZipWriter::AlwaysCompress)
- {
- }
-
- QZipWriter::Status status;
- QFile::Permissions permissions;
- QZipWriter::CompressionPolicy compressionPolicy;
-
- enum EntryType { Directory, File, Symlink };
-
- void addEntry(EntryType type, const QString &fileName, const QByteArray &contents);
-};
-
-static LocalFileHeader toLocalHeader(const CentralFileHeader &ch)
-{
- LocalFileHeader h;
- writeUInt(h.signature, 0x04034b50);
- copyUShort(h.version_needed, ch.version_needed);
- copyUShort(h.general_purpose_bits, ch.general_purpose_bits);
- copyUShort(h.compression_method, ch.compression_method);
- copyUInt(h.last_mod_file, ch.last_mod_file);
- copyUInt(h.crc_32, ch.crc_32);
- copyUInt(h.compressed_size, ch.compressed_size);
- copyUInt(h.uncompressed_size, ch.uncompressed_size);
- copyUShort(h.file_name_length, ch.file_name_length);
- copyUShort(h.extra_field_length, ch.extra_field_length);
- return h;
-}
-
-void QZipReaderPrivate::scanFiles()
-{
- if (!dirtyFileTree)
- return;
-
- if (! (device->isOpen() || device->open(QIODevice::ReadOnly))) {
- status = QZipReader::FileOpenError;
- return;
- }
-
- if ((device->openMode() & QIODevice::ReadOnly) == 0) { // only read the index from readable files.
- status = QZipReader::FileReadError;
- return;
- }
-
- dirtyFileTree = false;
- uchar tmp[4];
- device->read((char *)tmp, 4);
- if (readUInt(tmp) != 0x04034b50) {
- qWarning("QZip: not a zip file!");
- return;
- }
-
- // find EndOfDirectory header
- int i = 0;
- int start_of_directory = -1;
- int num_dir_entries = 0;
- EndOfDirectory eod;
- while (start_of_directory == -1) {
- const int pos = device->size() - int(sizeof(EndOfDirectory)) - i;
- if (pos < 0 || i > 65535) {
- qWarning("QZip: EndOfDirectory not found");
- return;
- }
-
- device->seek(pos);
- device->read((char *)&eod, sizeof(EndOfDirectory));
- if (readUInt(eod.signature) == 0x06054b50)
- break;
- ++i;
- }
-
- // have the eod
- start_of_directory = readUInt(eod.dir_start_offset);
- num_dir_entries = readUShort(eod.num_dir_entries);
- ZDEBUG("start_of_directory at %d, num_dir_entries=%d", start_of_directory, num_dir_entries);
- int comment_length = readUShort(eod.comment_length);
- if (comment_length != i)
- qWarning("QZip: failed to parse zip file.");
- comment = device->read(qMin(comment_length, i));
-
-
- device->seek(start_of_directory);
- for (i = 0; i < num_dir_entries; ++i) {
- FileHeader header;
- int read = device->read((char *) &header.h, sizeof(CentralFileHeader));
- if (read < (int)sizeof(CentralFileHeader)) {
- qWarning("QZip: Failed to read complete header, index may be incomplete");
- break;
- }
- if (readUInt(header.h.signature) != 0x02014b50) {
- qWarning("QZip: invalid header signature, index may be incomplete");
- break;
- }
-
- int l = readUShort(header.h.file_name_length);
- header.file_name = device->read(l);
- if (header.file_name.length() != l) {
- qWarning("QZip: Failed to read filename from zip index, index may be incomplete");
- break;
- }
- l = readUShort(header.h.extra_field_length);
- header.extra_field = device->read(l);
- if (header.extra_field.length() != l) {
- qWarning("QZip: Failed to read extra field in zip file, skipping file, index may be incomplete");
- break;
- }
- l = readUShort(header.h.file_comment_length);
- header.file_comment = device->read(l);
- if (header.file_comment.length() != l) {
- qWarning("QZip: Failed to read read file comment, index may be incomplete");
- break;
- }
-
- ZDEBUG("found file '%s'", header.file_name.data());
- fileHeaders.append(header);
- }
-}
-
-void QZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const QByteArray &contents/*, QFile::Permissions permissions, QZip::Method m*/)
-{
-#ifndef NDEBUG
- static const char *const entryTypes[] = {
- "directory",
- "file ",
- "symlink " };
- ZDEBUG() << "adding" << entryTypes[type] <<":" << fileName.toUtf8().data() << (type == 2 ? QByteArray(" -> " + contents).constData() : "");
-#endif
-
- if (! (device->isOpen() || device->open(QIODevice::WriteOnly))) {
- status = QZipWriter::FileOpenError;
- return;
- }
- device->seek(start_of_directory);
-
- // don't compress small files
- QZipWriter::CompressionPolicy compression = compressionPolicy;
- if (compressionPolicy == QZipWriter::AutoCompress) {
- if (contents.length() < 64)
- compression = QZipWriter::NeverCompress;
- else
- compression = QZipWriter::AlwaysCompress;
- }
-
- FileHeader header;
- memset(&header.h, 0, sizeof(CentralFileHeader));
- writeUInt(header.h.signature, 0x02014b50);
-
- writeUShort(header.h.version_needed, ZIP_VERSION);
- writeUInt(header.h.uncompressed_size, contents.length());
- writeMSDosDate(header.h.last_mod_file, QDateTime::currentDateTime());
- QByteArray data = contents;
- if (compression == QZipWriter::AlwaysCompress) {
- writeUShort(header.h.compression_method, CompressionMethodDeflated);
-
- ulong len = contents.length();
- // shamelessly copied form zlib
- len += (len >> 12) + (len >> 14) + 11;
- int res;
- do {
- data.resize(len);
- res = deflate((uchar*)data.data(), &len, (const uchar*)contents.constData(), contents.length());
-
- switch (res) {
- case Z_OK:
- data.resize(len);
- break;
- case Z_MEM_ERROR:
- qWarning("QZip: Z_MEM_ERROR: Not enough memory to compress file, skipping");
- data.resize(0);
- break;
- case Z_BUF_ERROR:
- len *= 2;
- break;
- }
- } while (res == Z_BUF_ERROR);
- }
-// TODO add a check if data.length() > contents.length(). Then try to store the original and revert the compression method to be uncompressed
- writeUInt(header.h.compressed_size, data.length());
- uint crc_32 = ::crc32(0, nullptr, 0);
- crc_32 = ::crc32(crc_32, (const uchar *)contents.constData(), contents.length());
- writeUInt(header.h.crc_32, crc_32);
-
- // if bit 11 is set, the filename and comment fields must be encoded using UTF-8
- ushort general_purpose_bits = Utf8Names; // always use utf-8
- writeUShort(header.h.general_purpose_bits, general_purpose_bits);
-
- const bool inUtf8 = (general_purpose_bits & Utf8Names) != 0;
- header.file_name = inUtf8 ? fileName.toUtf8() : fileName.toLocal8Bit();
- if (header.file_name.size() > 0xffff) {
- qWarning("QZip: Filename is too long, chopping it to 65535 bytes");
- header.file_name = header.file_name.left(0xffff); // ### don't break the utf-8 sequence, if any
- }
- if (header.file_comment.size() + header.file_name.size() > 0xffff) {
- qWarning("QZip: File comment is too long, chopping it to 65535 bytes");
- header.file_comment.truncate(0xffff - header.file_name.size()); // ### don't break the utf-8 sequence, if any
- }
- writeUShort(header.h.file_name_length, header.file_name.length());
- //h.extra_field_length[2];
-
- writeUShort(header.h.version_made, HostUnix << 8);
- //uchar internal_file_attributes[2];
- //uchar external_file_attributes[4];
- quint32 mode = permissionsToMode(permissions);
- switch (type) {
- case Symlink:
- mode |= UnixFileAttributes::SymLink;
- break;
- case Directory:
- mode |= UnixFileAttributes::Dir;
- break;
- case File:
- mode |= UnixFileAttributes::File;
- break;
- default:
- Q_UNREACHABLE();
- break;
- }
- writeUInt(header.h.external_file_attributes, mode << 16);
- writeUInt(header.h.offset_local_header, start_of_directory);
-
-
- fileHeaders.append(header);
-
- LocalFileHeader h = toLocalHeader(header.h);
- device->write((const char *)&h, sizeof(LocalFileHeader));
- device->write(header.file_name);
- device->write(data);
- start_of_directory = device->pos();
- dirtyFileTree = true;
-}
-
-////////////////////////////// Reader
-
-/*!
- \class QZipReader::FileInfo
- \internal
- Represents one entry in the zip table of contents.
-*/
-
-/*!
- \variable FileInfo::filePath
- The full filepath inside the archive.
-*/
-
-/*!
- \variable FileInfo::isDir
- A boolean type indicating if the entry is a directory.
-*/
-
-/*!
- \variable FileInfo::isFile
- A boolean type, if it is one this entry is a file.
-*/
-
-/*!
- \variable FileInfo::isSymLink
- A boolean type, if it is one this entry is symbolic link.
-*/
-
-/*!
- \variable FileInfo::permissions
- A list of flags for the permissions of this entry.
-*/
-
-/*!
- \variable FileInfo::crc
- The calculated checksum as a crc type.
-*/
-
-/*!
- \variable FileInfo::size
- The total size of the unpacked content.
-*/
-
-/*!
- \class QZipReader
- \internal
- \since 4.5
-
- \brief the QZipReader class provides a way to inspect the contents of a zip
- archive and extract individual files from it.
-
- QZipReader can be used to read a zip archive either from a file or from any
- device. An in-memory QBuffer for instance. The reader can be used to read
- which files are in the archive using fileInfoList() and entryInfoAt() but
- also to extract individual files using fileData() or even to extract all
- files in the archive using extractAll()
-*/
-
-/*!
- Create a new zip archive that operates on the \a fileName. The file will be
- opened with the \a mode.
-*/
-QZipReader::QZipReader(const QString &archive, QIODevice::OpenMode mode)
-{
- auto f = std::make_unique<QFile>(archive);
- const bool result = f->open(mode);
- QZipReader::Status status;
- const QFileDevice::FileError error = f->error();
- if (result && error == QFile::NoError) {
- status = NoError;
- } else {
- if (error == QFile::ReadError)
- status = FileReadError;
- else if (error == QFile::OpenError)
- status = FileOpenError;
- else if (error == QFile::PermissionsError)
- status = FilePermissionsError;
- else
- status = FileError;
- }
-
- d = new QZipReaderPrivate(f.get(), /*ownDevice=*/true);
- Q_UNUSED(f.release());
- d->status = status;
-}
-
-/*!
- Create a new zip archive that operates on the archive found in \a device.
- You have to open the device previous to calling the constructor and only a
- device that is readable will be scanned for zip filecontent.
- */
-QZipReader::QZipReader(QIODevice *device)
- : d(new QZipReaderPrivate(device, /*ownDevice=*/false))
-{
- Q_ASSERT(device);
-}
-
-/*!
- Destructor
-*/
-QZipReader::~QZipReader()
-{
- close();
- delete d;
-}
-
-/*!
- Returns device used for reading zip archive.
-*/
-QIODevice* QZipReader::device() const
-{
- return d->device;
-}
-
-/*!
- Returns \c true if the user can read the file; otherwise returns \c false.
-*/
-bool QZipReader::isReadable() const
-{
- return d->device->isReadable();
-}
-
-/*!
- Returns \c true if the file exists; otherwise returns \c false.
-*/
-bool QZipReader::exists() const
-{
- QFile *f = qobject_cast<QFile*> (d->device);
- if (f == nullptr)
- return true;
- return f->exists();
-}
-
-/*!
- Returns the list of files the archive contains.
-*/
-QList<QZipReader::FileInfo> QZipReader::fileInfoList() const
-{
- d->scanFiles();
- QList<FileInfo> files;
- const int numFileHeaders = d->fileHeaders.size();
- files.reserve(numFileHeaders);
- for (int i = 0; i < numFileHeaders; ++i)
- files.append(d->fillFileInfo(i));
- return files;
-
-}
-
-/*!
- Return the number of items in the zip archive.
-*/
-int QZipReader::count() const
-{
- d->scanFiles();
- return d->fileHeaders.count();
-}
-
-/*!
- Returns a FileInfo of an entry in the zipfile.
- The \a index is the index into the directory listing of the zipfile.
- Returns an invalid FileInfo if \a index is out of boundaries.
-
- \sa fileInfoList()
-*/
-QZipReader::FileInfo QZipReader::entryInfoAt(int index) const
-{
- d->scanFiles();
- if (index >= 0 && index < d->fileHeaders.count())
- return d->fillFileInfo(index);
- return QZipReader::FileInfo();
-}
-
-/*!
- Fetch the file contents from the zip archive and return the uncompressed bytes.
-*/
-QByteArray QZipReader::fileData(const QString &fileName) const
-{
- d->scanFiles();
- int i;
- for (i = 0; i < d->fileHeaders.size(); ++i) {
- if (QString::fromLocal8Bit(d->fileHeaders.at(i).file_name) == fileName)
- break;
- }
- if (i == d->fileHeaders.size())
- return QByteArray();
-
- FileHeader header = d->fileHeaders.at(i);
-
- ushort version_needed = readUShort(header.h.version_needed);
- if (version_needed > ZIP_VERSION) {
- qWarning("QZip: .ZIP specification version %d implementationis needed to extract the data.", version_needed);
- return QByteArray();
- }
-
- ushort general_purpose_bits = readUShort(header.h.general_purpose_bits);
- int compressed_size = readUInt(header.h.compressed_size);
- int uncompressed_size = readUInt(header.h.uncompressed_size);
- int start = readUInt(header.h.offset_local_header);
- //qDebug("uncompressing file %d: local header at %d", i, start);
-
- d->device->seek(start);
- LocalFileHeader lh;
- d->device->read((char *)&lh, sizeof(LocalFileHeader));
- uint skip = readUShort(lh.file_name_length) + readUShort(lh.extra_field_length);
- d->device->seek(d->device->pos() + skip);
-
- int compression_method = readUShort(lh.compression_method);
- //qDebug("file=%s: compressed_size=%d, uncompressed_size=%d", fileName.toLocal8Bit().data(), compressed_size, uncompressed_size);
-
- if ((general_purpose_bits & Encrypted) != 0) {
- qWarning("QZip: Unsupported encryption method is needed to extract the data.");
- return QByteArray();
- }
-
- //qDebug("file at %lld", d->device->pos());
- QByteArray compressed = d->device->read(compressed_size);
- if (compression_method == CompressionMethodStored) {
- // no compression
- compressed.truncate(uncompressed_size);
- return compressed;
- } else if (compression_method == CompressionMethodDeflated) {
- // Deflate
- //qDebug("compressed=%d", compressed.size());
- compressed.truncate(compressed_size);
- QByteArray baunzip;
- ulong len = qMax(uncompressed_size, 1);
- int res;
- do {
- baunzip.resize(len);
- res = inflate((uchar*)baunzip.data(), &len,
- (const uchar*)compressed.constData(), compressed_size);
-
- switch (res) {
- case Z_OK:
- if ((int)len != baunzip.size())
- baunzip.resize(len);
- break;
- case Z_MEM_ERROR:
- qWarning("QZip: Z_MEM_ERROR: Not enough memory");
- break;
- case Z_BUF_ERROR:
- len *= 2;
- break;
- case Z_DATA_ERROR:
- qWarning("QZip: Z_DATA_ERROR: Input data is corrupted");
- break;
- }
- } while (res == Z_BUF_ERROR);
- return baunzip;
- }
-
- qWarning("QZip: Unsupported compression method %d is needed to extract the data.", compression_method);
- return QByteArray();
-}
-
-/*!
- Extracts the full contents of the zip file into \a destinationDir on
- the local filesystem.
- In case writing or linking a file fails, the extraction will be aborted.
-*/
-bool QZipReader::extractAll(const QString &destinationDir) const
-{
- QDir baseDir(destinationDir);
-
- // create directories first
- const QList<FileInfo> allFiles = fileInfoList();
- bool foundDirs = false;
- bool hasDirs = false;
- for (const FileInfo &fi : allFiles) {
- const QString absPath = destinationDir + QDir::separator() + fi.filePath;
- if (fi.isDir) {
- foundDirs = true;
- if (!baseDir.mkpath(fi.filePath))
- return false;
- if (!QFile::setPermissions(absPath, fi.permissions))
- return false;
- } else if (!hasDirs && fi.filePath.contains(u"/")) {
- // filePath does not have leading or trailing '/', so if we find
- // one, than the file path contains directories.
- hasDirs = true;
- }
- }
-
- // Some zip archives can be broken in the sense that they do not report
- // separate entries for directories, only for files. In this case we
- // need to recreate directory structure based on the file paths.
- if (hasDirs && !foundDirs) {
- for (const FileInfo &fi : allFiles) {
- const auto dirPath = fi.filePath.left(fi.filePath.lastIndexOf(u"/"));
- if (!baseDir.mkpath(dirPath))
- return false;
- // We will leave the directory permissions default in this case,
- // because setting dir permissions based on file is incorrect
- }
- }
-
- // set up symlinks
- for (const FileInfo &fi : allFiles) {
- const QString absPath = destinationDir + QDir::separator() + fi.filePath;
- if (fi.isSymLink) {
- QString destination = QFile::decodeName(fileData(fi.filePath));
- if (destination.isEmpty())
- return false;
- QFileInfo linkFi(absPath);
- if (!QFile::exists(linkFi.absolutePath()))
- QDir::root().mkpath(linkFi.absolutePath());
- if (!QFile::link(destination, absPath))
- return false;
- /* cannot change permission of links
- if (!QFile::setPermissions(absPath, fi.permissions))
- return false;
- */
- }
- }
-
- for (const FileInfo &fi : allFiles) {
- const QString absPath = destinationDir + QDir::separator() + fi.filePath;
- if (fi.isFile) {
- QFile f(absPath);
- if (!f.open(QIODevice::WriteOnly))
- return false;
- f.write(fileData(fi.filePath));
- f.setPermissions(fi.permissions);
- f.close();
- }
- }
-
- return true;
-}
-
-/*!
- \enum QZipReader::Status
-
- The following status values are possible:
-
- \value NoError No error occurred.
- \value FileReadError An error occurred when reading from the file.
- \value FileOpenError The file could not be opened.
- \value FilePermissionsError The file could not be accessed.
- \value FileError Another file error occurred.
-*/
-
-/*!
- Returns a status code indicating the first error that was met by QZipReader,
- or QZipReader::NoError if no error occurred.
-*/
-QZipReader::Status QZipReader::status() const
-{
- return d->status;
-}
-
-/*!
- Close the zip file.
-*/
-void QZipReader::close()
-{
- d->device->close();
-}
-
-////////////////////////////// Writer
-
-/*!
- \class QZipWriter
- \internal
- \since 4.5
-
- \brief the QZipWriter class provides a way to create a new zip archive.
-
- QZipWriter can be used to create a zip archive containing any number of files
- and directories. The files in the archive will be compressed in a way that is
- compatible with common zip reader applications.
-*/
-
-
-/*!
- Create a new zip archive that operates on the \a archive filename. The file will
- be opened with the \a mode.
- \sa isValid()
-*/
-QZipWriter::QZipWriter(const QString &fileName, QIODevice::OpenMode mode)
-{
- auto f = std::make_unique<QFile>(fileName);
- QZipWriter::Status status;
- if (f->open(mode) && f->error() == QFile::NoError)
- status = QZipWriter::NoError;
- else {
- if (f->error() == QFile::WriteError)
- status = QZipWriter::FileWriteError;
- else if (f->error() == QFile::OpenError)
- status = QZipWriter::FileOpenError;
- else if (f->error() == QFile::PermissionsError)
- status = QZipWriter::FilePermissionsError;
- else
- status = QZipWriter::FileError;
- }
-
- d = new QZipWriterPrivate(f.get(), /*ownDevice=*/true);
- Q_UNUSED(f.release());
- d->status = status;
-}
-
-/*!
- Create a new zip archive that operates on the archive found in \a device.
- You have to open the device previous to calling the constructor and
- only a device that is readable will be scanned for zip filecontent.
- */
-QZipWriter::QZipWriter(QIODevice *device)
- : d(new QZipWriterPrivate(device, /*ownDevice=*/false))
-{
- Q_ASSERT(device);
-}
-
-QZipWriter::~QZipWriter()
-{
- close();
- delete d;
-}
-
-/*!
- Returns device used for writing zip archive.
-*/
-QIODevice* QZipWriter::device() const
-{
- return d->device;
-}
-
-/*!
- Returns \c true if the user can write to the archive; otherwise returns \c false.
-*/
-bool QZipWriter::isWritable() const
-{
- return d->device->isWritable();
-}
-
-/*!
- Returns \c true if the file exists; otherwise returns \c false.
-*/
-bool QZipWriter::exists() const
-{
- QFile *f = qobject_cast<QFile*> (d->device);
- if (f == nullptr)
- return true;
- return f->exists();
-}
-
-/*!
- \enum QZipWriter::Status
-
- The following status values are possible:
-
- \value NoError No error occurred.
- \value FileWriteError An error occurred when writing to the device.
- \value FileOpenError The file could not be opened.
- \value FilePermissionsError The file could not be accessed.
- \value FileError Another file error occurred.
-*/
-
-/*!
- Returns a status code indicating the first error that was met by QZipWriter,
- or QZipWriter::NoError if no error occurred.
-*/
-QZipWriter::Status QZipWriter::status() const
-{
- return d->status;
-}
-
-/*!
- \enum QZipWriter::CompressionPolicy
-
- \value AlwaysCompress A file that is added is compressed.
- \value NeverCompress A file that is added will be stored without changes.
- \value AutoCompress A file that is added will be compressed only if that will give a smaller file.
-*/
-
-/*!
- Sets the policy for compressing newly added files to the new \a policy.
-
- \note the default policy is AlwaysCompress
-
- \sa compressionPolicy()
- \sa addFile()
-*/
-void QZipWriter::setCompressionPolicy(CompressionPolicy policy)
-{
- d->compressionPolicy = policy;
-}
-
-/*!
- Returns the currently set compression policy.
- \sa setCompressionPolicy()
- \sa addFile()
-*/
-QZipWriter::CompressionPolicy QZipWriter::compressionPolicy() const
-{
- return d->compressionPolicy;
-}
-
-/*!
- Sets the permissions that will be used for newly added files.
-
- \note the default permissions are QFile::ReadOwner | QFile::WriteOwner.
-
- \sa creationPermissions()
- \sa addFile()
-*/
-void QZipWriter::setCreationPermissions(QFile::Permissions permissions)
-{
- d->permissions = permissions;
-}
-
-/*!
- Returns the currently set creation permissions.
-
- \sa setCreationPermissions()
- \sa addFile()
-*/
-QFile::Permissions QZipWriter::creationPermissions() const
-{
- return d->permissions;
-}
-
-/*!
- Add a file to the archive with \a data as the file contents.
- The file will be stored in the archive using the \a fileName which
- includes the full path in the archive.
-
- The new file will get the file permissions based on the current
- creationPermissions and it will be compressed using the zip compression
- based on the current compression policy.
-
- \sa setCreationPermissions()
- \sa setCompressionPolicy()
-*/
-void QZipWriter::addFile(const QString &fileName, const QByteArray &data)
-{
- d->addEntry(QZipWriterPrivate::File, QDir::fromNativeSeparators(fileName), data);
-}
-
-/*!
- Add a file to the archive with \a device as the source of the contents.
- The contents returned from QIODevice::readAll() will be used as the
- filedata.
- The file will be stored in the archive using the \a fileName which
- includes the full path in the archive.
-*/
-void QZipWriter::addFile(const QString &fileName, QIODevice *device)
-{
- Q_ASSERT(device);
- QIODevice::OpenMode mode = device->openMode();
- bool opened = false;
- if ((mode & QIODevice::ReadOnly) == 0) {
- opened = true;
- if (! device->open(QIODevice::ReadOnly)) {
- d->status = FileOpenError;
- return;
- }
- }
- d->addEntry(QZipWriterPrivate::File, QDir::fromNativeSeparators(fileName), device->readAll());
- if (opened)
- device->close();
-}
-
-/*!
- Create a new directory in the archive with the specified \a dirName and
- the \a permissions;
-*/
-void QZipWriter::addDirectory(const QString &dirName)
-{
- QString name(QDir::fromNativeSeparators(dirName));
- // separator is mandatory
- if (!name.endsWith(u'/'))
- name.append(u'/');
- d->addEntry(QZipWriterPrivate::Directory, name, QByteArray());
-}
-
-/*!
- Create a new symbolic link in the archive with the specified \a dirName
- and the \a permissions;
- A symbolic link contains the destination (relative) path and name.
-*/
-void QZipWriter::addSymLink(const QString &fileName, const QString &destination)
-{
- d->addEntry(QZipWriterPrivate::Symlink, QDir::fromNativeSeparators(fileName), QFile::encodeName(destination));
-}
-
-/*!
- Closes the zip file.
-*/
-void QZipWriter::close()
-{
- if (!(d->device->openMode() & QIODevice::WriteOnly)) {
- d->device->close();
- return;
- }
-
- //qDebug("QZip::close writing directory, %d entries", d->fileHeaders.size());
- d->device->seek(d->start_of_directory);
- // write new directory
- for (int i = 0; i < d->fileHeaders.size(); ++i) {
- const FileHeader &header = d->fileHeaders.at(i);
- d->device->write((const char *)&header.h, sizeof(CentralFileHeader));
- d->device->write(header.file_name);
- d->device->write(header.extra_field);
- d->device->write(header.file_comment);
- }
- int dir_size = d->device->pos() - d->start_of_directory;
- // write end of directory
- EndOfDirectory eod;
- memset(&eod, 0, sizeof(EndOfDirectory));
- writeUInt(eod.signature, 0x06054b50);
- //uchar this_disk[2];
- //uchar start_of_directory_disk[2];
- writeUShort(eod.num_dir_entries_this_disk, d->fileHeaders.size());
- writeUShort(eod.num_dir_entries, d->fileHeaders.size());
- writeUInt(eod.directory_size, dir_size);
- writeUInt(eod.dir_start_offset, d->start_of_directory);
- writeUShort(eod.comment_length, d->comment.length());
-
- d->device->write((const char *)&eod, sizeof(EndOfDirectory));
- d->device->write(d->comment);
- d->device->close();
-}
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_TEXTODFWRITER
diff --git a/src/gui/text/qzipreader_p.h b/src/gui/text/qzipreader_p.h
deleted file mode 100644
index 2e8d6bc951..0000000000
--- a/src/gui/text/qzipreader_p.h
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef QZIPREADER_H
-#define QZIPREADER_H
-
-#include <QtGui/private/qtguiglobal_p.h>
-#include <QtCore/qglobal.h>
-
-#ifndef QT_NO_TEXTODFWRITER
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QZipReader class. This header file may change from
-// version to version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/qdatetime.h>
-#include <QtCore/qfile.h>
-#include <QtCore/qstring.h>
-
-QT_BEGIN_NAMESPACE
-
-class QZipReaderPrivate;
-
-class Q_GUI_EXPORT QZipReader
-{
-public:
- explicit QZipReader(const QString &fileName, QIODevice::OpenMode mode = QIODevice::ReadOnly );
-
- explicit QZipReader(QIODevice *device);
- ~QZipReader();
-
- QIODevice* device() const;
-
- bool isReadable() const;
- bool exists() const;
-
- struct FileInfo
- {
- FileInfo() noexcept
- : isDir(false), isFile(false), isSymLink(false), crc(0), size(0)
- {}
-
- bool isValid() const noexcept { return isDir || isFile || isSymLink; }
-
- QString filePath;
- uint isDir : 1;
- uint isFile : 1;
- uint isSymLink : 1;
- QFile::Permissions permissions;
- uint crc;
- qint64 size;
- QDateTime lastModified;
- };
-
- QList<FileInfo> fileInfoList() const;
- int count() const;
-
- FileInfo entryInfoAt(int index) const;
- QByteArray fileData(const QString &fileName) const;
- bool extractAll(const QString &destinationDir) const;
-
- enum Status {
- NoError,
- FileReadError,
- FileOpenError,
- FilePermissionsError,
- FileError
- };
-
- Status status() const;
-
- void close();
-
-private:
- QZipReaderPrivate *d;
- Q_DISABLE_COPY_MOVE(QZipReader)
-};
-Q_DECLARE_TYPEINFO(QZipReader::FileInfo, Q_RELOCATABLE_TYPE);
-Q_DECLARE_TYPEINFO(QZipReader::Status, Q_PRIMITIVE_TYPE);
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_TEXTODFWRITER
-#endif // QZIPREADER_H
diff --git a/src/gui/text/qzipwriter_p.h b/src/gui/text/qzipwriter_p.h
deleted file mode 100644
index 6c1ef5d848..0000000000
--- a/src/gui/text/qzipwriter_p.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QZIPWRITER_H
-#define QZIPWRITER_H
-
-#include <QtGui/private/qtguiglobal_p.h>
-
-#ifndef QT_NO_TEXTODFWRITER
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QZipWriter class. This header file may change from
-// version to version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/qstring.h>
-#include <QtCore/qfile.h>
-
-QT_BEGIN_NAMESPACE
-
-class QZipWriterPrivate;
-
-
-class Q_GUI_EXPORT QZipWriter
-{
-public:
- explicit QZipWriter(const QString &fileName, QIODevice::OpenMode mode = (QIODevice::WriteOnly | QIODevice::Truncate) );
-
- explicit QZipWriter(QIODevice *device);
- ~QZipWriter();
-
- QIODevice* device() const;
-
- bool isWritable() const;
- bool exists() const;
-
- enum Status {
- NoError,
- FileWriteError,
- FileOpenError,
- FilePermissionsError,
- FileError
- };
-
- Status status() const;
-
- enum CompressionPolicy {
- AlwaysCompress,
- NeverCompress,
- AutoCompress
- };
-
- void setCompressionPolicy(CompressionPolicy policy);
- CompressionPolicy compressionPolicy() const;
-
- void setCreationPermissions(QFile::Permissions permissions);
- QFile::Permissions creationPermissions() const;
-
- void addFile(const QString &fileName, const QByteArray &data);
-
- void addFile(const QString &fileName, QIODevice *device);
-
- void addDirectory(const QString &dirName);
-
- void addSymLink(const QString &fileName, const QString &destination);
-
- void close();
-private:
- QZipWriterPrivate *d;
- Q_DISABLE_COPY_MOVE(QZipWriter)
-};
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_TEXTODFWRITER
-#endif // QZIPWRITER_H
diff --git a/src/gui/text/unix/qfontconfigdatabase.cpp b/src/gui/text/unix/qfontconfigdatabase.cpp
index 9b60cf2963..d607d38235 100644
--- a/src/gui/text/unix/qfontconfigdatabase.cpp
+++ b/src/gui/text/unix/qfontconfigdatabase.cpp
@@ -16,7 +16,6 @@
#include <qpa/qplatformservices.h>
#include <QtGui/private/qguiapplication_p.h>
-#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtGui/qguiapplication.h>
@@ -29,6 +28,8 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcFontDb)
+
static inline int mapToQtWeightForRange(int fcweight, int fcLower, int fcUpper, int qtLower, int qtUpper)
{
return qtLower + ((fcweight - fcLower) * (qtUpper - qtLower)) / (fcUpper - fcLower);
@@ -246,6 +247,8 @@ static const char specialLanguages[][6] = {
"", // Tangsa
"", // Toto
"", // Vithkuqi
+ "", // Kawi
+ "", // NagMundari
};
static_assert(sizeof specialLanguages / sizeof *specialLanguages == QChar::ScriptCount);
@@ -364,7 +367,10 @@ static inline bool requiresOpenType(int writingSystem)
|| writingSystem == QFontDatabase::Khmer || writingSystem == QFontDatabase::Nko);
}
-static void populateFromPattern(FcPattern *pattern, QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr)
+static void populateFromPattern(FcPattern *pattern,
+ QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr,
+ FT_Face face = nullptr,
+ QFontconfigDatabase *db = nullptr)
{
QString familyName;
QString familyNameLang;
@@ -487,6 +493,20 @@ static void populateFromPattern(FcPattern *pattern, QFontDatabasePrivate::Applic
}
QPlatformFontDatabase::registerFont(familyName,styleName,QLatin1StringView((const char *)foundry_value),weight,style,stretch,antialias,scalable,pixel_size,fixedPitch,writingSystems,fontFile);
+ if (applicationFont != nullptr && face != nullptr && db != nullptr) {
+ db->addNamedInstancesForFace(face,
+ indexValue,
+ familyName,
+ styleName,
+ weight,
+ stretch,
+ style,
+ fixedPitch,
+ writingSystems,
+ QByteArray((const char*)file_value),
+ applicationFont->data);
+ }
+
// qDebug() << familyName << (const char *)foundry_value << weight << style << &writingSystems << scalable << true << pixel_size;
for (int k = 1; FcPatternGetString(pattern, FC_FAMILY, k, &value) == FcResultMatch; ++k) {
@@ -526,6 +546,11 @@ static void populateFromPattern(FcPattern *pattern, QFontDatabasePrivate::Applic
}
+static bool isDprScaling()
+{
+ return !qFuzzyCompare(qApp->devicePixelRatio(), 1.0);
+}
+
QFontconfigDatabase::~QFontconfigDatabase()
{
FcConfigDestroy(FcConfigGetCurrent());
@@ -554,6 +579,12 @@ void QFontconfigDatabase::populateFontDatabase()
FcObjectSetAdd(os, *p);
++p;
}
+
+#ifdef FC_VARIABLE
+ /* Support the named instance of Variable Fonts. */
+ FcPatternAddBool(pattern, FC_VARIABLE, FcFalse);
+#endif
+
fonts = FcFontList(nullptr, pattern, os);
FcObjectSetDestroy(os);
FcPatternDestroy(pattern);
@@ -611,7 +642,7 @@ QFontEngineMulti *QFontconfigDatabase::fontEngineMulti(QFontEngine *fontEngine,
}
namespace {
-QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintingPreference, FcPattern *match, bool useXftConf)
+QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintingPreference, FcPattern *match, bool preferXftConf)
{
switch (hintingPreference) {
case QFont::PreferNoHinting:
@@ -624,9 +655,16 @@ QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintin
break;
}
- if (QHighDpiScaling::isActive())
+ if (isDprScaling())
return QFontEngine::HintNone;
+ void *hintStyleResource =
+ QGuiApplication::platformNativeInterface()->nativeResourceForScreen("hintstyle",
+ QGuiApplication::primaryScreen());
+ int xftHintStyle = int(reinterpret_cast<qintptr>(hintStyleResource));
+ if (preferXftConf && xftHintStyle > 0)
+ return QFontEngine::HintStyle(xftHintStyle - 1);
+
int hint_style = 0;
if (FcPatternGetInteger (match, FC_HINT_STYLE, 0, &hint_style) == FcResultMatch) {
switch (hint_style) {
@@ -643,21 +681,21 @@ QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintin
break;
}
}
-
- if (useXftConf) {
- void *hintStyleResource =
- QGuiApplication::platformNativeInterface()->nativeResourceForScreen("hintstyle",
- QGuiApplication::primaryScreen());
- int hintStyle = int(reinterpret_cast<qintptr>(hintStyleResource));
- if (hintStyle > 0)
- return QFontEngine::HintStyle(hintStyle - 1);
- }
+ if (xftHintStyle > 0)
+ return QFontEngine::HintStyle(xftHintStyle - 1);
return QFontEngine::HintFull;
}
-QFontEngine::SubpixelAntialiasingType subpixelTypeFromMatch(FcPattern *match, bool useXftConf)
+QFontEngine::SubpixelAntialiasingType subpixelTypeFromMatch(FcPattern *match, bool preferXftConf)
{
+ void *subpixelTypeResource =
+ QGuiApplication::platformNativeInterface()->nativeResourceForScreen("subpixeltype",
+ QGuiApplication::primaryScreen());
+ int xftSubpixelType = int(reinterpret_cast<qintptr>(subpixelTypeResource));
+ if (preferXftConf && xftSubpixelType > 0)
+ return QFontEngine::SubpixelAntialiasingType(xftSubpixelType - 1);
+
int subpixel = FC_RGBA_UNKNOWN;
if (FcPatternGetInteger(match, FC_RGBA, 0, &subpixel) == FcResultMatch) {
switch (subpixel) {
@@ -678,14 +716,8 @@ QFontEngine::SubpixelAntialiasingType subpixelTypeFromMatch(FcPattern *match, bo
}
}
- if (useXftConf) {
- void *subpixelTypeResource =
- QGuiApplication::platformNativeInterface()->nativeResourceForScreen("subpixeltype",
- QGuiApplication::primaryScreen());
- int subpixelType = int(reinterpret_cast<qintptr>(subpixelTypeResource));
- if (subpixelType > 0)
- return QFontEngine::SubpixelAntialiasingType(subpixelType - 1);
- }
+ if (xftSubpixelType > 0)
+ return QFontEngine::SubpixelAntialiasingType(xftSubpixelType - 1);
return QFontEngine::Subpixel_None;
}
@@ -700,6 +732,8 @@ QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, void *usrPtr)
QFontEngine::FaceId fid;
fid.filename = QFile::encodeName(fontfile->fileName);
fid.index = fontfile->indexValue;
+ fid.instanceIndex = fontfile->instanceIndex;
+ fid.variableAxes = f.variableAxisValues;
// FIXME: Unify with logic in QFontEngineFT::create()
QFontEngineFT *engine = new QFontEngineFT(f);
@@ -801,26 +835,28 @@ QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family, QFont
return fallbackFamilies;
}
-static FcPattern *queryFont(const FcChar8 *file, const QByteArray &data, int id, FcBlanks *blanks, int *count)
+static FcPattern *queryFont(const FcChar8 *file, const QByteArray &data, int id, FcBlanks *blanks, int *count, FT_Face *face)
{
#if FC_VERSION < 20402
Q_UNUSED(data);
+ *face = nullptr;
return FcFreeTypeQuery(file, id, blanks, count);
#else
- if (data.isEmpty())
+ if (data.isEmpty()) {
+ *face = nullptr;
return FcFreeTypeQuery(file, id, blanks, count);
+ }
FT_Library lib = qt_getFreetype();
FcPattern *pattern = nullptr;
- FT_Face face;
- if (!FT_New_Memory_Face(lib, (const FT_Byte *)data.constData(), data.size(), id, &face)) {
- *count = face->num_faces;
-
- pattern = FcFreeTypeQueryFace(face, file, id, blanks);
+ if (!FT_New_Memory_Face(lib, (const FT_Byte *)data.constData(), data.size(), id, face)) {
+ *count = (*face)->num_faces;
- FT_Done_Face(face);
+ pattern = FcFreeTypeQueryFace(*face, file, id, blanks);
+ } else {
+ *face = nullptr;
}
return pattern;
@@ -848,8 +884,9 @@ QStringList QFontconfigDatabase::addApplicationFont(const QByteArray &fontData,
FcPattern *pattern;
do {
+ FT_Face face;
pattern = queryFont((const FcChar8 *)QFile::encodeName(fileName).constData(),
- fontData, id, blanks, &count);
+ fontData, id, blanks, &count, &face);
if (!pattern)
return families;
@@ -858,7 +895,10 @@ QStringList QFontconfigDatabase::addApplicationFont(const QByteArray &fontData,
QString family = QString::fromUtf8(reinterpret_cast<const char *>(fam));
families << family;
}
- populateFromPattern(pattern, applicationFont);
+ populateFromPattern(pattern, applicationFont, face, this);
+
+ if (face)
+ FT_Done_Face(face);
FcFontSetAdd(set, pattern);
@@ -923,28 +963,20 @@ QFont QFontconfigDatabase::defaultFont() const
void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef &fontDef) const
{
bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
- bool forcedAntialiasSetting = !antialias || QHighDpiScaling::isActive();
+ bool forcedAntialiasSetting = !antialias || isDprScaling();
const QPlatformServices *services = QGuiApplicationPrivate::platformIntegration()->services();
- bool useXftConf = false;
+ bool preferXftConf = false;
if (services) {
const QList<QByteArray> desktopEnv = services->desktopEnvironment().split(':');
- useXftConf = desktopEnv.contains("GNOME") || desktopEnv.contains("UNITY") || desktopEnv.contains("XFCE");
- }
-
- if (useXftConf && !forcedAntialiasSetting) {
- void *antialiasResource =
- QGuiApplication::platformNativeInterface()->nativeResourceForScreen("antialiasingEnabled",
- QGuiApplication::primaryScreen());
- int antialiasingEnabled = int(reinterpret_cast<qintptr>(antialiasResource));
- if (antialiasingEnabled > 0)
- antialias = antialiasingEnabled - 1;
+ preferXftConf = !(desktopEnv.contains("KDE") || desktopEnv.contains("LXQT") || desktopEnv.contains("UKUI"));
}
QFontEngine::GlyphFormat format;
// try and get the pattern
FcPattern *pattern = FcPatternCreate();
+ FcPattern *match = nullptr;
FcValue value;
value.type = FcTypeString;
@@ -963,7 +995,7 @@ void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef
FcPatternAdd(pattern,FC_INDEX,value,true);
}
- if (fontDef.pixelSize > 0.1)
+ if (!qFuzzyIsNull(fontDef.pixelSize))
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fontDef.pixelSize);
FcResult result;
@@ -971,9 +1003,68 @@ void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef
FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
- FcPattern *match = FcFontMatch(nullptr, pattern, &result);
+#ifdef FC_VARIABLE
+ if (!fid.filename.isEmpty()) {
+ // FC_INDEX is ignored during processing in FcFontMatch.
+ // So iterate FcPatterns directly and find it out.
+ FcFontSet *fcsets[2], *fcfs;
+
+ fcsets[0] = FcConfigGetFonts(nullptr, FcSetSystem);
+ fcsets[1] = FcConfigGetFonts(nullptr, FcSetApplication);
+ for (int nset = 0; nset < 2; nset++) {
+ fcfs = fcsets[nset];
+ if (fcfs == nullptr)
+ continue;
+ for (int fnum = 0; fnum < fcfs->nfont; fnum++) {
+ FcPattern *fcpat = fcfs->fonts[fnum];
+ FcChar8 *fcfile;
+ FcBool variable;
+ double fcpixelsize;
+ int fcindex;
+
+ // Skip the variable font itself, only to use the named instances and normal fonts here
+ if (FcPatternGetBool(fcpat, FC_VARIABLE, 0, &variable) == FcResultMatch &&
+ variable == FcTrue)
+ continue;
+
+ if (!qFuzzyIsNull(fontDef.pixelSize)) {
+ if (FcPatternGetDouble(fcpat, FC_PIXEL_SIZE, 0, &fcpixelsize) == FcResultMatch &&
+ fontDef.pixelSize != fcpixelsize)
+ continue;
+ }
+
+ if (FcPatternGetString(fcpat, FC_FILE, 0, &fcfile) == FcResultMatch &&
+ FcPatternGetInteger(fcpat, FC_INDEX, 0, &fcindex) == FcResultMatch) {
+ QByteArray f = QByteArray::fromRawData((const char *)fcfile,
+ qstrlen((const char *)fcfile));
+ if (f == fid.filename && fcindex == fid.index) {
+ // We found it.
+ match = FcFontRenderPrepare(nullptr, pattern, fcpat);
+ goto bail;
+ }
+ }
+ }
+ }
+ }
+bail:
+#endif
+
+ if (!match)
+ match = FcFontMatch(nullptr, pattern, &result);
+
+ int xftAntialias = 0;
+ if (!forcedAntialiasSetting) {
+ void *antialiasResource =
+ QGuiApplication::platformNativeInterface()->nativeResourceForScreen("antialiasingEnabled",
+ QGuiApplication::primaryScreen());
+ xftAntialias = int(reinterpret_cast<qintptr>(antialiasResource));
+ if ((preferXftConf || !match) && xftAntialias > 0) {
+ antialias = xftAntialias - 1;
+ forcedAntialiasSetting = true;
+ }
+ }
if (match) {
- engine->setDefaultHintStyle(defaultHintStyleFromMatch((QFont::HintingPreference)fontDef.hintingPreference, match, useXftConf));
+ engine->setDefaultHintStyle(defaultHintStyleFromMatch((QFont::HintingPreference)fontDef.hintingPreference, match, preferXftConf));
FcBool fc_autohint;
if (FcPatternGetBool(match, FC_AUTOHINT,0, &fc_autohint) == FcResultMatch)
@@ -994,18 +1085,37 @@ void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef
if (antialias) {
QFontEngine::SubpixelAntialiasingType subpixelType = QFontEngine::Subpixel_None;
if (!(fontDef.styleStrategy & QFont::NoSubpixelAntialias))
- subpixelType = subpixelTypeFromMatch(match, useXftConf);
+ subpixelType = subpixelTypeFromMatch(match, preferXftConf);
engine->subpixelType = subpixelType;
-
- format = (subpixelType == QFontEngine::Subpixel_None)
- ? QFontEngine::Format_A8
- : QFontEngine::Format_A32;
- } else
- format = QFontEngine::Format_Mono;
+ }
FcPatternDestroy(match);
- } else
- format = antialias ? QFontEngine::Format_A8 : QFontEngine::Format_Mono;
+ } else {
+ void *hintStyleResource =
+ QGuiApplication::platformNativeInterface()->nativeResourceForScreen("hintstyle",
+ QGuiApplication::primaryScreen());
+ int xftHintStyle = int(reinterpret_cast<qintptr>(hintStyleResource));
+ if (xftHintStyle > 0)
+ engine->setDefaultHintStyle(QFontEngine::HintStyle(xftHintStyle - 1));
+ if (antialias) {
+ engine->subpixelType = QFontEngine::Subpixel_None;
+ if (!(fontDef.styleStrategy & QFont::NoSubpixelAntialias)) {
+ void *subpixelTypeResource =
+ QGuiApplication::platformNativeInterface()->nativeResourceForScreen("subpixeltype",
+ QGuiApplication::primaryScreen());
+ int xftSubpixelType = int(reinterpret_cast<qintptr>(subpixelTypeResource));
+ if (xftSubpixelType > 1)
+ engine->subpixelType = QFontEngine::SubpixelAntialiasingType(xftSubpixelType - 1);
+ }
+ }
+ }
+ if (antialias) {
+ format = (engine->subpixelType == QFontEngine::Subpixel_None)
+ ? QFontEngine::Format_A8
+ : QFontEngine::Format_A32;
+ } else {
+ format = QFontEngine::Format_Mono;
+ }
FcPatternDestroy(pattern);
@@ -1014,4 +1124,13 @@ void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef
engine->glyphFormat = format;
}
+bool QFontconfigDatabase::supportsVariableApplicationFonts() const
+{
+#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
+ return true;
+#else
+ return false;
+#endif
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/text/unix/qfontconfigdatabase_p.h b/src/gui/text/unix/qfontconfigdatabase_p.h
index cf15306e40..dd7a70a375 100644
--- a/src/gui/text/unix/qfontconfigdatabase_p.h
+++ b/src/gui/text/unix/qfontconfigdatabase_p.h
@@ -28,6 +28,7 @@ public:
~QFontconfigDatabase() override;
void populateFontDatabase() override;
void invalidate() override;
+ bool supportsVariableApplicationFonts() const override;
QFontEngineMulti *fontEngineMulti(QFontEngine *fontEngine, QChar::Script script) override;
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override;
diff --git a/src/gui/text/unix/qfontenginemultifontconfig.cpp b/src/gui/text/unix/qfontenginemultifontconfig.cpp
index 4e0a47a112..6419a764f7 100644
--- a/src/gui/text/unix/qfontenginemultifontconfig.cpp
+++ b/src/gui/text/unix/qfontenginemultifontconfig.cpp
@@ -14,7 +14,7 @@ QFontEngineMultiFontConfig::QFontEngineMultiFontConfig(QFontEngine *fe, int scri
QFontEngineMultiFontConfig::~QFontEngineMultiFontConfig()
{
- for (FcPattern *pattern : qAsConst(cachedMatchPatterns)) {
+ for (FcPattern *pattern : std::as_const(cachedMatchPatterns)) {
if (pattern)
FcPatternDestroy(pattern);
}
diff --git a/src/gui/text/unix/qgenericunixfontdatabase_p.h b/src/gui/text/unix/qgenericunixfontdatabase_p.h
index 10d5c988fb..96124d025b 100644
--- a/src/gui/text/unix/qgenericunixfontdatabase_p.h
+++ b/src/gui/text/unix/qgenericunixfontdatabase_p.h
@@ -20,9 +20,12 @@
#if QT_CONFIG(fontconfig)
#include <QtGui/private/qfontconfigdatabase_p.h>
using QGenericUnixFontDatabase = QFontconfigDatabase;
-#else
+#elif QT_CONFIG(freetype)
#include <QtGui/private/qfreetypefontdatabase_p.h>
using QGenericUnixFontDatabase = QFreeTypeFontDatabase;
-#endif //Q_FONTCONFIGDATABASE
+#else
+#include <qpa/qplatformfontdatabase.h>
+using QGenericUnixFontDatabase = QPlatformFontDatabase;
+#endif
#endif // QGENERICUNIXFONTDATABASE_H
diff --git a/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp b/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp
index 6b4933cca7..2e15fbb1ac 100644
--- a/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp
+++ b/src/gui/text/windows/qwindowsdirectwritefontdatabase.cpp
@@ -18,6 +18,32 @@ QT_BEGIN_NAMESPACE
// Defined in gui/text/qfontdatabase.cpp
Q_GUI_EXPORT QFontDatabase::WritingSystem qt_writing_system_for_script(int script);
+template<typename T>
+struct DirectWriteScope {
+ DirectWriteScope(T *res = nullptr) : m_res(res) {}
+ ~DirectWriteScope() {
+ if (m_res != nullptr)
+ m_res->Release();
+ }
+
+ T **operator&()
+ {
+ return &m_res;
+ }
+
+ T *operator->()
+ {
+ return m_res;
+ }
+
+ T *operator*() {
+ return m_res;
+ }
+
+private:
+ T *m_res;
+};
+
QWindowsDirectWriteFontDatabase::QWindowsDirectWriteFontDatabase()
{
qCDebug(lcQpaFonts) << "Creating DirectWrite database";
@@ -80,6 +106,12 @@ static QFont::Style fromDirectWriteStyle(DWRITE_FONT_STYLE style)
void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
{
auto it = m_populatedFonts.find(familyName);
+ if (it == m_populatedFonts.end() && m_populatedBitmapFonts.contains(familyName)) {
+ qCDebug(lcQpaFonts) << "Populating bitmap font" << familyName;
+ QWindowsFontDatabase::populateFamily(familyName);
+ return;
+ }
+
IDWriteFontFamily *fontFamily = it != m_populatedFonts.end() ? it.value() : nullptr;
if (fontFamily == nullptr) {
qCWarning(lcQpaFonts) << "Cannot find" << familyName << "in list of fonts";
@@ -98,7 +130,7 @@ void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
const bool antialias = false;
const int size = SMOOTH_SCALABLE;
- IDWriteFontList *matchingFonts;
+ DirectWriteScope<IDWriteFontList> matchingFonts;
if (SUCCEEDED(fontFamily->GetMatchingFonts(DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STRETCH_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
@@ -106,7 +138,7 @@ void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
for (uint j = 0; j < matchingFonts->GetFontCount(); ++j) {
IDWriteFont *font;
if (SUCCEEDED(matchingFonts->GetFont(j, &font))) {
- IDWriteFont1 *font1 = nullptr;
+ DirectWriteScope<IDWriteFont1> font1;
if (!SUCCEEDED(font->QueryInterface(__uuidof(IDWriteFont1),
reinterpret_cast<void **>(&font1)))) {
qCWarning(lcQpaFonts) << "COM object does not support IDWriteFont1";
@@ -116,27 +148,23 @@ void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
QString defaultLocaleFamilyName;
QString englishLocaleFamilyName;
- IDWriteFontFamily *fontFamily2;
+ DirectWriteScope<IDWriteFontFamily> fontFamily2;
if (SUCCEEDED(font1->GetFontFamily(&fontFamily2))) {
- IDWriteLocalizedStrings *names;
+ DirectWriteScope<IDWriteLocalizedStrings> names;
if (SUCCEEDED(fontFamily2->GetFamilyNames(&names))) {
- defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
- englishLocaleFamilyName = localeString(names, englishLocale);
-
- names->Release();
+ defaultLocaleFamilyName = hasDefaultLocale ? localeString(*names, defaultLocale) : QString();
+ englishLocaleFamilyName = localeString(*names, englishLocale);
}
-
- fontFamily2->Release();
}
if (defaultLocaleFamilyName.isEmpty() && englishLocaleFamilyName.isEmpty())
englishLocaleFamilyName = familyName;
{
- IDWriteLocalizedStrings *names;
+ DirectWriteScope<IDWriteLocalizedStrings> names;
if (SUCCEEDED(font1->GetFaceNames(&names))) {
- QString defaultLocaleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
- QString englishLocaleStyleName = localeString(names, englishLocale);
+ QString defaultLocaleStyleName = hasDefaultLocale ? localeString(*names, defaultLocale) : QString();
+ QString englishLocaleStyleName = localeString(*names, englishLocale);
QFont::Stretch stretch = fromDirectWriteStretch(font1->GetStretch());
QFont::Style style = fromDirectWriteStyle(font1->GetStyle());
@@ -145,77 +173,233 @@ void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
qCDebug(lcQpaFonts) << "Family" << familyName << "has english variant" << englishLocaleStyleName << ", in default locale:" << defaultLocaleStyleName << stretch << style << weight << fixed;
- IDWriteFontFace *face = nullptr;
+ DirectWriteScope<IDWriteFontFace> face;
if (SUCCEEDED(font->CreateFontFace(&face))) {
- QSupportedWritingSystems writingSystems;
-
- const void *tableData = nullptr;
- UINT32 tableSize;
- void *tableContext = nullptr;
- BOOL exists;
- HRESULT hr = face->TryGetFontTable(qbswap<quint32>(MAKE_TAG('O','S','/','2')),
- &tableData,
- &tableSize,
- &tableContext,
- &exists);
- if (SUCCEEDED(hr) && exists) {
- writingSystems = QPlatformFontDatabase::writingSystemsFromOS2Table(reinterpret_cast<const char *>(tableData), tableSize);
- } else { // Fall back to checking first character of each Unicode range in font (may include too many writing systems)
- quint32 rangeCount;
- hr = font1->GetUnicodeRanges(0, nullptr, &rangeCount);
-
- if (rangeCount > 0) {
- QVarLengthArray<DWRITE_UNICODE_RANGE, QChar::ScriptCount> ranges(rangeCount);
-
- hr = font1->GetUnicodeRanges(rangeCount, ranges.data(), &rangeCount);
- if (SUCCEEDED(hr)) {
- for (uint i = 0; i < rangeCount; ++i) {
- QChar::Script script = QChar::script(ranges.at(i).first);
-
- QFontDatabase::WritingSystem writingSystem = qt_writing_system_for_script(script);
-
- if (writingSystem > QFontDatabase::Any && writingSystem < QFontDatabase::WritingSystemsCount)
- writingSystems.setSupported(writingSystem);
- }
- } else {
- const QString errorString = qt_error_string(int(hr));
- qCWarning(lcQpaFonts) << "Failed to get unicode ranges for font" << englishLocaleFamilyName << englishLocaleStyleName << ":" << errorString;
- }
- }
- }
+ QSupportedWritingSystems writingSystems = supportedWritingSystems(*face);
if (!englishLocaleStyleName.isEmpty() || defaultLocaleStyleName.isEmpty()) {
qCDebug(lcQpaFonts) << "Font" << englishLocaleFamilyName << englishLocaleStyleName << "supports writing systems:" << writingSystems;
- QPlatformFontDatabase::registerFont(englishLocaleFamilyName, englishLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
- face->AddRef();
+ QPlatformFontDatabase::registerFont(englishLocaleFamilyName,
+ englishLocaleStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(*face, englishLocaleFamilyName));
}
if (!defaultLocaleFamilyName.isEmpty() && defaultLocaleFamilyName != englishLocaleFamilyName) {
- QPlatformFontDatabase::registerFont(defaultLocaleFamilyName, defaultLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
- face->AddRef();
+ QPlatformFontDatabase::registerFont(defaultLocaleFamilyName,
+ defaultLocaleStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(*face, defaultLocaleFamilyName));
}
-
- face->Release();
}
-
- names->Release();
}
}
+ }
+ }
+ }
+}
- font1->Release();
- font->Release();
+QSupportedWritingSystems QWindowsDirectWriteFontDatabase::supportedWritingSystems(IDWriteFontFace *face) const
+{
+ QSupportedWritingSystems writingSystems;
+ writingSystems.setSupported(QFontDatabase::Any);
+
+ DirectWriteScope<IDWriteFontFace1> face1;
+ if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace1),
+ reinterpret_cast<void **>(&face1)))) {
+ const void *tableData = nullptr;
+ UINT32 tableSize;
+ void *tableContext = nullptr;
+ BOOL exists;
+ HRESULT hr = face->TryGetFontTable(qFromBigEndian(QFont::Tag("OS/2").value()),
+ &tableData,
+ &tableSize,
+ &tableContext,
+ &exists);
+ if (SUCCEEDED(hr) && exists) {
+ writingSystems = QPlatformFontDatabase::writingSystemsFromOS2Table(reinterpret_cast<const char *>(tableData), tableSize);
+ } else { // Fall back to checking first character of each Unicode range in font (may include too many writing systems)
+ quint32 rangeCount;
+ hr = face1->GetUnicodeRanges(0, nullptr, &rangeCount);
+
+ if (rangeCount > 0) {
+ QVarLengthArray<DWRITE_UNICODE_RANGE, QChar::ScriptCount> ranges(rangeCount);
+
+ hr = face1->GetUnicodeRanges(rangeCount, ranges.data(), &rangeCount);
+ if (SUCCEEDED(hr)) {
+ for (uint i = 0; i < rangeCount; ++i) {
+ QChar::Script script = QChar::script(ranges.at(i).first);
+
+ QFontDatabase::WritingSystem writingSystem = qt_writing_system_for_script(script);
+
+ if (writingSystem > QFontDatabase::Any && writingSystem < QFontDatabase::WritingSystemsCount)
+ writingSystems.setSupported(writingSystem);
+ }
+ } else {
+ const QString errorString = qt_error_string(int(hr));
+ qCWarning(lcQpaFonts) << "Failed to get unicode ranges for font:" << errorString;
+ }
}
}
+ }
+
+ return writingSystems;
+}
+
+bool QWindowsDirectWriteFontDatabase::populateFamilyAliases(const QString &missingFamily)
+{
+ // If the font has not been populated, it is possible this is a legacy font family supported
+ // by GDI. We make an attempt at loading it via GDI and then add this face directly to the
+ // database.
+ if (!missingFamily.isEmpty()
+ && missingFamily.size() < LF_FACESIZE
+ && !m_populatedFonts.contains(missingFamily)
+ && !m_populatedBitmapFonts.contains(missingFamily)) {
+ qCDebug(lcQpaFonts) << "Loading unpopulated" << missingFamily << ". Trying GDI.";
+
+ LOGFONT lf;
+ memset(&lf, 0, sizeof(LOGFONT));
+ memcpy(lf.lfFaceName, missingFamily.utf16(), missingFamily.size() * sizeof(wchar_t));
+
+ HFONT hfont = CreateFontIndirect(&lf);
+ if (hfont) {
+ HDC dummy = GetDC(0);
+ HGDIOBJ oldFont = SelectObject(dummy, hfont);
+
+ DirectWriteScope<IDWriteFontFace> directWriteFontFace;
+ if (SUCCEEDED(data()->directWriteGdiInterop->CreateFontFaceFromHdc(dummy, &directWriteFontFace))) {
+ DirectWriteScope<IDWriteFontCollection> fontCollection;
+ if (SUCCEEDED(data()->directWriteFactory->GetSystemFontCollection(&fontCollection))) {
+ DirectWriteScope<IDWriteFont> font;
+ if (SUCCEEDED(fontCollection->GetFontFromFontFace(*directWriteFontFace, &font))) {
+
+ DirectWriteScope<IDWriteFont1> font1;
+ if (SUCCEEDED(font->QueryInterface(__uuidof(IDWriteFont1),
+ reinterpret_cast<void **>(&font1)))) {
+ DirectWriteScope<IDWriteLocalizedStrings> names;
+ if (SUCCEEDED(font1->GetFaceNames(&names))) {
+ wchar_t englishLocale[] = L"en-us";
+ QString englishLocaleStyleName = localeString(*names, englishLocale);
+
+ QFont::Stretch stretch = fromDirectWriteStretch(font1->GetStretch());
+ QFont::Style style = fromDirectWriteStyle(font1->GetStyle());
+ QFont::Weight weight = fromDirectWriteWeight(font1->GetWeight());
+ bool fixed = font1->IsMonospacedFont();
+
+ QSupportedWritingSystems writingSystems = supportedWritingSystems(*directWriteFontFace);
+
+ qCDebug(lcQpaFonts) << "Registering legacy font family" << missingFamily;
+ QPlatformFontDatabase::registerFont(missingFamily,
+ englishLocaleStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ false,
+ true,
+ 0xffff,
+ fixed,
+ writingSystems,
+ new FontHandle(*directWriteFontFace, missingFamily));
+
+ SelectObject(dummy, oldFont);
+ DeleteObject(hfont);
+
+ return true;
+ }
+ }
+ }
+ }
+ }
- matchingFonts->Release();
+ SelectObject(dummy, oldFont);
+ DeleteObject(hfont);
+ }
}
+
+ // Skip over implementation in QWindowsFontDatabase
+ return QWindowsFontDatabaseBase::populateFamilyAliases(missingFamily);
+}
+
+QFontEngine *QWindowsDirectWriteFontDatabase::fontEngine(const QByteArray &fontData,
+ qreal pixelSize,
+ QFont::HintingPreference hintingPreference)
+{
+ // Skip over implementation in QWindowsFontDatabase
+ return QWindowsFontDatabaseBase::fontEngine(fontData, pixelSize, hintingPreference);
}
QFontEngine *QWindowsDirectWriteFontDatabase::fontEngine(const QFontDef &fontDef, void *handle)
{
- IDWriteFontFace *face = reinterpret_cast<IDWriteFontFace *>(handle);
- Q_ASSERT(face != nullptr);
+ const FontHandle *fontHandle = static_cast<const FontHandle *>(handle);
+ IDWriteFontFace *face = fontHandle->fontFace;
+ if (face == nullptr) {
+ qCDebug(lcQpaFonts) << "Falling back to GDI";
+ return QWindowsFontDatabase::fontEngine(fontDef, handle);
+ }
+
+ DWRITE_FONT_SIMULATIONS simulations = DWRITE_FONT_SIMULATIONS_NONE;
+ if (fontDef.weight >= QFont::DemiBold || fontDef.style != QFont::StyleNormal) {
+ DirectWriteScope<IDWriteFontFace3> face3;
+ if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3),
+ reinterpret_cast<void **>(&face3)))) {
+ if (fontDef.weight >= QFont::DemiBold && face3->GetWeight() < DWRITE_FONT_WEIGHT_DEMI_BOLD)
+ simulations |= DWRITE_FONT_SIMULATIONS_BOLD;
+
+ if (fontDef.style != QFont::StyleNormal && face3->GetStyle() == DWRITE_FONT_STYLE_NORMAL)
+ simulations |= DWRITE_FONT_SIMULATIONS_OBLIQUE;
+ }
+ }
+
+ DirectWriteScope<IDWriteFontFace5> newFace;
+ if (!fontDef.variableAxisValues.isEmpty() || simulations != DWRITE_FONT_SIMULATIONS_NONE) {
+ DirectWriteScope<IDWriteFontFace5> face5;
+ if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace5),
+ reinterpret_cast<void **>(&face5)))) {
+ DirectWriteScope<IDWriteFontResource> font;
+ if (SUCCEEDED(face5->GetFontResource(&font))) {
+ UINT32 fontAxisCount = font->GetFontAxisCount();
+ QVarLengthArray<DWRITE_FONT_AXIS_VALUE, 8> fontAxisValues(fontAxisCount);
+
+ if (!fontDef.variableAxisValues.isEmpty()) {
+ if (SUCCEEDED(face5->GetFontAxisValues(fontAxisValues.data(), fontAxisCount))) {
+ for (UINT32 i = 0; i < fontAxisCount; ++i) {
+ if (auto maybeTag = QFont::Tag::fromValue(qToBigEndian<UINT32>(fontAxisValues[i].axisTag))) {
+ if (fontDef.variableAxisValues.contains(*maybeTag))
+ fontAxisValues[i].value = fontDef.variableAxisValues.value(*maybeTag);
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(font->CreateFontFace(simulations,
+ !fontDef.variableAxisValues.isEmpty() ? fontAxisValues.data() : nullptr,
+ !fontDef.variableAxisValues.isEmpty() ? fontAxisCount : 0,
+ &newFace))) {
+ face = *newFace;
+ } else {
+ qCWarning(lcQpaFonts) << "DirectWrite: Can't create font face for variable axis values";
+ }
+ }
+ }
+ }
QWindowsFontEngineDirectWrite *fontEngine = new QWindowsFontEngineDirectWrite(face, fontDef.pixelSize, data());
fontEngine->initFontInfo(fontDef, defaultVerticalDPI());
@@ -249,111 +433,255 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
loadedData = file.readAll();
}
- IDWriteFontFace *face = createDirectWriteFace(loadedData);
- if (face == nullptr) {
+ QList<IDWriteFontFace *> faces = createDirectWriteFaces(loadedData);
+ if (faces.isEmpty()) {
qCWarning(lcQpaFonts) << "Failed to create DirectWrite face from font data. Font may be unsupported.";
return QStringList();
}
- wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
- bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
- wchar_t englishLocale[] = L"en-us";
+ QSet<QString> ret;
+ for (int i = 0; i < faces.size(); ++i) {
+ IDWriteFontFace *face = faces.at(i);
+ wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
+ bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
+ wchar_t englishLocale[] = L"en-us";
+
+ static const int SMOOTH_SCALABLE = 0xffff;
+ const bool scalable = true;
+ const bool antialias = false;
+ const int size = SMOOTH_SCALABLE;
+
+ QSupportedWritingSystems writingSystems = supportedWritingSystems(face);
+ DirectWriteScope<IDWriteFontFace3> face3;
+ if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3),
+ reinterpret_cast<void **>(&face3)))) {
+ QString defaultLocaleFamilyName;
+ QString englishLocaleFamilyName;
+
+ IDWriteLocalizedStrings *names = nullptr;
+ if (SUCCEEDED(face3->GetFamilyNames(&names))) {
+ defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
+ englishLocaleFamilyName = localeString(names, englishLocale);
+
+ names->Release();
+ }
- static const int SMOOTH_SCALABLE = 0xffff;
- const QString foundryName; // No such concept.
- const bool scalable = true;
- const bool antialias = false;
- const int size = SMOOTH_SCALABLE;
+ QString defaultLocaleStyleName;
+ QString englishLocaleStyleName;
+ if (SUCCEEDED(face3->GetFaceNames(&names))) {
+ defaultLocaleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
+ englishLocaleStyleName = localeString(names, englishLocale);
- QSupportedWritingSystems writingSystems;
- writingSystems.setSupported(QFontDatabase::Any);
- writingSystems.setSupported(QFontDatabase::Latin);
+ names->Release();
+ }
- QStringList ret;
- IDWriteFontFace3 *face3 = nullptr;
- if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3),
- reinterpret_cast<void **>(&face3)))) {
- QString defaultLocaleFamilyName;
- QString englishLocaleFamilyName;
+ BOOL ok;
+ QString defaultLocaleGdiCompatibleFamilyName;
+ QString englishLocaleGdiCompatibleFamilyName;
+ if (SUCCEEDED(face3->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &names, &ok)) && ok) {
+ defaultLocaleGdiCompatibleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
+ englishLocaleGdiCompatibleFamilyName = localeString(names, englishLocale);
- IDWriteLocalizedStrings *names;
- if (SUCCEEDED(face3->GetFamilyNames(&names))) {
- defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
- englishLocaleFamilyName = localeString(names, englishLocale);
+ names->Release();
+ }
- names->Release();
- }
+ QString defaultLocaleGdiCompatibleStyleName;
+ QString englishLocaleGdiCompatibleStyleName;
+ if (SUCCEEDED(face3->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES, &names, &ok)) && ok) {
+ defaultLocaleGdiCompatibleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
+ englishLocaleGdiCompatibleStyleName = localeString(names, englishLocale);
- QString defaultLocaleStyleName;
- QString englishLocaleStyleName;
- if (SUCCEEDED(face3->GetFaceNames(&names))) {
- defaultLocaleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
- englishLocaleStyleName = localeString(names, englishLocale);
+ names->Release();
+ }
- names->Release();
- }
+ QString defaultLocaleTypographicFamilyName;
+ QString englishLocaleTypographicFamilyName;
+ if (SUCCEEDED(face3->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_FAMILY_NAMES, &names, &ok)) && ok) {
+ defaultLocaleTypographicFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
+ englishLocaleTypographicFamilyName = localeString(names, englishLocale);
- QFont::Stretch stretch = fromDirectWriteStretch(face3->GetStretch());
- QFont::Style style = fromDirectWriteStyle(face3->GetStyle());
- QFont::Weight weight = fromDirectWriteWeight(face3->GetWeight());
- bool fixed = face3->IsMonospacedFont();
-
- qCDebug(lcQpaFonts) << "\tFont names:" << englishLocaleFamilyName << ", " << defaultLocaleFamilyName
- << ", style names:" << englishLocaleStyleName << ", " << defaultLocaleStyleName
- << ", stretch:" << stretch
- << ", style:" << style
- << ", weight:" << weight
- << ", fixed:" << fixed;
-
- if (!englishLocaleFamilyName.isEmpty()) {
- if (applicationFont != nullptr) {
- QFontDatabasePrivate::ApplicationFont::Properties properties;
- properties.style = style;
- properties.weight = weight;
- properties.familyName = englishLocaleFamilyName;
- properties.styleName = englishLocaleStyleName;
- applicationFont->properties.append(properties);
+ names->Release();
}
- ret.append(englishLocaleFamilyName);
- QPlatformFontDatabase::registerFont(englishLocaleFamilyName, englishLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
- face->AddRef();
- }
+ QString defaultLocaleTypographicStyleName;
+ QString englishLocaleTypographicStyleName;
+ if (SUCCEEDED(face3->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_SUBFAMILY_NAMES, &names, &ok)) && ok) {
+ defaultLocaleTypographicStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
+ englishLocaleTypographicStyleName = localeString(names, englishLocale);
- if (!defaultLocaleFamilyName.isEmpty() && defaultLocaleFamilyName != englishLocaleFamilyName) {
- if (applicationFont != nullptr) {
- QFontDatabasePrivate::ApplicationFont::Properties properties;
- properties.style = style;
- properties.weight = weight;
- properties.familyName = englishLocaleFamilyName;
- properties.styleName = englishLocaleStyleName;
- applicationFont->properties.append(properties);
+ names->Release();
}
- ret.append(defaultLocaleFamilyName);
- QPlatformFontDatabase::registerFont(defaultLocaleFamilyName, defaultLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
- face->AddRef();
- }
+ QFont::Stretch stretch = fromDirectWriteStretch(face3->GetStretch());
+ QFont::Style style = fromDirectWriteStyle(face3->GetStyle());
+ QFont::Weight weight = fromDirectWriteWeight(face3->GetWeight());
+ bool fixed = face3->IsMonospacedFont();
+
+ qCDebug(lcQpaFonts) << "\tFont names:" << englishLocaleFamilyName << ", " << defaultLocaleFamilyName
+ << ", style names:" << englishLocaleStyleName << ", " << defaultLocaleStyleName
+ << ", stretch:" << stretch
+ << ", style:" << style
+ << ", weight:" << weight
+ << ", fixed:" << fixed;
+
+ if (!englishLocaleFamilyName.isEmpty()) {
+ if (applicationFont != nullptr) {
+ QFontDatabasePrivate::ApplicationFont::Properties properties;
+ properties.style = style;
+ properties.weight = weight;
+ properties.familyName = englishLocaleFamilyName;
+ properties.styleName = englishLocaleStyleName;
+ applicationFont->properties.append(properties);
+ }
- face3->Release();
- } else {
- qCWarning(lcQpaFonts) << "Unable to query IDWriteFontFace3 interface from font face.";
- }
+ ret.insert(englishLocaleFamilyName);
+ QPlatformFontDatabase::registerFont(englishLocaleFamilyName,
+ englishLocaleStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(face, englishLocaleFamilyName));
+ }
- face->Release();
+ if (!defaultLocaleFamilyName.isEmpty() && !ret.contains(defaultLocaleFamilyName)) {
+ if (applicationFont != nullptr) {
+ QFontDatabasePrivate::ApplicationFont::Properties properties;
+ properties.style = style;
+ properties.weight = weight;
+ properties.familyName = englishLocaleFamilyName;
+ properties.styleName = englishLocaleStyleName;
+ applicationFont->properties.append(properties);
+ }
- return ret;
-}
+ ret.insert(defaultLocaleFamilyName);
+ QPlatformFontDatabase::registerFont(defaultLocaleFamilyName,
+ defaultLocaleStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(face, defaultLocaleFamilyName));
+ }
-void QWindowsDirectWriteFontDatabase::releaseHandle(void *handle)
-{
- IDWriteFontFace *face = reinterpret_cast<IDWriteFontFace *>(handle);
- face->Release();
-}
+ if (!englishLocaleGdiCompatibleFamilyName.isEmpty() &&
+ !ret.contains(englishLocaleGdiCompatibleFamilyName)) {
+ if (applicationFont != nullptr) {
+ QFontDatabasePrivate::ApplicationFont::Properties properties;
+ properties.style = style;
+ properties.weight = weight;
+ properties.familyName = englishLocaleGdiCompatibleFamilyName;
+ applicationFont->properties.append(properties);
+ }
-bool QWindowsDirectWriteFontDatabase::fontsAlwaysScalable() const
-{
- return true;
+ ret.insert(englishLocaleGdiCompatibleFamilyName);
+ QPlatformFontDatabase::registerFont(englishLocaleGdiCompatibleFamilyName,
+ englishLocaleGdiCompatibleStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(face, englishLocaleGdiCompatibleFamilyName));
+ }
+
+ if (!defaultLocaleGdiCompatibleFamilyName.isEmpty()
+ && !ret.contains(defaultLocaleGdiCompatibleFamilyName)) {
+ if (applicationFont != nullptr) {
+ QFontDatabasePrivate::ApplicationFont::Properties properties;
+ properties.style = style;
+ properties.weight = weight;
+ properties.familyName = defaultLocaleGdiCompatibleFamilyName;
+ applicationFont->properties.append(properties);
+ }
+
+ ret.insert(defaultLocaleGdiCompatibleFamilyName);
+ QPlatformFontDatabase::registerFont(defaultLocaleGdiCompatibleFamilyName,
+ defaultLocaleGdiCompatibleStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(face, defaultLocaleGdiCompatibleFamilyName));
+ }
+
+ if (!englishLocaleTypographicFamilyName.isEmpty()
+ && !ret.contains(englishLocaleTypographicFamilyName)) {
+ if (applicationFont != nullptr) {
+ QFontDatabasePrivate::ApplicationFont::Properties properties;
+ properties.style = style;
+ properties.weight = weight;
+ properties.familyName = englishLocaleTypographicFamilyName;
+ applicationFont->properties.append(properties);
+ }
+
+ ret.insert(englishLocaleTypographicFamilyName);
+ QPlatformFontDatabase::registerFont(englishLocaleTypographicFamilyName,
+ englishLocaleTypographicStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(face, englishLocaleTypographicFamilyName));
+ }
+
+ if (!defaultLocaleTypographicFamilyName.isEmpty()
+ && !ret.contains(defaultLocaleTypographicFamilyName)) {
+ if (applicationFont != nullptr) {
+ QFontDatabasePrivate::ApplicationFont::Properties properties;
+ properties.style = style;
+ properties.weight = weight;
+ properties.familyName = defaultLocaleTypographicFamilyName;
+ applicationFont->properties.append(properties);
+ }
+
+ ret.insert(defaultLocaleTypographicFamilyName);
+ QPlatformFontDatabase::registerFont(defaultLocaleTypographicFamilyName,
+ defaultLocaleTypographicStyleName,
+ QString(),
+ weight,
+ style,
+ stretch,
+ antialias,
+ scalable,
+ size,
+ fixed,
+ writingSystems,
+ new FontHandle(face, defaultLocaleTypographicFamilyName));
+ }
+
+ } else {
+ qCWarning(lcQpaFonts) << "Unable to query IDWriteFontFace3 interface from font face.";
+ }
+
+ face->Release();
+ }
+
+ return ret.values();
}
bool QWindowsDirectWriteFontDatabase::isPrivateFontFamily(const QString &family) const
@@ -362,62 +690,103 @@ bool QWindowsDirectWriteFontDatabase::isPrivateFontFamily(const QString &family)
return false;
}
+static int QT_WIN_CALLBACK populateBitmapFonts(const LOGFONT *logFont,
+ const TEXTMETRIC *textmetric,
+ DWORD type,
+ LPARAM lparam)
+{
+ Q_UNUSED(textmetric);
+
+ // the "@family" fonts are just the same as "family". Ignore them.
+ const ENUMLOGFONTEX *f = reinterpret_cast<const ENUMLOGFONTEX *>(logFont);
+ const wchar_t *faceNameW = f->elfLogFont.lfFaceName;
+ if (faceNameW[0] && faceNameW[0] != L'@' && wcsncmp(faceNameW, L"WST_", 4)) {
+ const QString faceName = QString::fromWCharArray(faceNameW);
+ if (type & RASTER_FONTTYPE || type == 0) {
+ QWindowsDirectWriteFontDatabase *db = reinterpret_cast<QWindowsDirectWriteFontDatabase *>(lparam);
+ if (!db->hasPopulatedFont(faceName)) {
+ db->registerFontFamily(faceName);
+ db->registerBitmapFont(faceName);
+ }
+ }
+ }
+ return 1; // continue
+}
+
void QWindowsDirectWriteFontDatabase::populateFontDatabase()
{
wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
wchar_t englishLocale[] = L"en-us";
- const QString defaultFontName = defaultFont().families().first();
- const QString systemDefaultFontName = systemDefaultFont().families().first();
+ const QString defaultFontName = defaultFont().families().constFirst();
+ const QString systemDefaultFontName = systemDefaultFont().families().constFirst();
+
+ DirectWriteScope<IDWriteFontCollection2> fontCollection;
+ DirectWriteScope<IDWriteFactory6> factory6;
+ if (FAILED(data()->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory6),
+ reinterpret_cast<void **>(&factory6)))) {
+ qCWarning(lcQpaFonts) << "Can't initialize IDWriteFactory6. Use GDI font engine instead.";
+ return;
+ }
- IDWriteFontCollection *fontCollection;
- if (SUCCEEDED(data()->directWriteFactory->GetSystemFontCollection(&fontCollection))) {
+ if (SUCCEEDED(factory6->GetSystemFontCollection(false,
+ DWRITE_FONT_FAMILY_MODEL_TYPOGRAPHIC,
+ &fontCollection))) {
for (uint i = 0; i < fontCollection->GetFontFamilyCount(); ++i) {
- IDWriteFontFamily *fontFamily;
+ DirectWriteScope<IDWriteFontFamily2> fontFamily;
if (SUCCEEDED(fontCollection->GetFontFamily(i, &fontFamily))) {
QString defaultLocaleName;
QString englishLocaleName;
- IDWriteLocalizedStrings *names;
+ DirectWriteScope<IDWriteLocalizedStrings> names;
if (SUCCEEDED(fontFamily->GetFamilyNames(&names))) {
if (hasDefaultLocale)
- defaultLocaleName = localeString(names, defaultLocale);
+ defaultLocaleName = localeString(*names, defaultLocale);
- englishLocaleName = localeString(names, englishLocale);
+ englishLocaleName = localeString(*names, englishLocale);
}
qCDebug(lcQpaFonts) << "Registering font, english name = " << englishLocaleName << ", name in current locale = " << defaultLocaleName;
if (!defaultLocaleName.isEmpty()) {
registerFontFamily(defaultLocaleName);
- m_populatedFonts.insert(defaultLocaleName, fontFamily);
+ m_populatedFonts.insert(defaultLocaleName, *fontFamily);
fontFamily->AddRef();
if (defaultLocaleName == defaultFontName && defaultFontName != systemDefaultFontName) {
qDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName << "as alternative to" << defaultLocaleName;
- m_populatedFonts.insert(systemDefaultFontName, fontFamily);
+ m_populatedFonts.insert(systemDefaultFontName, *fontFamily);
fontFamily->AddRef();
}
}
if (!englishLocaleName.isEmpty() && englishLocaleName != defaultLocaleName) {
registerFontFamily(englishLocaleName);
- m_populatedFonts.insert(englishLocaleName, fontFamily);
+ m_populatedFonts.insert(englishLocaleName, *fontFamily);
fontFamily->AddRef();
if (englishLocaleName == defaultFontName && defaultFontName != systemDefaultFontName) {
qDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName << "as alternative to" << englishLocaleName;
- m_populatedFonts.insert(systemDefaultFontName, fontFamily);
+ m_populatedFonts.insert(systemDefaultFontName, *fontFamily);
fontFamily->AddRef();
}
}
-
- fontFamily->Release();
}
}
}
+
+ // Since bitmap fonts are not supported by DirectWrite, we need to populate these as well
+ {
+ HDC dummy = GetDC(0);
+ LOGFONT lf;
+ lf.lfCharSet = DEFAULT_CHARSET;
+ lf.lfFaceName[0] = 0;
+ lf.lfPitchAndFamily = 0;
+ EnumFontFamiliesEx(dummy, &lf, populateBitmapFonts, reinterpret_cast<intptr_t>(this), 0);
+ ReleaseDC(0, dummy);
+ }
}
QFont QWindowsDirectWriteFontDatabase::defaultFont() const
@@ -425,4 +794,16 @@ QFont QWindowsDirectWriteFontDatabase::defaultFont() const
return QFont(QStringLiteral("Segoe UI"));
}
+bool QWindowsDirectWriteFontDatabase::supportsVariableApplicationFonts() const
+{
+ QSharedPointer<QWindowsFontEngineData> fontEngineData = data();
+ DirectWriteScope<IDWriteFactory5> factory5;
+ if (SUCCEEDED(fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory5),
+ reinterpret_cast<void **>(&factory5)))) {
+ return true;
+ }
+
+ return false;
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h b/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h
index 89b216a239..093c629a16 100644
--- a/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h
+++ b/src/gui/text/windows/qwindowsdirectwritefontdatabase_p.h
@@ -20,17 +20,18 @@
QT_REQUIRE_CONFIG(directwrite3);
-#include "qwindowsfontdatabasebase_p.h"
+#include "qwindowsfontdatabase_p.h"
#include <QtCore/qloggingcategory.h>
struct IDWriteFactory;
struct IDWriteFont;
+struct IDWriteFont1;
struct IDWriteFontFamily;
struct IDWriteLocalizedStrings;
QT_BEGIN_NAMESPACE
-class Q_GUI_EXPORT QWindowsDirectWriteFontDatabase : public QWindowsFontDatabaseBase
+class Q_GUI_EXPORT QWindowsDirectWriteFontDatabase : public QWindowsFontDatabase
{
Q_DISABLE_COPY_MOVE(QWindowsDirectWriteFontDatabase)
public:
@@ -39,19 +40,34 @@ public:
void populateFontDatabase() override;
void populateFamily(const QString &familyName) override;
+ bool populateFamilyAliases(const QString &missingFamily) override;
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
+ QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override;
QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const override;
QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *font = nullptr) override;
- void releaseHandle(void *handle) override;
QFont defaultFont() const override;
- bool fontsAlwaysScalable() const override;
bool isPrivateFontFamily(const QString &family) const override;
+ bool supportsVariableApplicationFonts() const override;
+
+ void registerBitmapFont(const QString &bitmapFont)
+ {
+ m_populatedBitmapFonts.insert(bitmapFont);
+ }
+
+ bool hasPopulatedFont(const QString &fontFamily) const
+ {
+ return m_populatedFonts.contains(fontFamily);
+ }
private:
+ friend class QWindowsFontEngineDirectWrite;
static QString localeString(IDWriteLocalizedStrings *names, wchar_t localeName[]);
+ QSupportedWritingSystems supportedWritingSystems(IDWriteFontFace *face) const;
+
QHash<QString, IDWriteFontFamily *> m_populatedFonts;
+ QSet<QString> m_populatedBitmapFonts;
};
QT_END_NAMESPACE
diff --git a/src/gui/text/windows/qwindowsfontdatabase.cpp b/src/gui/text/windows/qwindowsfontdatabase.cpp
index b3949402e9..adc06a6c2a 100644
--- a/src/gui/text/windows/qwindowsfontdatabase.cpp
+++ b/src/gui/text/windows/qwindowsfontdatabase.cpp
@@ -10,7 +10,6 @@
#include <QtGui/QFont>
#include <QtGui/QGuiApplication>
-#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtGui/private/qtgui-config_p.h>
#include <QtCore/qmath.h>
@@ -32,6 +31,8 @@
# include "qwindowsfontenginedirectwrite_p.h"
#endif
+#include <mutex>
+
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
@@ -55,7 +56,7 @@ static inline bool useDirectWrite(QFont::HintingPreference hintingPreference,
return hintingPreference == QFont::PreferNoHinting
|| hintingPreference == QFont::PreferVerticalHinting
- || (QHighDpiScaling::isActive() && hintingPreference == QFont::PreferDefaultHinting);
+ || (!qFuzzyCompare(qApp->devicePixelRatio(), 1.0) && hintingPreference == QFont::PreferDefaultHinting);
}
#endif // !QT_NO_DIRECTWRITE
@@ -190,17 +191,6 @@ static inline QFontDatabase::WritingSystem writingSystemFromCharSet(uchar charSe
return QFontDatabase::Any;
}
-#ifdef MAKE_TAG
-#undef MAKE_TAG
-#endif
-// GetFontData expects the tags in little endian ;(
-#define MAKE_TAG(ch1, ch2, ch3, ch4) (\
- (((quint32)(ch4)) << 24) | \
- (((quint32)(ch3)) << 16) | \
- (((quint32)(ch2)) << 8) | \
- ((quint32)(ch1)) \
- )
-
bool qt_localizedName(const QString &name)
{
const QChar *c = name.unicode();
@@ -378,7 +368,7 @@ QString qt_getEnglishName(const QString &familyName, bool includeStyle)
HGDIOBJ oldobj = SelectObject( hdc, hfont );
- const DWORD name_tag = MAKE_TAG( 'n', 'a', 'm', 'e' );
+ const DWORD name_tag = qFromBigEndian(QFont::Tag("name").value());
// get the name table
unsigned char *table = 0;
@@ -427,7 +417,7 @@ QFontNames qt_getCanonicalFontNames(const LOGFONT &lf)
// get the name table
QByteArray table;
- const DWORD name_tag = MAKE_TAG('n', 'a', 'm', 'e');
+ const DWORD name_tag = qFromBigEndian(QFont::Tag("name").value());
DWORD bytes = GetFontData(hdc, name_tag, 0, 0, 0);
if (bytes != GDI_ERROR) {
table.resize(bytes);
@@ -443,18 +433,6 @@ QFontNames qt_getCanonicalFontNames(const LOGFONT &lf)
return fontNames;
}
-static QChar *createFontFile(const QString &faceName)
-{
- QChar *faceNamePtr = nullptr;
- if (!faceName.isEmpty()) {
- const int nameLength = qMin(faceName.length(), LF_FACESIZE - 1);
- faceNamePtr = new QChar[nameLength + 1];
- memcpy(static_cast<void *>(faceNamePtr), faceName.data(), sizeof(wchar_t) * nameLength);
- faceNamePtr[nameLength] = u'\0';
- }
- return faceNamePtr;
-}
-
namespace {
struct StoreFontPayload {
StoreFontPayload(const QString &family,
@@ -561,33 +539,35 @@ static bool addFontToDatabase(QString familyName,
writingSystems.setSupported(ws);
}
- // We came here from populating a different font family, so we have
- // to ensure the entire typographic family is populated before we
- // mark it as such inside registerFont()
- if (!subFamilyName.isEmpty()
- && familyName != subFamilyName
- && sfp->populatedFontFamily != familyName
- && !QPlatformFontDatabase::isFamilyPopulated(familyName)) {
- sfp->windowsFontDatabase->populateFamily(familyName);
- }
-
+ const bool wasPopulated = QPlatformFontDatabase::isFamilyPopulated(familyName);
QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight,
- style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
+ style, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
+
// add fonts windows can generate for us:
if (weight <= QFont::DemiBold && styleName.isEmpty())
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold,
- style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
+ style, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
if (style != QFont::StyleItalic && styleName.isEmpty())
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, weight,
- QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
+ QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
if (weight <= QFont::DemiBold && style != QFont::StyleItalic && styleName.isEmpty())
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold,
- QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
+ QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
+
+ // We came here from populating a different font family, so we have
+ // to ensure the entire typographic family is populated before we
+ // mark it as such inside registerFont()
+ if (!subFamilyName.isEmpty()
+ && familyName != subFamilyName
+ && sfp->populatedFontFamily != familyName
+ && !wasPopulated) {
+ sfp->windowsFontDatabase->populateFamily(familyName);
+ }
if (!subFamilyName.isEmpty() && familyName != subFamilyName) {
QPlatformFontDatabase::registerFont(subFamilyName, subFamilyStyle, foundryName, weight,
- style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
+ style, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
}
if (!englishName.isEmpty() && englishName != familyName)
@@ -725,7 +705,7 @@ void QWindowsFontDatabase::populateFontDatabase()
EnumFontFamiliesEx(dummy, &lf, populateFontFamilies, 0, 0);
ReleaseDC(0, dummy);
// Work around EnumFontFamiliesEx() not listing the system font.
- const QString systemDefaultFamily = QWindowsFontDatabase::systemDefaultFont().families().first();
+ const QString systemDefaultFamily = QWindowsFontDatabase::systemDefaultFont().families().constFirst();
if (QPlatformFontDatabase::resolveFontFamilyAlias(systemDefaultFamily) == systemDefaultFamily)
QPlatformFontDatabase::registerFontFamily(systemDefaultFamily);
addDefaultEUDCFont();
@@ -733,6 +713,7 @@ void QWindowsFontDatabase::populateFontDatabase()
void QWindowsFontDatabase::invalidate()
{
+ QWindowsFontDatabaseBase::invalidate();
removeApplicationFonts();
}
@@ -758,7 +739,8 @@ QWindowsFontDatabase::~QWindowsFontDatabase()
QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, void *handle)
{
- const QString faceName(static_cast<const QChar*>(handle));
+ FontHandle *fontHandle = static_cast<FontHandle *>(handle);
+ const QString faceName = fontHandle->faceName.left(LF_FACESIZE - 1);
QFontEngine *fe = QWindowsFontDatabase::createEngine(fontDef, faceName,
defaultVerticalDPI(),
data());
@@ -820,7 +802,7 @@ QT_WARNING_POP
if (fontEngine) {
if (request.families != fontEngine->fontDef.families) {
qWarning("%s: Failed to load font. Got fallback instead: %s", __FUNCTION__,
- qPrintable(fontEngine->fontDef.families.first()));
+ qPrintable(fontEngine->fontDef.families.constFirst()));
if (fontEngine->ref.loadRelaxed() == 0)
delete fontEngine;
fontEngine = 0;
@@ -844,10 +826,13 @@ QT_WARNING_POP
Q_ASSERT_X(false, Q_FUNC_INFO, "Unhandled font engine.");
}
- UniqueFontData uniqueData;
+ UniqueFontData uniqueData{};
uniqueData.handle = fontHandle;
- uniqueData.refCount.ref();
- m_uniqueFontData[uniqueFamilyName] = uniqueData;
+ ++uniqueData.refCount;
+ {
+ const std::scoped_lock lock(m_uniqueFontDataMutex);
+ m_uniqueFontData[uniqueFamilyName] = uniqueData;
+ }
}
} else {
RemoveFontMemResourceEx(fontHandle);
@@ -868,36 +853,70 @@ QT_WARNING_POP
return fontEngine;
}
-static QList<quint32> getTrueTypeFontOffsets(const uchar *fontData)
+static QList<quint32> getTrueTypeFontOffsets(const uchar *fontData, const uchar *fileEndSentinel)
{
QList<quint32> offsets;
- const quint32 headerTag = *reinterpret_cast<const quint32 *>(fontData);
- if (headerTag != MAKE_TAG('t', 't', 'c', 'f')) {
- if (headerTag != MAKE_TAG(0, 1, 0, 0)
- && headerTag != MAKE_TAG('O', 'T', 'T', 'O')
- && headerTag != MAKE_TAG('t', 'r', 'u', 'e')
- && headerTag != MAKE_TAG('t', 'y', 'p', '1'))
+ if (fileEndSentinel - fontData < 12) {
+ qCWarning(lcQpaFonts) << "Corrupted font data detected";
+ return offsets;
+ }
+
+ const quint32 headerTag = qFromUnaligned<quint32>(fontData);
+ if (headerTag != qFromBigEndian(QFont::Tag("ttcf").value())) {
+ if (headerTag != qFromBigEndian(QFont::Tag("\0\1\0\0").value())
+ && headerTag != qFromBigEndian(QFont::Tag("OTTO").value())
+ && headerTag != qFromBigEndian(QFont::Tag("true").value())
+ && headerTag != qFromBigEndian(QFont::Tag("typ1").value())) {
return offsets;
+ }
offsets << 0;
return offsets;
}
+
+ const quint32 maximumNumFonts = 0xffff;
const quint32 numFonts = qFromBigEndian<quint32>(fontData + 8);
- for (uint i = 0; i < numFonts; ++i) {
- offsets << qFromBigEndian<quint32>(fontData + 12 + i * 4);
+ if (numFonts > maximumNumFonts) {
+ qCWarning(lcQpaFonts) << "Font collection of" << numFonts << "fonts is too large. Aborting.";
+ return offsets;
+ }
+
+ if (quintptr(fileEndSentinel - fontData) > 12 + (numFonts - 1) * 4) {
+ for (quint32 i = 0; i < numFonts; ++i)
+ offsets << qFromBigEndian<quint32>(fontData + 12 + i * 4);
+ } else {
+ qCWarning(lcQpaFonts) << "Corrupted font data detected";
}
+
return offsets;
}
-static void getFontTable(const uchar *fileBegin, const uchar *data, quint32 tag, const uchar **table, quint32 *length)
+static void getFontTable(const uchar *fileBegin, const uchar *fileEndSentinel, const uchar *data, quint32 tag, const uchar **table, quint32 *length)
{
- const quint16 numTables = qFromBigEndian<quint16>(data + 4);
- for (uint i = 0; i < numTables; ++i) {
- const quint32 offset = 12 + 16 * i;
- if (*reinterpret_cast<const quint32 *>(data + offset) == tag) {
- *table = fileBegin + qFromBigEndian<quint32>(data + offset + 8);
- *length = qFromBigEndian<quint32>(data + offset + 12);
- return;
+ if (fileEndSentinel - data >= 6) {
+ const quint16 numTables = qFromBigEndian<quint16>(data + 4);
+ if (fileEndSentinel - data >= 28 + 16 * (numTables - 1)) {
+ for (quint32 i = 0; i < numTables; ++i) {
+ const quint32 offset = 12 + 16 * i;
+ if (qFromUnaligned<quint32>(data + offset) == tag) {
+ const quint32 tableOffset = qFromBigEndian<quint32>(data + offset + 8);
+ if (quintptr(fileEndSentinel - fileBegin) <= tableOffset) {
+ qCWarning(lcQpaFonts) << "Corrupted font data detected";
+ break;
+ }
+ *table = fileBegin + tableOffset;
+ *length = qFromBigEndian<quint32>(data + offset + 12);
+ if (quintptr(fileEndSentinel - *table) < *length) {
+ qCWarning(lcQpaFonts) << "Corrupted font data detected";
+ break;
+ }
+ return;
+ }
+ }
+ } else {
+ qCWarning(lcQpaFonts) << "Corrupted font data detected";
}
+ } else {
+ qCWarning(lcQpaFonts) << "Corrupted font data detected";
}
*table = 0;
*length = 0;
@@ -910,8 +929,9 @@ static void getFamiliesAndSignatures(const QByteArray &fontData,
QList<QFontValues> *values)
{
const uchar *data = reinterpret_cast<const uchar *>(fontData.constData());
+ const uchar *dataEndSentinel = data + fontData.size();
- QList<quint32> offsets = getTrueTypeFontOffsets(data);
+ QList<quint32> offsets = getTrueTypeFontOffsets(data, dataEndSentinel);
if (offsets.isEmpty())
return;
@@ -919,7 +939,9 @@ static void getFamiliesAndSignatures(const QByteArray &fontData,
const uchar *font = data + offsets.at(i);
const uchar *table;
quint32 length;
- getFontTable(data, font, MAKE_TAG('n', 'a', 'm', 'e'), &table, &length);
+ getFontTable(data, dataEndSentinel, font,
+ qFromBigEndian(QFont::Tag("name").value()),
+ &table, &length);
if (!table)
continue;
QFontNames names = qt_getCanonicalFontNames(table, length);
@@ -928,8 +950,11 @@ static void getFamiliesAndSignatures(const QByteArray &fontData,
families->append(std::move(names));
- if (values || signatures)
- getFontTable(data, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length);
+ if (values || signatures) {
+ getFontTable(data, dataEndSentinel, font,
+ qFromBigEndian(QFont::Tag("OS/2").value()),
+ &table, &length);
+ }
if (values) {
QFontValues fontValues;
@@ -1075,7 +1100,7 @@ QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData,
void QWindowsFontDatabase::removeApplicationFonts()
{
- for (const WinApplicationFont &font : qAsConst(m_applicationFonts)) {
+ for (const WinApplicationFont &font : std::as_const(m_applicationFonts)) {
if (font.handle) {
RemoveFontMemResourceEx(font.handle);
} else {
@@ -1087,10 +1112,22 @@ void QWindowsFontDatabase::removeApplicationFonts()
m_eudcFonts.clear();
}
+QWindowsFontDatabase::FontHandle::FontHandle(IDWriteFontFace *face, const QString &name)
+ : fontFace(face), faceName(name)
+{
+ fontFace->AddRef();
+}
+
+
+QWindowsFontDatabase::FontHandle::~FontHandle()
+{
+ if (fontFace != nullptr)
+ fontFace->Release();
+}
+
void QWindowsFontDatabase::releaseHandle(void *handle)
{
- const QChar *faceName = reinterpret_cast<const QChar *>(handle);
- delete[] faceName;
+ delete static_cast<FontHandle *>(handle);
}
QString QWindowsFontDatabase::fontDir() const
@@ -1107,18 +1144,22 @@ bool QWindowsFontDatabase::fontsAlwaysScalable() const
void QWindowsFontDatabase::derefUniqueFont(const QString &uniqueFont)
{
- if (m_uniqueFontData.contains(uniqueFont)) {
- if (!m_uniqueFontData[uniqueFont].refCount.deref()) {
- RemoveFontMemResourceEx(m_uniqueFontData[uniqueFont].handle);
- m_uniqueFontData.remove(uniqueFont);
+ const std::scoped_lock lock(m_uniqueFontDataMutex);
+ const auto it = m_uniqueFontData.find(uniqueFont);
+ if (it != m_uniqueFontData.end()) {
+ if (--it->refCount == 0) {
+ RemoveFontMemResourceEx(it->handle);
+ m_uniqueFontData.erase(it);
}
}
}
void QWindowsFontDatabase::refUniqueFont(const QString &uniqueFont)
{
- if (m_uniqueFontData.contains(uniqueFont))
- m_uniqueFontData[uniqueFont].refCount.ref();
+ const std::scoped_lock lock(m_uniqueFontDataMutex);
+ const auto it = m_uniqueFontData.find(uniqueFont);
+ if (it != m_uniqueFontData.end())
+ ++it->refCount;
}
QStringList QWindowsFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
@@ -1186,6 +1227,7 @@ QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, const Q
HRESULT hr = data->directWriteGdiInterop->CreateFontFaceFromHdc(data->hdc, &directWriteFontFace);
if (SUCCEEDED(hr)) {
bool isColorFont = false;
+ bool needsSimulation = false;
#if QT_CONFIG(direct2d)
IDWriteFontFace2 *directWriteFontFace2 = nullptr;
if (SUCCEEDED(directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace2),
@@ -1193,10 +1235,12 @@ QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, const Q
if (directWriteFontFace2->IsColorFont())
isColorFont = directWriteFontFace2->GetPaletteEntryCount() > 0;
+ needsSimulation = directWriteFontFace2->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE;
+
directWriteFontFace2->Release();
}
#endif // direct2d
- useDw = useDw || useDirectWrite(hintingPreference, fam, isColorFont);
+ useDw = useDw || useDirectWrite(hintingPreference, fam, isColorFont) || needsSimulation;
qCDebug(lcQpaFonts)
<< __FUNCTION__ << request.families.first() << request.pointSize << "pt"
<< "hintingPreference=" << hintingPreference << "color=" << isColorFont
@@ -1212,9 +1256,6 @@ QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, const Q
QFontDef fontDef = request;
fontDef.families = QStringList(QString::fromWCharArray(n));
-
- if (isColorFont)
- fedw->glyphFormat = QFontEngine::Format_ARGB;
fedw->initFontInfo(fontDef, dpi);
fe = fedw;
}
diff --git a/src/gui/text/windows/qwindowsfontdatabase_ft.cpp b/src/gui/text/windows/qwindowsfontdatabase_ft.cpp
index f30a39aecc..0604a85e35 100644
--- a/src/gui/text/windows/qwindowsfontdatabase_ft.cpp
+++ b/src/gui/text/windows/qwindowsfontdatabase_ft.cpp
@@ -295,6 +295,21 @@ static int QT_WIN_CALLBACK storeFont(const LOGFONT *logFont, const TEXTMETRIC *t
return 1;
}
+bool QWindowsFontDatabaseFT::populateFamilyAliases(const QString &missingFamily)
+{
+ Q_UNUSED(missingFamily);
+
+ if (m_hasPopulatedAliases)
+ return false;
+
+ QStringList families = QFontDatabase::families();
+ for (const QString &family : families)
+ populateFamily(family);
+ m_hasPopulatedAliases = true;
+
+ return true;
+}
+
/*
\brief Populates the font database using EnumFontFamiliesEx().
@@ -363,7 +378,7 @@ void QWindowsFontDatabaseFT::populateFontDatabase()
EnumFontFamiliesEx(dummy, &lf, populateFontFamilies, 0, 0);
ReleaseDC(0, dummy);
// Work around EnumFontFamiliesEx() not listing the system font
- const QString systemDefaultFamily = QWindowsFontDatabase::systemDefaultFont().families().first();
+ const QString systemDefaultFamily = QWindowsFontDatabase::systemDefaultFont().families().constFirst();
if (QPlatformFontDatabase::resolveFontFamilyAlias(systemDefaultFamily) == systemDefaultFamily)
QPlatformFontDatabase::registerFontFamily(systemDefaultFamily);
}
@@ -371,7 +386,7 @@ void QWindowsFontDatabaseFT::populateFontDatabase()
QFontEngine * QWindowsFontDatabaseFT::fontEngine(const QFontDef &fontDef, void *handle)
{
QFontEngine *fe = QFreeTypeFontDatabase::fontEngine(fontDef, handle);
- qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDEF" << fontDef.families.first() << fe << handle;
+ qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDEF" << fontDef.families.constFirst() << fe << handle;
return fe;
}
diff --git a/src/gui/text/windows/qwindowsfontdatabase_ft_p.h b/src/gui/text/windows/qwindowsfontdatabase_ft_p.h
index b908cd54c6..381a7be4e7 100644
--- a/src/gui/text/windows/qwindowsfontdatabase_ft_p.h
+++ b/src/gui/text/windows/qwindowsfontdatabase_ft_p.h
@@ -25,6 +25,7 @@ class Q_GUI_EXPORT QWindowsFontDatabaseFT : public QFreeTypeFontDatabase
{
public:
void populateFontDatabase() override;
+ bool populateFamilyAliases(const QString &familyName) override;
void populateFamily(const QString &familyName) override;
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize,
@@ -36,6 +37,8 @@ public:
QString fontDir() const override;
QFont defaultFont() const override;
+
+ bool m_hasPopulatedAliases = false;
};
QT_END_NAMESPACE
diff --git a/src/gui/text/windows/qwindowsfontdatabase_p.h b/src/gui/text/windows/qwindowsfontdatabase_p.h
index 923f875336..0c99c91fde 100644
--- a/src/gui/text/windows/qwindowsfontdatabase_p.h
+++ b/src/gui/text/windows/qwindowsfontdatabase_p.h
@@ -21,6 +21,7 @@
#include <QtCore/QSharedPointer>
#include <QtCore/QLoggingCategory>
#include <QtCore/qhashfunctions.h>
+#include <QtCore/qmutex.h>
#include <QtCore/qt_windows.h>
QT_BEGIN_NAMESPACE
@@ -44,6 +45,7 @@ public:
void populateFontDatabase() override;
void invalidate() override;
+ void removeApplicationFonts();
void populateFamily(const QString &familyName) override;
bool populateFamilyAliases(const QString &missingFamily) override;
@@ -73,8 +75,16 @@ public:
static void debugFormat(QDebug &d, const LOGFONT &lf);
#endif // !QT_NO_DEBUG_STREAM
+ struct FontHandle {
+ FontHandle(const QString &name) : faceName(name) {}
+ FontHandle(IDWriteFontFace *face, const QString &name);
+ ~FontHandle();
+
+ IDWriteFontFace *fontFace = nullptr;
+ QString faceName;
+ };
+
private:
- void removeApplicationFonts();
void addDefaultEUDCFont();
struct WinApplicationFont {
@@ -86,9 +96,10 @@ private:
struct UniqueFontData {
HANDLE handle;
- QAtomicInt refCount;
+ int refCount;
};
+ QMutex m_uniqueFontDataMutex; // protects m_uniqueFontData
QMap<QString, UniqueFontData> m_uniqueFontData;
static unsigned m_fontOptions;
diff --git a/src/gui/text/windows/qwindowsfontdatabasebase.cpp b/src/gui/text/windows/qwindowsfontdatabasebase.cpp
index a6df42da95..84e619b0d9 100644
--- a/src/gui/text/windows/qwindowsfontdatabasebase.cpp
+++ b/src/gui/text/windows/qwindowsfontdatabasebase.cpp
@@ -359,10 +359,10 @@ namespace {
{
}
- inline void addKey(const void *key, const QByteArray &fontData)
+ inline void addKey(const QByteArray &fontData)
{
- Q_ASSERT(!m_fontDatas.contains(key));
- m_fontDatas.insert(key, fontData);
+ if (!m_fontDatas.contains(fontData.data()))
+ m_fontDatas.insert(fontData.data(), fontData);
}
inline void removeKey(const void *key)
@@ -378,6 +378,11 @@ namespace {
UINT32 fontFileReferenceKeySize,
OUT IDWriteFontFileStream **fontFileStream) override;
+ void clear()
+ {
+ m_fontDatas.clear();
+ }
+
private:
ULONG m_referenceCount;
QHash<const void *, QByteArray> m_fontDatas;
@@ -435,52 +440,62 @@ namespace {
return S_OK;
}
- class CustomFontFileLoader
+} // Anonymous namespace
+
+class QCustomFontFileLoader
+{
+public:
+ QCustomFontFileLoader(IDWriteFactory *factory)
{
- public:
- CustomFontFileLoader(IDWriteFactory *factory)
- {
- m_directWriteFactory = factory;
+ m_directWriteFactory = factory;
- if (m_directWriteFactory) {
- m_directWriteFactory->AddRef();
+ if (m_directWriteFactory) {
+ m_directWriteFactory->AddRef();
- m_directWriteFontFileLoader = new DirectWriteFontFileLoader();
- m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader);
- }
+ m_directWriteFontFileLoader = new DirectWriteFontFileLoader();
+ m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader);
}
+ }
- ~CustomFontFileLoader()
- {
- if (m_directWriteFactory != nullptr && m_directWriteFontFileLoader != nullptr)
- m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader);
+ ~QCustomFontFileLoader()
+ {
+ clear();
- if (m_directWriteFactory != nullptr)
- m_directWriteFactory->Release();
- }
+ if (m_directWriteFactory != nullptr && m_directWriteFontFileLoader != nullptr)
+ m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader);
- void addKey(const void *key, const QByteArray &fontData)
- {
- if (m_directWriteFontFileLoader != nullptr)
- m_directWriteFontFileLoader->addKey(key, fontData);
- }
+ if (m_directWriteFactory != nullptr)
+ m_directWriteFactory->Release();
+ }
- void removeKey(const void *key)
- {
- if (m_directWriteFontFileLoader != nullptr)
- m_directWriteFontFileLoader->removeKey(key);
- }
+ void addKey(const QByteArray &fontData)
+ {
+ if (m_directWriteFontFileLoader != nullptr)
+ m_directWriteFontFileLoader->addKey(fontData);
+ }
- IDWriteFontFileLoader *loader() const
- {
- return m_directWriteFontFileLoader;
- }
+ void removeKey(const void *key)
+ {
+ if (m_directWriteFontFileLoader != nullptr)
+ m_directWriteFontFileLoader->removeKey(key);
+ }
+
+ IDWriteFontFileLoader *loader() const
+ {
+ return m_directWriteFontFileLoader;
+ }
+
+ void clear()
+ {
+ if (m_directWriteFontFileLoader != nullptr)
+ m_directWriteFontFileLoader->clear();
+ }
+
+private:
+ IDWriteFactory *m_directWriteFactory = nullptr;
+ DirectWriteFontFileLoader *m_directWriteFontFileLoader = nullptr;
+};
- private:
- IDWriteFactory *m_directWriteFactory = nullptr;
- DirectWriteFontFileLoader *m_directWriteFontFileLoader = nullptr;
- };
-} // Anonymous namespace
#endif // directwrite && direct2d
@@ -550,12 +565,27 @@ void QWindowsFontDatabaseBase::createDirectWriteFactory(IDWriteFactory **factory
IUnknown *result = nullptr;
# if QT_CONFIG(directwrite3)
- DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3), &result);
+ qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory6";
+ DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory6), &result);
+
+ if (result == nullptr) {
+ qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory5";
+ DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory5), &result);
+ }
+
+ if (result == nullptr) {
+ qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory3";
+ DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3), &result);
+ }
# endif
- if (result == nullptr)
+
+ if (result == nullptr) {
+ qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory2";
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result);
+ }
if (result == nullptr) {
+ qCDebug(lcQpaFonts) << "Trying to create plain IDWriteFactory";
if (FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &result))) {
qErrnoWarning("DWriteCreateFactory failed");
return;
@@ -566,16 +596,9 @@ void QWindowsFontDatabaseBase::createDirectWriteFactory(IDWriteFactory **factory
}
#endif // directwrite && direct2d
-static int s_defaultVerticalDPI = 96; // Native Pixels
-
int QWindowsFontDatabaseBase::defaultVerticalDPI()
{
- return s_defaultVerticalDPI;
-}
-
-void QWindowsFontDatabaseBase::setDefaultVerticalDPI(int d)
-{
- s_defaultVerticalDPI = d;
+ return 96;
}
LOGFONT QWindowsFontDatabaseBase::fontDefToLOGFONT(const QFontDef &request, const QString &faceName)
@@ -690,36 +713,55 @@ HFONT QWindowsFontDatabaseBase::systemFont()
QFont QWindowsFontDatabaseBase::systemDefaultFont()
{
// Qt 6: Obtain default GUI font (typically "Segoe UI, 9pt", see QTBUG-58610)
- NONCLIENTMETRICS ncm;
- ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT);
- SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0);
+ NONCLIENTMETRICS ncm = {};
+ ncm.cbSize = sizeof(ncm);
+ SystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0, defaultVerticalDPI());
const QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont);
qCDebug(lcQpaFonts) << __FUNCTION__ << systemFont;
return systemFont;
}
+void QWindowsFontDatabaseBase::invalidate()
+{
+#if QT_CONFIG(directwrite)
+ m_fontFileLoader.reset(nullptr);
+#endif
+}
+
#if QT_CONFIG(directwrite) && QT_CONFIG(direct2d)
-IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArray &fontData) const
+IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArray &fontData)
+{
+ QList<IDWriteFontFace *> faces = createDirectWriteFaces(fontData, false);
+ Q_ASSERT(faces.size() <= 1);
+
+ return faces.isEmpty() ? nullptr : faces.first();
+}
+
+QList<IDWriteFontFace *> QWindowsFontDatabaseBase::createDirectWriteFaces(const QByteArray &fontData,
+ bool queryVariations) const
{
+ QList<IDWriteFontFace *> ret;
QSharedPointer<QWindowsFontEngineData> fontEngineData = data();
if (fontEngineData->directWriteFactory == nullptr) {
qCWarning(lcQpaFonts) << "DirectWrite factory not created in QWindowsFontDatabaseBase::createDirectWriteFace()";
- return nullptr;
+ return ret;
}
- CustomFontFileLoader fontFileLoader(fontEngineData->directWriteFactory);
- fontFileLoader.addKey(this, fontData);
+ if (m_fontFileLoader == nullptr)
+ m_fontFileLoader.reset(new QCustomFontFileLoader(fontEngineData->directWriteFactory));
+
+ m_fontFileLoader->addKey(fontData);
IDWriteFontFile *fontFile = nullptr;
- const void *key = this;
+ const void *key = fontData.data();
HRESULT hres = fontEngineData->directWriteFactory->CreateCustomFontFileReference(&key,
sizeof(void *),
- fontFileLoader.loader(),
+ m_fontFileLoader->loader(),
&fontFile);
if (FAILED(hres)) {
qErrnoWarning(hres, "%s: CreateCustomFontFileReference failed", __FUNCTION__);
- return nullptr;
+ return ret;
}
BOOL isSupportedFontType;
@@ -729,28 +771,75 @@ IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArra
fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces);
if (!isSupportedFontType) {
fontFile->Release();
- return nullptr;
+ return ret;
+ }
+
+#if QT_CONFIG(directwrite3)
+ IDWriteFactory5 *factory5 = nullptr;
+ if (queryVariations && SUCCEEDED(fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory5),
+ reinterpret_cast<void **>(&factory5)))) {
+
+ IDWriteFontSetBuilder1 *builder;
+ if (SUCCEEDED(factory5->CreateFontSetBuilder(&builder))) {
+ if (SUCCEEDED(builder->AddFontFile(fontFile))) {
+ IDWriteFontSet *fontSet;
+ if (SUCCEEDED(builder->CreateFontSet(&fontSet))) {
+ int count = fontSet->GetFontCount();
+ qCDebug(lcQpaFonts) << "Found" << count << "variations in font file";
+ for (int i = 0; i < count; ++i) {
+ IDWriteFontFaceReference *ref;
+ if (SUCCEEDED(fontSet->GetFontFaceReference(i, &ref))) {
+ IDWriteFontFace3 *face;
+ if (SUCCEEDED(ref->CreateFontFace(&face))) {
+ ret.append(face);
+ }
+ ref->Release();
+ }
+ }
+ fontSet->Release();
+ }
+ }
+
+ builder->Release();
+ }
+
+ factory5->Release();
}
+#else
+ Q_UNUSED(queryVariations);
+#endif
// ### Currently no support for .ttc, but we could easily return a list here.
- IDWriteFontFace *directWriteFontFace = nullptr;
- hres = fontEngineData->directWriteFactory->CreateFontFace(fontFaceType,
- 1,
- &fontFile,
- 0,
- DWRITE_FONT_SIMULATIONS_NONE,
- &directWriteFontFace);
- if (FAILED(hres)) {
- qErrnoWarning(hres, "%s: CreateFontFace failed", __FUNCTION__);
- fontFile->Release();
- return nullptr;
+ if (ret.isEmpty()) {
+ IDWriteFontFace *directWriteFontFace = nullptr;
+ hres = fontEngineData->directWriteFactory->CreateFontFace(fontFaceType,
+ 1,
+ &fontFile,
+ 0,
+ DWRITE_FONT_SIMULATIONS_NONE,
+ &directWriteFontFace);
+ if (FAILED(hres)) {
+ qErrnoWarning(hres, "%s: CreateFontFace failed", __FUNCTION__);
+ fontFile->Release();
+ return ret;
+ } else {
+ ret.append(directWriteFontFace);
+ }
}
fontFile->Release();
- return directWriteFontFace;
+
+ return ret;
}
#endif // directwrite && direct2d
+QFontEngine *QWindowsFontDatabaseBase::fontEngine(const QFontDef &fontDef, void *handle)
+{
+ // This function was apparently not used before, and probably isn't now either,
+ // call the base implementation which just prints that it's not supported.
+ return QPlatformFontDatabase::fontEngine(fontDef, handle);
+}
+
QFontEngine *QWindowsFontDatabaseBase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
{
QFontEngine *fontEngine = nullptr;
@@ -760,7 +849,10 @@ QFontEngine *QWindowsFontDatabaseBase::fontEngine(const QByteArray &fontData, qr
if (fontEngineData->directWriteFactory == nullptr)
return nullptr;
- IDWriteFontFace *directWriteFontFace = createDirectWriteFace(fontData);
+ IDWriteFontFace * directWriteFontFace = createDirectWriteFace(fontData);
+ if (directWriteFontFace == nullptr)
+ return nullptr;
+
fontEngine = new QWindowsFontEngineDirectWrite(directWriteFontFace,
pixelSize,
fontEngineData);
@@ -802,7 +894,7 @@ QString QWindowsFontDatabaseBase::familyForStyleHint(QFont::StyleHint styleHint)
default:
break;
}
- return QStringLiteral("MS Shell Dlg 2");
+ return QStringLiteral("Tahoma");
}
// Creation functions
diff --git a/src/gui/text/windows/qwindowsfontdatabasebase_p.h b/src/gui/text/windows/qwindowsfontdatabasebase_p.h
index d192c62d7f..55a3363551 100644
--- a/src/gui/text/windows/qwindowsfontdatabasebase_p.h
+++ b/src/gui/text/windows/qwindowsfontdatabasebase_p.h
@@ -29,6 +29,10 @@
QT_BEGIN_NAMESPACE
+#if QT_CONFIG(directwrite)
+ class QCustomFontFileLoader;
+#endif
+
class QWindowsFontEngineData
{
Q_DISABLE_COPY_MOVE(QWindowsFontEngineData)
@@ -53,10 +57,12 @@ public:
QWindowsFontDatabaseBase();
~QWindowsFontDatabaseBase() override;
+ QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override;
+ void invalidate() override;
+
static int defaultVerticalDPI();
- static void setDefaultVerticalDPI(int d);
static QSharedPointer<QWindowsFontEngineData> data();
#if QT_CONFIG(directwrite)
@@ -91,11 +97,17 @@ public:
protected:
#if QT_CONFIG(directwrite)
- IDWriteFontFace *createDirectWriteFace(const QByteArray &fontData) const;
+ QList<IDWriteFontFace *> createDirectWriteFaces(const QByteArray &fontData,
+ bool queryVariations = true) const;
+ IDWriteFontFace *createDirectWriteFace(const QByteArray &fontData);
#endif
private:
static bool init(QSharedPointer<QWindowsFontEngineData> data);
+
+#if QT_CONFIG(directwrite)
+ mutable std::unique_ptr<QCustomFontFileLoader> m_fontFileLoader;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/gui/text/windows/qwindowsfontengine.cpp b/src/gui/text/windows/qwindowsfontengine.cpp
index 545b5459e2..5de80dc8a3 100644
--- a/src/gui/text/windows/qwindowsfontengine.cpp
+++ b/src/gui/text/windows/qwindowsfontengine.cpp
@@ -104,7 +104,7 @@ void QWindowsFontEngine::getCMap()
SelectObject(hdc, hfont);
bool symb = false;
if (ttf) {
- cmapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p'));
+ cmapTable = getSfntTable(QFont::Tag("cmap").value());
cmap = QFontEngine::getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()),
cmapTable.size(), &symb, &cmapSize);
}
@@ -132,8 +132,9 @@ void QWindowsFontEngine::getCMap()
}
}
-int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs) const
+int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs, int *mappedGlyphs) const
{
+ *mappedGlyphs = 0;
int glyph_pos = 0;
{
if (symbol) {
@@ -143,6 +144,8 @@ int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLa
glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc);
if (!glyphs->glyphs[glyph_pos] && uc < 0x100)
glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc + 0xf000);
+ if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
+ (*mappedGlyphs)++;
++glyph_pos;
}
} else if (ttf) {
@@ -150,6 +153,8 @@ int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLa
while (it.hasNext()) {
const uint uc = it.next();
glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc);
+ if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
+ (*mappedGlyphs)++;
++glyph_pos;
}
} else {
@@ -160,6 +165,8 @@ int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLa
glyphs->glyphs[glyph_pos] = uc;
else
glyphs->glyphs[glyph_pos] = 0;
+ if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
+ (*mappedGlyphs)++;
++glyph_pos;
}
}
@@ -259,7 +266,7 @@ HGDIOBJ QWindowsFontEngine::selectDesignFont() const
return SelectObject(m_fontEngineData->hdc, designFont);
}
-bool QWindowsFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
+int QWindowsFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
{
Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
if (*nglyphs < len) {
@@ -268,12 +275,13 @@ bool QWindowsFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *g
}
glyphs->numGlyphs = *nglyphs;
- *nglyphs = getGlyphIndexes(str, len, glyphs);
+ int mappedGlyphs;
+ *nglyphs = getGlyphIndexes(str, len, glyphs, &mappedGlyphs);
if (!(flags & GlyphIndicesOnly))
recalcAdvances(glyphs, flags);
- return true;
+ return mappedGlyphs;
}
inline void calculateTTFGlyphWidth(HDC hdc, UINT glyph, int &width)
@@ -352,18 +360,6 @@ void QWindowsFontEngine::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::Shape
}
}
-glyph_metrics_t QWindowsFontEngine::boundingBox(const QGlyphLayout &glyphs)
-{
- if (glyphs.numGlyphs == 0)
- return glyph_metrics_t();
-
- QFixed w = 0;
- for (int i = 0; i < glyphs.numGlyphs; ++i)
- w += glyphs.effectiveAdvance(i);
-
- return glyph_metrics_t(0, -tm.tmAscent, w - lastRightBearing(glyphs), tm.tmHeight, w, 0);
-}
-
bool QWindowsFontEngine::getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const
{
Q_ASSERT(metrics != 0);
@@ -483,7 +479,7 @@ namespace {
QFixed QWindowsFontEngine::capHeight() const
{
- const QByteArray tableData = getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
+ const QByteArray tableData = getSfntTable(QFont::Tag("OS/2").value());
if (size_t(tableData.size()) >= sizeof(OS2Table)) {
const OS2Table *table = reinterpret_cast<const OS2Table *>(tableData.constData());
if (qFromBigEndian<quint16>(table->version) >= 2) {
@@ -1101,7 +1097,7 @@ QImage QWindowsFontEngine::alphaRGBMapForGlyph(glyph_t glyph,
QFontEngine *QWindowsFontEngine::cloneWithSize(qreal pixelSize) const
{
QFontDef request = fontDef;
- QString actualFontName = request.families.first();
+ QString actualFontName = request.families.constFirst();
if (!uniqueFamilyName.isEmpty())
request.families = QStringList(uniqueFamilyName);
request.pixelSize = pixelSize;
diff --git a/src/gui/text/windows/qwindowsfontengine_p.h b/src/gui/text/windows/qwindowsfontengine_p.h
index c5ef40761d..07f4db3c4a 100644
--- a/src/gui/text/windows/qwindowsfontengine_p.h
+++ b/src/gui/text/windows/qwindowsfontengine_p.h
@@ -48,7 +48,7 @@ public:
QFixed emSquareSize() const override;
glyph_t glyphIndex(uint ucs4) const override;
- bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override;
+ int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override;
void recalcAdvances(QGlyphLayout *glyphs, ShaperFlags) const override;
void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) override;
@@ -57,7 +57,6 @@ public:
HGDIOBJ selectDesignFont() const;
- glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) override;
glyph_metrics_t boundingBox(glyph_t g) override { return boundingBox(g, QTransform()); }
glyph_metrics_t boundingBox(glyph_t g, const QTransform &t) override;
@@ -89,7 +88,7 @@ public:
bool hasUnreliableGlyphOutline() const override;
- int getGlyphIndexes(const QChar *ch, int numChars, QGlyphLayout *glyphs) const;
+ int getGlyphIndexes(const QChar *ch, int numChars, QGlyphLayout *glyphs, int *mappedGlyphs) const;
void getCMap();
bool getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const;
diff --git a/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp b/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp
index bc8e0b7ed1..47b8a7ee3c 100644
--- a/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp
+++ b/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp
@@ -12,10 +12,14 @@
#include <QtCore/private/qwinregistry_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
-#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtGui/qpainterpath.h>
-#include <dwrite_2.h>
+#if QT_CONFIG(directwrite3)
+# include "qwindowsdirectwritefontdatabase_p.h"
+# include <dwrite_3.h>
+#else
+# include <dwrite_2.h>
+#endif
#include <d2d1.h>
@@ -65,7 +69,7 @@ namespace {
};
void GeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers,
- UINT bezierCount)
+ UINT bezierCount) noexcept
{
for (uint i=0; i<bezierCount; ++i) {
QPointF c1 = fromD2D1_POINT_2F(beziers[i].point1);
@@ -76,48 +80,48 @@ namespace {
}
}
- void GeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount)
+ void GeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount) noexcept
{
for (uint i=0; i<pointsCount; ++i)
m_path->lineTo(fromD2D1_POINT_2F(points[i]));
}
void GeometrySink::BeginFigure(D2D1_POINT_2F startPoint,
- D2D1_FIGURE_BEGIN /*figureBegin*/)
+ D2D1_FIGURE_BEGIN /*figureBegin*/) noexcept
{
m_startPoint = fromD2D1_POINT_2F(startPoint);
m_path->moveTo(m_startPoint);
}
- IFACEMETHODIMP GeometrySink::Close()
+ IFACEMETHODIMP GeometrySink::Close() noexcept
{
return E_NOTIMPL;
}
- void GeometrySink::EndFigure(D2D1_FIGURE_END figureEnd)
+ void GeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) noexcept
{
if (figureEnd == D2D1_FIGURE_END_CLOSED)
m_path->closeSubpath();
}
- void GeometrySink::SetFillMode(D2D1_FILL_MODE fillMode)
+ void GeometrySink::SetFillMode(D2D1_FILL_MODE fillMode) noexcept
{
m_path->setFillRule(fillMode == D2D1_FILL_MODE_ALTERNATE
? Qt::OddEvenFill
: Qt::WindingFill);
}
- void GeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT /*vertexFlags*/)
+ void GeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT /*vertexFlags*/) noexcept
{
/* Not implemented */
}
- IFACEMETHODIMP_(unsigned long) GeometrySink::AddRef()
+ IFACEMETHODIMP_(unsigned long) GeometrySink::AddRef() noexcept
{
return InterlockedIncrement(&m_refCount);
}
- IFACEMETHODIMP_(unsigned long) GeometrySink::Release()
+ IFACEMETHODIMP_(unsigned long) GeometrySink::Release() noexcept
{
unsigned long newCount = InterlockedDecrement(&m_refCount);
if (newCount == 0)
@@ -129,7 +133,7 @@ namespace {
return newCount;
}
- IFACEMETHODIMP GeometrySink::QueryInterface(IID const &riid, void **ppvObject)
+ IFACEMETHODIMP GeometrySink::QueryInterface(IID const &riid, void **ppvObject) noexcept
{
if (__uuidof(IDWriteGeometrySink) == riid) {
*ppvObject = this;
@@ -158,10 +162,13 @@ static DWRITE_MEASURING_MODE renderModeToMeasureMode(DWRITE_RENDERING_MODE rende
}
}
-static DWRITE_RENDERING_MODE hintingPreferenceToRenderingMode(const QFontDef &fontDef)
+DWRITE_RENDERING_MODE QWindowsFontEngineDirectWrite::hintingPreferenceToRenderingMode(const QFontDef &fontDef) const
{
+ if ((fontDef.styleStrategy & QFont::NoAntialias) && glyphFormat != QFontEngine::Format_ARGB)
+ return DWRITE_RENDERING_MODE_ALIASED;
+
QFont::HintingPreference hintingPreference = QFont::HintingPreference(fontDef.hintingPreference);
- if (QHighDpiScaling::isActive() && hintingPreference == QFont::PreferDefaultHinting) {
+ if (!qFuzzyCompare(qApp->devicePixelRatio(), 1.0) && hintingPreference == QFont::PreferDefaultHinting) {
// Microsoft documentation recommends using asymmetric rendering for small fonts
// at pixel size 16 and less, and symmetric for larger fonts.
hintingPreference = fontDef.pixelSize > 16.0
@@ -212,7 +219,7 @@ QWindowsFontEngineDirectWrite::QWindowsFontEngineDirectWrite(IDWriteFontFace *di
fontDef.pixelSize = pixelSize;
collectMetrics();
- cache_cost = (m_ascent.toInt() + m_descent.toInt()) * m_xHeight.toInt() * 2000;
+ cache_cost = m_xHeight.toInt() * m_xHeight.toInt() * 2000;
}
QWindowsFontEngineDirectWrite::~QWindowsFontEngineDirectWrite()
@@ -354,12 +361,14 @@ void QWindowsFontEngineDirectWrite::collectMetrics()
fontFile->Release();
}
- QByteArray table = getSfntTable(MAKE_TAG('h', 'h', 'e', 'a'));
+ QByteArray table = getSfntTable(QFont::Tag("hhea").value());
const int advanceWidthMaxLocation = 10;
if (table.size() >= advanceWidthMaxLocation + int(sizeof(quint16))) {
quint16 advanceWidthMax = qFromBigEndian<quint16>(table.constData() + advanceWidthMaxLocation);
m_maxAdvanceWidth = DESIGN_TO_LOGICAL(advanceWidthMax);
}
+
+ loadKerningPairs(emSquareSize() / QFixed::fromReal(fontDef.pixelSize));
}
QFixed QWindowsFontEngineDirectWrite::underlinePosition() const
@@ -426,13 +435,13 @@ glyph_t QWindowsFontEngineDirectWrite::glyphIndex(uint ucs4) const
return glyphIndex;
}
-bool QWindowsFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
- int *nglyphs, QFontEngine::ShaperFlags flags) const
+int QWindowsFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
+ int *nglyphs, QFontEngine::ShaperFlags flags) const
{
Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
if (*nglyphs < len) {
*nglyphs = len;
- return false;
+ return -1;
}
QVarLengthArray<UINT32> codePoints(len);
@@ -446,11 +455,15 @@ bool QWindowsFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGly
glyphIndices.data());
if (FAILED(hr)) {
qErrnoWarning("%s: GetGlyphIndicesW failed", __FUNCTION__);
- return false;
+ return -1;
}
- for (int i = 0; i < actualLength; ++i)
+ int mappedGlyphs = 0;
+ for (int i = 0; i < actualLength; ++i) {
glyphs->glyphs[i] = glyphIndices.at(i);
+ if (glyphs->glyphs[i] != 0 || isIgnorableChar(codePoints.at(i)))
+ mappedGlyphs++;
+ }
*nglyphs = actualLength;
glyphs->numGlyphs = actualLength;
@@ -458,7 +471,7 @@ bool QWindowsFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGly
if (!(flags & GlyphIndicesOnly))
recalcAdvances(glyphs, {});
- return true;
+ return mappedGlyphs;
}
QFontEngine::FaceId QWindowsFontEngineDirectWrite::faceId() const
@@ -466,7 +479,7 @@ QFontEngine::FaceId QWindowsFontEngineDirectWrite::faceId() const
return m_faceId;
}
-void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags) const
+void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags shaperFlags) const
{
QVarLengthArray<UINT16> glyphIndices(glyphs->numGlyphs);
@@ -478,11 +491,14 @@ void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEn
HRESULT hr;
DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
- if (renderMode == DWRITE_RENDERING_MODE_GDI_CLASSIC || renderMode == DWRITE_RENDERING_MODE_GDI_NATURAL) {
+ bool needsDesignMetrics = shaperFlags & QFontEngine::DesignMetrics;
+ if (!needsDesignMetrics && (renderMode == DWRITE_RENDERING_MODE_GDI_CLASSIC
+ || renderMode == DWRITE_RENDERING_MODE_GDI_NATURAL
+ || renderMode == DWRITE_RENDERING_MODE_ALIASED)) {
hr = m_directWriteFontFace->GetGdiCompatibleGlyphMetrics(float(fontDef.pixelSize),
1.0f,
NULL,
- TRUE,
+ renderMode == DWRITE_RENDERING_MODE_GDI_NATURAL,
glyphIndices.data(),
glyphIndices.size(),
glyphMetrics.data());
@@ -500,6 +516,53 @@ void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEn
}
}
+void QWindowsFontEngineDirectWrite::getUnscaledGlyph(glyph_t glyph,
+ QPainterPath *path,
+ glyph_metrics_t *metric)
+{
+ float advance = 0.0f;
+ UINT16 g = glyph;
+ DWRITE_GLYPH_OFFSET offset;
+ offset.advanceOffset = 0;
+ offset.ascenderOffset = 0;
+ GeometrySink geometrySink(path);
+ HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline(m_unitsPerEm,
+ &g,
+ &advance,
+ &offset,
+ 1,
+ false,
+ false,
+ &geometrySink);
+ if (FAILED(hr)) {
+ qErrnoWarning("%s: GetGlyphRunOutline failed", __FUNCTION__);
+ return;
+ }
+
+ DWRITE_GLYPH_METRICS glyphMetrics;
+ hr = m_directWriteFontFace->GetDesignGlyphMetrics(&g, 1, &glyphMetrics);
+ if (FAILED(hr)) {
+ qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__);
+ return;
+ }
+
+ QFixed advanceWidth = QFixed(int(glyphMetrics.advanceWidth));
+ QFixed leftSideBearing = QFixed(glyphMetrics.leftSideBearing);
+ QFixed rightSideBearing = QFixed(glyphMetrics.rightSideBearing);
+ QFixed advanceHeight = QFixed(int(glyphMetrics.advanceHeight));
+ QFixed verticalOriginY = QFixed(glyphMetrics.verticalOriginY);
+ QFixed topSideBearing = QFixed(glyphMetrics.topSideBearing);
+ QFixed bottomSideBearing = QFixed(glyphMetrics.bottomSideBearing);
+ QFixed width = advanceWidth - leftSideBearing - rightSideBearing;
+ QFixed height = advanceHeight - topSideBearing - bottomSideBearing;
+ *metric = glyph_metrics_t(leftSideBearing,
+ -verticalOriginY + topSideBearing,
+ width,
+ height,
+ advanceWidth,
+ 0);
+}
+
void QWindowsFontEngineDirectWrite::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
QPainterPath *path, QTextItem::RenderFlags flags)
{
@@ -539,7 +602,9 @@ glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(const QGlyphLayout &g
for (int i = 0; i < glyphs.numGlyphs; ++i)
w += glyphs.effectiveAdvance(i);
- return glyph_metrics_t(0, -ascent(), w - lastRightBearing(glyphs), ascent() + descent(), w, 0);
+ const QFixed leftBearing = firstLeftBearing(glyphs);
+ return glyph_metrics_t(leftBearing, -ascent(), w - leftBearing - lastRightBearing(glyphs),
+ ascent() + descent(), w, 0);
}
glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(glyph_t g)
@@ -618,7 +683,37 @@ QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph,
bool QWindowsFontEngineDirectWrite::supportsHorizontalSubPixelPositions() const
{
- return true;
+ DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
+ return (renderMode != DWRITE_RENDERING_MODE_GDI_CLASSIC
+ && renderMode != DWRITE_RENDERING_MODE_GDI_NATURAL
+ && renderMode != DWRITE_RENDERING_MODE_ALIASED);
+}
+
+QFontEngine::Properties QWindowsFontEngineDirectWrite::properties() const
+{
+ IDWriteFontFace2 *directWriteFontFace2;
+ if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace2),
+ reinterpret_cast<void **>(&directWriteFontFace2)))) {
+ DWRITE_FONT_METRICS1 metrics;
+ directWriteFontFace2->GetMetrics(&metrics);
+
+ Properties p = QFontEngine::properties();
+ p.emSquare = metrics.designUnitsPerEm;
+ p.boundingBox = QRectF(metrics.glyphBoxLeft,
+ -metrics.glyphBoxTop,
+ metrics.glyphBoxRight - metrics.glyphBoxLeft,
+ metrics.glyphBoxTop - metrics.glyphBoxBottom);
+ p.ascent = metrics.ascent;
+ p.descent = metrics.descent;
+ p.leading = metrics.lineGap;
+ p.capHeight = metrics.capHeight;
+ p.lineWidth = metrics.underlineThickness;
+
+ directWriteFontFace2->Release();
+ return p;
+ } else {
+ return QFontEngine::properties();
+ }
}
QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
@@ -694,7 +789,10 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
if (SUCCEEDED(hr)) {
RECT rect;
- glyphAnalysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, &rect);
+ glyphAnalysis->GetAlphaTextureBounds(renderMode == DWRITE_RENDERING_MODE_ALIASED
+ ? DWRITE_TEXTURE_ALIASED_1x1
+ : DWRITE_TEXTURE_CLEARTYPE_3x1,
+ &rect);
if (rect.top == rect.bottom || rect.left == rect.right)
return QImage();
@@ -775,7 +873,8 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
b,
a,
colorGlyphsAnalysis,
- boundingRect);
+ boundingRect,
+ renderMode);
}
colorGlyphsAnalysis->Release();
@@ -802,7 +901,8 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
b,
a,
glyphAnalysis,
- boundingRect);
+ boundingRect,
+ renderMode);
}
glyphAnalysis->Release();
@@ -820,7 +920,8 @@ void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
float b,
float a,
IDWriteGlyphRunAnalysis *glyphAnalysis,
- const QRect &boundingRect)
+ const QRect &boundingRect,
+ DWRITE_RENDERING_MODE renderMode)
{
const int width = destination->width();
const int height = destination->height();
@@ -841,12 +942,14 @@ void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
BYTE *alphaValues = alphaValueArray.data();
memset(alphaValues, 0, size);
- HRESULT hr = glyphAnalysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1,
+ HRESULT hr = glyphAnalysis->CreateAlphaTexture(renderMode == DWRITE_RENDERING_MODE_ALIASED
+ ? DWRITE_TEXTURE_ALIASED_1x1
+ : DWRITE_TEXTURE_CLEARTYPE_3x1,
&rect,
alphaValues,
size);
if (SUCCEEDED(hr)) {
- if (destination->hasAlphaChannel()) {
+ if (destination->hasAlphaChannel()) { // Color glyphs
for (int y = 0; y < height; ++y) {
uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
BYTE *src = alphaValues + width * 3 * y;
@@ -864,7 +967,16 @@ void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
qRound(qAlpha(currentRgb) * (1.0 - averageAlpha) + averageAlpha * 255));
}
}
+ } else if (renderMode == DWRITE_RENDERING_MODE_ALIASED) {
+ for (int y = 0; y < height; ++y) {
+ uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
+ BYTE *src = alphaValues + width * y;
+ for (int x = 0; x < width; ++x) {
+ int alpha = *(src++);
+ dest[x] = (alpha << 16) + (alpha << 8) + alpha;
+ }
+ }
} else {
for (int y = 0; y < height; ++y) {
uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
@@ -933,6 +1045,27 @@ void QWindowsFontEngineDirectWrite::initFontInfo(const QFontDef &request,
fontDef.pointSize = fontDef.pixelSize * 72. / dpi;
else if (fontDef.pixelSize == -1)
fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.);
+
+ m_faceId.variableAxes = request.variableAxisValues;
+
+#if QT_CONFIG(directwrite3)
+ IDWriteFontFace3 *face3 = nullptr;
+ if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace3),
+ reinterpret_cast<void **>(&face3)))) {
+ IDWriteLocalizedStrings *names;
+ if (SUCCEEDED(face3->GetFaceNames(&names))) {
+ wchar_t englishLocale[] = L"en-us";
+ fontDef.styleName = QWindowsDirectWriteFontDatabase::localeString(names, englishLocale);
+ names->Release();
+ }
+
+ // Color font
+ if (face3->GetPaletteEntryCount() > 0)
+ glyphFormat = QFontEngine::Format_ARGB;
+
+ face3->Release();
+ }
+#endif
}
QString QWindowsFontEngineDirectWrite::fontNameSubstitute(const QString &familyName)
@@ -1018,7 +1151,7 @@ glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph
if (SUCCEEDED(hr)) {
RECT rect;
- glyphAnalysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, &rect);
+ glyphAnalysis->GetAlphaTextureBounds(renderMode == DWRITE_RENDERING_MODE_ALIASED ? DWRITE_TEXTURE_ALIASED_1x1 : DWRITE_TEXTURE_CLEARTYPE_3x1, &rect);
glyphAnalysis->Release();
int margin = glyphMargin(format);
diff --git a/src/gui/text/windows/qwindowsfontenginedirectwrite_p.h b/src/gui/text/windows/qwindowsfontenginedirectwrite_p.h
index b672f8b2e8..d7c9a79267 100644
--- a/src/gui/text/windows/qwindowsfontenginedirectwrite_p.h
+++ b/src/gui/text/windows/qwindowsfontenginedirectwrite_p.h
@@ -22,6 +22,7 @@ QT_REQUIRE_CONFIG(directwrite);
#include <QtGui/private/qfontengine_p.h>
#include <QtCore/QSharedPointer>
+#include <dwrite.h>
struct IDWriteFont;
struct IDWriteFontFace;
@@ -52,8 +53,8 @@ public:
QFixed emSquareSize() const override;
glyph_t glyphIndex(uint ucs4) const override;
- bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
- ShaperFlags flags) const override;
+ int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
+ ShaperFlags flags) const override;
void recalcAdvances(QGlyphLayout *glyphs, ShaperFlags) const override;
void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
@@ -98,6 +99,9 @@ public:
void initializeHeightMetrics() const override;
+ Properties properties() const override;
+ void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) override;
+
private:
QImage imageForGlyph(glyph_t t,
const QFixedPoint &subPixelPosition,
@@ -105,8 +109,16 @@ private:
const QTransform &xform,
const QColor &color = QColor());
void collectMetrics();
- void renderGlyphRun(QImage *destination, float r, float g, float b, float a, IDWriteGlyphRunAnalysis *glyphAnalysis, const QRect &boundingRect);
+ void renderGlyphRun(QImage *destination,
+ float r,
+ float g,
+ float b,
+ float a,
+ IDWriteGlyphRunAnalysis *glyphAnalysis,
+ const QRect &boundingRect,
+ DWRITE_RENDERING_MODE renderMode);
static QString filenameFromFontFile(IDWriteFontFile *fontFile);
+ DWRITE_RENDERING_MODE hintingPreferenceToRenderingMode(const QFontDef &fontDef) const;
const QSharedPointer<QWindowsFontEngineData> m_fontEngineData;
diff --git a/src/gui/util/qastchandler.cpp b/src/gui/util/qastchandler.cpp
index 6c05e0e248..ec8b92f557 100644
--- a/src/gui/util/qastchandler.cpp
+++ b/src/gui/util/qastchandler.cpp
@@ -37,7 +37,7 @@ quint32 QAstcHandler::astcGLFormat(quint8 xBlockDim, quint8 yBlockDim) const
static const quint32 glFormatRGBABase = 0x93B0; // GL_COMPRESSED_RGBA_ASTC_4x4_KHR
static const quint32 glFormatSRGBBase = 0x93D0; // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR
- static QSize dims[14] = {
+ Q_CONSTINIT static QSize dims[14] = {
{ 4, 4 }, // GL_COMPRESSED_xxx_ASTC_4x4_KHR
{ 5, 4 }, // GL_COMPRESSED_xxx_ASTC_5x4_KHR
{ 5, 5 }, // GL_COMPRESSED_xxx_ASTC_5x5_KHR
@@ -109,9 +109,9 @@ QTextureFileData QAstcHandler::read()
int zBlocks = (zSz + header->blockDimZ - 1) / header->blockDimZ;
int byteCount = 0;
- bool oob = mul_overflow(xBlocks, yBlocks, &byteCount)
- || mul_overflow(byteCount, zBlocks, &byteCount)
- || mul_overflow(byteCount, 16, &byteCount);
+ bool oob = qMulOverflow(xBlocks, yBlocks, &byteCount)
+ || qMulOverflow(byteCount, zBlocks, &byteCount)
+ || qMulOverflow(byteCount, 16, &byteCount);
res.setDataOffset(sizeof(AstcHeader));
diff --git a/src/gui/util/qdesktopservices.cpp b/src/gui/util/qdesktopservices.cpp
index ae452885fc..4a12f6db6f 100644
--- a/src/gui/util/qdesktopservices.cpp
+++ b/src/gui/util/qdesktopservices.cpp
@@ -162,6 +162,13 @@ void QOpenUrlHandlerRegistry::handlerDestroyed(QObject *handler)
\snippet code/src_gui_util_qdesktopservices.cpp 3
+ \note For Android Nougat (SDK 24) and above, URLs with a \c file scheme
+ are opened using \l {Android: FileProvider}{FileProvider} which tries to obtain
+ a shareable \c content scheme URI first. For that reason, Qt for Android defines
+ a file provider with the authority \c ${applicationId}.qtprovider, with \c applicationId
+ being the app's package name to avoid name conflicts. For more information, also see
+ \l {Android: Setting up file sharing}{Setting up file sharing}.
+
\sa setUrlHandler()
*/
bool QDesktopServices::openUrl(const QUrl &url)
diff --git a/src/gui/util/qedidparser.cpp b/src/gui/util/qedidparser.cpp
index 191eea710b..4dae151e6a 100644
--- a/src/gui/util/qedidparser.cpp
+++ b/src/gui/util/qedidparser.cpp
@@ -72,7 +72,7 @@ static QString lookupVendorIdInSystemDatabase(QByteArrayView id)
bool QEdidParser::parse(const QByteArray &blob)
{
const quint8 *data = reinterpret_cast<const quint8 *>(blob.constData());
- const size_t length = blob.length();
+ const size_t length = blob.size();
// Verify header
if (length < 128)
@@ -235,16 +235,22 @@ QString QEdidParser::parseEdidString(const quint8 *data)
{
QByteArray buffer(reinterpret_cast<const char *>(data), 13);
- // Erase carriage return and line feed
- buffer = buffer.replace('\r', '\0').replace('\n', '\0');
-
- // Replace non-printable characters with dash
for (int i = 0; i < buffer.size(); ++i) {
+ // If there are less than 13 characters in the string, the string
+ // is terminated with the ASCII code ‘0Ah’ (line feed) and padded
+ // with ASCII code ‘20h’ (space). See EDID 1.4, sections 3.10.3.1,
+ // 3.10.3.2, and 3.10.3.4.
+ if (buffer[i] == '\n') {
+ buffer.truncate(i);
+ break;
+ }
+
+ // Replace non-printable characters with dash
if (buffer[i] < '\040' || buffer[i] > '\176')
buffer[i] = '-';
}
- return QString::fromLatin1(buffer.trimmed());
+ return QString::fromLatin1(buffer);
}
QT_END_NAMESPACE
diff --git a/src/gui/util/qgraphicsframecapture.cpp b/src/gui/util/qgraphicsframecapture.cpp
new file mode 100644
index 0000000000..e49fb83008
--- /dev/null
+++ b/src/gui/util/qgraphicsframecapture.cpp
@@ -0,0 +1,123 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qgraphicsframecapture_p.h"
+#if (defined (Q_OS_WIN) || defined(Q_OS_LINUX)) && QT_CONFIG(library)
+#include "qgraphicsframecapturerenderdoc_p_p.h"
+#elif QT_CONFIG(metal)
+#include "qgraphicsframecapturemetal_p_p.h"
+#else
+#include "qgraphicsframecapture_p_p.h"
+#endif
+
+#include <QtCore/qstandardpaths.h>
+#include <QtCore/qcoreapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+QGraphicsFrameCapturePrivate::QGraphicsFrameCapturePrivate()
+ : m_capturePath(QStandardPaths::writableLocation(QStandardPaths::TempLocation) +
+ QStringLiteral("/") + QCoreApplication::applicationName() +
+ QStringLiteral("/captures")),
+ m_capturePrefix(QCoreApplication::applicationName())
+{
+
+}
+
+QGraphicsFrameCapture::QGraphicsFrameCapture()
+{
+#if (defined (Q_OS_WIN) || defined(Q_OS_LINUX)) && QT_CONFIG(library)
+ d.reset(new QGraphicsFrameCaptureRenderDoc);
+#elif QT_CONFIG(metal)
+ d.reset(new QGraphicsFrameCaptureMetal);
+#endif
+}
+
+QGraphicsFrameCapture::~QGraphicsFrameCapture()
+{
+
+}
+
+void QGraphicsFrameCapture::setRhi(QRhi *rhi)
+{
+ if (!d.isNull())
+ d->setRhi(rhi);
+}
+
+void QGraphicsFrameCapture::startCaptureFrame()
+{
+ if (!d.isNull())
+ d->startCaptureFrame();
+}
+
+void QGraphicsFrameCapture::endCaptureFrame()
+{
+ if (!d.isNull())
+ d->endCaptureFrame();
+}
+
+QString QGraphicsFrameCapture::capturePath() const
+{
+ if (!d.isNull())
+ return d->capturePath();
+ return QString();
+}
+
+void QGraphicsFrameCapture::setCapturePath(const QString &path)
+{
+ if (!d.isNull())
+ d->setCapturePath(path);
+}
+
+QString QGraphicsFrameCapture::capturePrefix() const
+{
+ if (!d.isNull())
+ return d->capturePrefix();
+ return QString();
+}
+
+void QGraphicsFrameCapture::setCapturePrefix(const QString &prefix)
+{
+ if (!d.isNull())
+ d->setCapturePrefix(prefix);
+}
+
+QString QGraphicsFrameCapture::capturedFileName()
+{
+ if (!d.isNull())
+ return d->capturedFileName();
+
+ return QString();
+}
+
+QStringList QGraphicsFrameCapture::capturedFilesNames()
+{
+ if (!d.isNull())
+ return d->capturedFilesNames();
+
+ return QStringList();
+}
+
+bool QGraphicsFrameCapture::isLoaded() const
+{
+ if (!d.isNull())
+ return d->initialized();
+
+ return false;
+}
+
+bool QGraphicsFrameCapture::isCapturing() const
+{
+ if (!d.isNull())
+ return d->isCapturing();
+
+ return false;
+}
+
+void QGraphicsFrameCapture::openCapture() const
+{
+ if (!d.isNull())
+ d->openCapture();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/util/qgraphicsframecapture_p.h b/src/gui/util/qgraphicsframecapture_p.h
new file mode 100644
index 0000000000..fef37d2869
--- /dev/null
+++ b/src/gui/util/qgraphicsframecapture_p.h
@@ -0,0 +1,55 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QGRAPHICSFRAMECAPTURE_P_H
+#define QGRAPHICSFRAMECAPTURE_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 <QtCore/qscopedpointer.h>
+#include <QtGui/qtguiglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsFrameCapturePrivate;
+class QRhi;
+
+class Q_GUI_EXPORT QGraphicsFrameCapture
+{
+public:
+ QGraphicsFrameCapture();
+ ~QGraphicsFrameCapture();
+
+ void setRhi(QRhi *rhi);
+ void startCaptureFrame();
+ void endCaptureFrame();
+
+ QString capturePath() const;
+ void setCapturePath(const QString &path);
+
+ QString capturePrefix() const;
+ void setCapturePrefix(const QString &prefix);
+
+ QString capturedFileName();
+ QStringList capturedFilesNames();
+
+ bool isLoaded() const;
+ bool isCapturing() const;
+ void openCapture() const;
+
+private:
+ QScopedPointer<QGraphicsFrameCapturePrivate> d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGRAPHICSFRAMECAPTURE_P_H
diff --git a/src/gui/util/qgraphicsframecapture_p_p.h b/src/gui/util/qgraphicsframecapture_p_p.h
new file mode 100644
index 0000000000..f8dbd2ca1d
--- /dev/null
+++ b/src/gui/util/qgraphicsframecapture_p_p.h
@@ -0,0 +1,67 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QGRAPHICSFRAMECAPTURE_P_P_H
+#define QGRAPHICSFRAMECAPTURE_P_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 <QtCore/qnamespace.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcGraphicsFrameCapture)
+
+class QRhi;
+struct QRhiNativeHandles;
+
+class QGraphicsFrameCapturePrivate
+{
+public:
+ QGraphicsFrameCapturePrivate() ;
+ virtual ~QGraphicsFrameCapturePrivate() = default;
+
+ virtual void setRhi(QRhi *rhi) = 0;
+ virtual void startCaptureFrame() = 0;
+ virtual void endCaptureFrame() = 0;
+
+ QString capturePath() const { return m_capturePath; };
+ virtual void setCapturePath(const QString &path) { m_capturePath = path; }
+
+ QString capturePrefix() const { return m_capturePrefix; }
+ virtual void setCapturePrefix(const QString &prefix) { m_capturePrefix = prefix; }
+
+ virtual QString capturedFileName() const
+ {
+ return !m_capturedFilesNames.isEmpty() ? m_capturedFilesNames.last() : QString();
+ }
+ virtual QStringList capturedFilesNames() const { return m_capturedFilesNames; }
+
+ virtual bool initialized() const = 0;
+ virtual bool isCapturing() const = 0;
+ virtual void openCapture() = 0;
+
+protected:
+ QRhi *m_rhi = nullptr;
+ QRhiNativeHandles *m_rhiHandles = nullptr;
+ void *m_nativeHandle = nullptr;
+ QString m_capturePath;
+ QString m_capturePrefix;
+ QStringList m_capturedFilesNames;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGRAPHICSFRAMECAPTURE_P_P_H
diff --git a/src/gui/util/qgraphicsframecapturemetal.mm b/src/gui/util/qgraphicsframecapturemetal.mm
new file mode 100644
index 0000000000..b0ff0bab2b
--- /dev/null
+++ b/src/gui/util/qgraphicsframecapturemetal.mm
@@ -0,0 +1,169 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qgraphicsframecapturemetal_p_p.h"
+#include <QtCore/qurl.h>
+#include "Metal/Metal.h"
+#include "qglobal.h"
+#include <QtGui/rhi/qrhi.h>
+#include <QtGui/rhi/qrhi_platform.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcGraphicsFrameCapture, "qt.gui.graphicsframecapture")
+
+#if __has_feature(objc_arc)
+#error ARC not supported
+#endif
+
+uint QGraphicsFrameCaptureMetal::frameNumber = 0;
+
+QGraphicsFrameCaptureMetal::QGraphicsFrameCaptureMetal()
+{
+ qputenv("METAL_CAPTURE_ENABLED", QByteArrayLiteral("1"));
+
+ m_captureDescriptor = [MTLCaptureDescriptor new];
+}
+
+QGraphicsFrameCaptureMetal::~QGraphicsFrameCaptureMetal()
+{
+#if defined(Q_OS_MACOS) && QT_CONFIG(process)
+ if (m_process) {
+ m_process->terminate();
+ delete m_process;
+ }
+#endif
+ [m_captureDescriptor release];
+}
+
+void QGraphicsFrameCaptureMetal::setRhi(QRhi *rhi)
+{
+ if (!rhi)
+ return;
+
+ QRhi::Implementation backend = rhi->backend();
+ const QRhiNativeHandles *nh = rhi->nativeHandles();
+
+ switch (backend) {
+ case QRhi::Implementation::Metal: {
+ const QRhiMetalNativeHandles *mtlnh = static_cast<const QRhiMetalNativeHandles *>(nh);
+ if (mtlnh->cmdQueue) {
+ m_captureDescriptor.captureObject = mtlnh->cmdQueue;
+ } else if (mtlnh->dev) {
+ m_captureDescriptor.captureObject = mtlnh->dev;
+ } else {
+ qCWarning(lcGraphicsFrameCapture) << "No valid Metal Device or Metal Command Queue found";
+ m_initialized = false;
+ return;
+ }
+ break;
+ }
+ default: {
+ qCWarning(lcGraphicsFrameCapture) << "Invalid handles were provided. MTLCaptureManager works only with Metal API";
+ m_initialized = false;
+ return;
+ break;
+ }
+ }
+
+ if (!m_captureManager) {
+ m_captureManager = MTLCaptureManager.sharedCaptureManager;
+ bool supportDocs = [m_captureManager supportsDestination:MTLCaptureDestinationGPUTraceDocument];
+ if (supportDocs) {
+ m_captureDescriptor.destination = MTLCaptureDestinationGPUTraceDocument;
+ m_initialized = true;
+ }
+ }
+}
+
+void QGraphicsFrameCaptureMetal::startCaptureFrame()
+{
+ if (!initialized()) {
+ qCWarning(lcGraphicsFrameCapture) << "Capturing on Metal was not initialized. Starting capturing can not be done.";
+ return;
+ }
+
+ if (isCapturing()) {
+ qCWarning(lcGraphicsFrameCapture) << "A frame capture is already in progress,"
+ "will not initiate another one until QGraphicsFrameCapture::endCaptureFrame is called.";
+ return;
+ }
+
+ updateCaptureFileName();
+ NSError *error;
+ if (![m_captureManager startCaptureWithDescriptor:m_captureDescriptor error:&error]) {
+ QString errorMsg = QString::fromNSString(error.localizedDescription);
+ qCWarning(lcGraphicsFrameCapture, "Failed to start capture : %s", qPrintable(errorMsg));
+ }
+}
+
+void QGraphicsFrameCaptureMetal::endCaptureFrame()
+{
+ if (!initialized()) {
+ qCWarning(lcGraphicsFrameCapture) << "Capturing on Metal was not initialized. End capturing can not be done.";
+ return;
+ }
+
+ if (!isCapturing()) {
+ qCWarning(lcGraphicsFrameCapture) << "A call to QGraphicsFrameCapture::endCaptureFrame can not be done"
+ " without a call to QGraphicsFrameCapture::startCaptureFrame";
+ return;
+ }
+
+ [m_captureManager stopCapture];
+ m_capturedFilesNames.append(QString::fromNSString(m_traceURL.path));
+ frameNumber++;
+}
+
+bool QGraphicsFrameCaptureMetal::initialized() const
+{
+ return m_initialized;
+}
+
+bool QGraphicsFrameCaptureMetal::isCapturing() const
+{
+ if (!initialized()) {
+ qCWarning(lcGraphicsFrameCapture) << "Capturing on Metal was not initialized. Can not query if capturing is in progress or not.";
+ return false;
+ }
+
+ return [m_captureManager isCapturing];
+}
+
+void QGraphicsFrameCaptureMetal::openCapture()
+{
+#if defined(Q_OS_MACOS)
+#if !QT_CONFIG(process)
+ qFatal("QGraphicsFrameCapture requires QProcess on macOS");
+#else
+ if (!initialized()) {
+ qCWarning(lcGraphicsFrameCapture) << "Capturing on Metal was not initialized. Can not open XCode with a valid capture.";
+ return;
+ }
+
+ if (!m_process) {
+ m_process = new QProcess();
+ m_process->setProgram(QStringLiteral("xed"));
+ QStringList args;
+ args.append(QUrl::fromNSURL(m_traceURL).toLocalFile());
+ m_process->setArguments(args);
+ }
+
+ m_process->kill();
+ m_process->start();
+#endif
+#endif
+}
+
+void QGraphicsFrameCaptureMetal::updateCaptureFileName()
+{
+ m_traceURL = QUrl::fromLocalFile(m_capturePath + "/" + m_capturePrefix + "_" + QString::number(frameNumber) + ".gputrace").toNSURL();
+ // We need to remove the trace file if it already existed else MTLCaptureManager
+ // will fail to.
+ if ([NSFileManager.defaultManager fileExistsAtPath:m_traceURL.path])
+ [NSFileManager.defaultManager removeItemAtURL:m_traceURL error:nil];
+
+ m_captureDescriptor.outputURL = m_traceURL;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/util/qgraphicsframecapturemetal_p_p.h b/src/gui/util/qgraphicsframecapturemetal_p_p.h
new file mode 100644
index 0000000000..d703949de2
--- /dev/null
+++ b/src/gui/util/qgraphicsframecapturemetal_p_p.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QGRAPHICSFRAMEDECAPTUREMETAL_P_P_H
+#define QGRAPHICSFRAMEDECAPTUREMETAL_P_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 "qgraphicsframecapture_p_p.h"
+#include <QtCore/qmutex.h>
+#ifdef Q_OS_MACOS
+#include <QtCore/qprocess.h>
+#endif
+
+Q_FORWARD_DECLARE_OBJC_CLASS(MTLCaptureManager);
+Q_FORWARD_DECLARE_OBJC_CLASS(MTLCaptureDescriptor);
+Q_FORWARD_DECLARE_OBJC_CLASS(NSURL);
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsFrameCaptureMetal : public QGraphicsFrameCapturePrivate
+{
+public:
+ QGraphicsFrameCaptureMetal();
+ ~QGraphicsFrameCaptureMetal();
+
+ void setRhi(QRhi *rhi) override;
+ void startCaptureFrame() override;
+ void endCaptureFrame() override;
+ bool initialized() const override;
+ bool isCapturing() const override;
+ void openCapture() override;
+
+private:
+ void updateCaptureFileName();
+#if defined(Q_OS_MACOS) && QT_CONFIG(process)
+ QProcess *m_process = nullptr;
+#endif
+ MTLCaptureManager *m_captureManager = nullptr;
+ MTLCaptureDescriptor *m_captureDescriptor = nullptr;
+ NSURL *m_traceURL = nullptr;
+ bool m_initialized = false;
+ static uint frameNumber;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGRAPHICSFRAMEDECAPTUREMETAL_P_P_H
diff --git a/src/gui/util/qgraphicsframecapturerenderdoc.cpp b/src/gui/util/qgraphicsframecapturerenderdoc.cpp
new file mode 100644
index 0000000000..88ba9d839f
--- /dev/null
+++ b/src/gui/util/qgraphicsframecapturerenderdoc.cpp
@@ -0,0 +1,310 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qgraphicsframecapturerenderdoc_p_p.h"
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qlibrary.h>
+#include <QtCore/qmutex.h>
+#include "QtGui/rhi/qrhi.h"
+#include "QtGui/rhi/qrhi_platform.h"
+#include "QtGui/qopenglcontext.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcGraphicsFrameCapture, "qt.gui.graphicsframecapture")
+
+RENDERDOC_API_1_6_0 *QGraphicsFrameCaptureRenderDoc::s_rdocApi = nullptr;
+#if QT_CONFIG(thread)
+QBasicMutex QGraphicsFrameCaptureRenderDoc::s_frameCaptureMutex;
+#endif
+
+#if QT_CONFIG(opengl)
+static void *glNativeContext(QOpenGLContext *context) {
+ void *nctx = nullptr;
+ if (context != nullptr && context->isValid()) {
+#ifdef Q_OS_WIN
+ nctx = context->nativeInterface<QNativeInterface::QWGLContext>()->nativeContext();
+#endif
+
+#ifdef Q_OS_LINUX
+#if QT_CONFIG(egl)
+ QNativeInterface::QEGLContext *eglItf = context->nativeInterface<QNativeInterface::QEGLContext>();
+ if (eglItf)
+ nctx = eglItf->nativeContext();
+#endif
+
+#if QT_CONFIG(xcb_glx_plugin)
+ QNativeInterface::QGLXContext *glxItf = context->nativeInterface<QNativeInterface::QGLXContext>();
+ if (glxItf)
+ nctx = glxItf->nativeContext();
+#endif
+#endif
+
+#if QT_CONFIG(metal)
+ nctx = context->nativeInterface<QNativeInterface::QCocoaGLContext>()->nativeContext();
+#endif
+ }
+ return nctx;
+}
+#endif // QT_CONFIG(opengl)
+
+/*!
+ \class QGraphicsFrameCaptureRenderDoc
+ \internal
+ \brief The QGraphicsFrameCaptureRenderDoc class provides a way to capture a record of draw calls
+ for different graphics APIs.
+ \since 6.6
+ \inmodule QtGui
+
+ For applications that render using graphics APIs like Vulkan or OpenGL, it would be
+ convenient to have a way to check the draw calls done by the application. Specially
+ for applications that make a large amount of draw calls and the output is different
+ from what is expected.
+
+ This class acts as a wrapper over \l {https://renderdoc.org/}{RenderDoc} that allows
+ applications to capture a rendered frame either programmatically, by clicking a key
+ on the keyboard or both. The captured frame could be viewed later using RenderDoc GUI.
+
+ Read the \l {https://renderdoc.org/docs/index.html} {RenderDoc Documentation}
+ for more information.
+
+ \section1 API device handle
+
+ The functions that capture a frame like QGraphicsFrameCaptureRenderDoc::startCaptureFrame takes a device
+ pointer as argument. This pointer is unique for each graphics API and is associated
+ with the window that will be captured. This pointer has a default value of \c nullptr.
+ If no value is passed to the function the underlying API will try to find the device to
+ use, but it is not guaranteed specially in a multi-window applications.
+
+ For OpenGL, the pointer should be the OpenGL context on the platform OpenGL is being
+ used. For example, on Windows it should be \c HGLRC.
+
+ For Vulkan, the pointer should point to the dispatch table within the \c VkInstance.
+*/
+
+
+
+/*!
+ Creates a new object of this class. The constructor will load RenderDoc library
+ from the default path.
+
+ Only one instance of RenderDoc library is loaded at runtime which means creating
+ several instances of this class will not affect the RenderDoc initialization.
+*/
+
+QGraphicsFrameCaptureRenderDoc::QGraphicsFrameCaptureRenderDoc()
+ : m_nativeHandlesSet(false)
+{
+ if (!s_rdocApi)
+ init();
+}
+
+void QGraphicsFrameCaptureRenderDoc::setRhi(QRhi *rhi)
+{
+ if (!rhi)
+ return;
+
+ QRhi::Implementation backend = rhi->backend();
+ const QRhiNativeHandles *nh = rhi->nativeHandles();
+
+ switch (backend) {
+ case QRhi::Implementation::D3D11: {
+#ifdef Q_OS_WIN
+ const QRhiD3D11NativeHandles *d3d11nh = static_cast<const QRhiD3D11NativeHandles *>(nh);
+ m_nativeHandle = d3d11nh->dev;
+ break;
+#endif
+ qCWarning(lcGraphicsFrameCapture) << "Could not find valid handles for D3D11. Check platform support";
+ break;
+ }
+ case QRhi::Implementation::D3D12: {
+#ifdef Q_OS_WIN
+ const QRhiD3D12NativeHandles *d3d12nh = static_cast<const QRhiD3D12NativeHandles *>(nh);
+ m_nativeHandle = d3d12nh->dev;
+ break;
+#endif
+ qCWarning(lcGraphicsFrameCapture) << "Could not find valid handles for D3D12. Check platform support";
+ break;
+ }
+ case QRhi::Implementation::Vulkan: {
+#if QT_CONFIG(vulkan)
+ const QRhiVulkanNativeHandles *vknh = static_cast<const QRhiVulkanNativeHandles *>(nh);
+ m_nativeHandle = RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(vknh->inst->vkInstance());
+ break;
+#endif
+ qCWarning(lcGraphicsFrameCapture) << "Could not find valid handles for Vulkan. Check platform support";
+ break;
+ }
+ case QRhi::Implementation::OpenGLES2: {
+#ifndef QT_NO_OPENGL
+ const QRhiGles2NativeHandles *glnh = static_cast<const QRhiGles2NativeHandles *>(nh);
+ m_nativeHandle = glNativeContext(glnh->context);
+ if (m_nativeHandle)
+ break;
+#endif
+ qCWarning(lcGraphicsFrameCapture) << "Could not find valid handles for OpenGL. Check platform support";
+ break;
+ }
+ case QRhi::Implementation::Metal:
+ case QRhi::Implementation::Null:
+ qCWarning(lcGraphicsFrameCapture) << "Invalid handles were provided."
+ " Metal and Null backends are not supported with RenderDoc";
+ break;
+ }
+
+ if (m_nativeHandle)
+ m_nativeHandlesSet = true;
+}
+
+/*!
+ Starts a frame capture using the set native handles provided through QGraphicsFrameCaptureRenderDoc::setRhi
+ \a device. This function must be called before QGraphicsFrameCaptureRenderDoc::endCaptureFrame.
+ \sa {API device handle}
+*/
+void QGraphicsFrameCaptureRenderDoc::startCaptureFrame()
+{
+
+ if (!initialized()) {
+ qCWarning(lcGraphicsFrameCapture) << "RenderDoc was not initialized."
+ " Starting capturing can not be done.";
+ return;
+ }
+
+#if QT_CONFIG(thread)
+ // There is a single instance of RenderDoc library and it needs mutex for multithreading access.
+ QMutexLocker locker(&s_frameCaptureMutex);
+#endif
+ if (s_rdocApi->IsFrameCapturing()) {
+ qCWarning(lcGraphicsFrameCapture) << "A frame capture is already in progress, "
+ "will not initiate another one until"
+ " QGraphicsFrameCapture::endCaptureFrame is called.";
+ return;
+ }
+
+ qCInfo(lcGraphicsFrameCapture) << "A frame capture is going to start.";
+ updateCapturePathAndTemplate();
+ s_rdocApi->StartFrameCapture(m_nativeHandle, nullptr);
+}
+
+/*!
+ Ends a frame capture started by a call to QGraphicsFrameCaptureRenderDoc::startCaptureFrame
+ using the set native handles provided through QGraphicsFrameCaptureRenderDoc::setRhi.
+ This function must be called after QGraphicsFrameCaptureRenderDoc::startCaptureFrame.
+ Otherwise, a warning message will be printend and nothing will happen.
+ \sa {API device handle}
+*/
+void QGraphicsFrameCaptureRenderDoc::endCaptureFrame()
+{
+ if (!initialized()) {
+ qCWarning(lcGraphicsFrameCapture) << "RenderDoc was not initialized."
+ " End capturing can not be done.";
+ return;
+ }
+
+#if QT_CONFIG(thread)
+ // There is a single instance of RenderDoc library and it needs mutex for multithreading access.
+ QMutexLocker locker(&s_frameCaptureMutex);
+#endif
+ if (!s_rdocApi->IsFrameCapturing()) {
+ qCWarning(lcGraphicsFrameCapture) << "A call to QGraphicsFrameCapture::endCaptureFrame can not be done"
+ " without a call to QGraphicsFrameCapture::startCaptureFrame";
+ return;
+ }
+
+ qCInfo(lcGraphicsFrameCapture) << "A frame capture is going to end.";
+ uint32_t result = s_rdocApi->EndFrameCapture(m_nativeHandle, nullptr);
+
+ if (result) {
+ uint32_t count = s_rdocApi->GetNumCaptures();
+ uint32_t pathLength = 0;
+ s_rdocApi->GetCapture(count - 1, nullptr, &pathLength, nullptr);
+ if (pathLength > 0) {
+ QVarLengthArray<char> name(pathLength, 0);
+ s_rdocApi->GetCapture(count - 1, name.data(), &pathLength, nullptr);
+ m_capturedFilesNames.append(QString::fromUtf8(name.data(), -1));
+ }
+ }
+}
+
+void QGraphicsFrameCaptureRenderDoc::updateCapturePathAndTemplate()
+{
+ if (!initialized()) {
+ qCWarning(lcGraphicsFrameCapture) << "RenderDoc was not initialized."
+ " Updating save location can not be done.";
+ return;
+ }
+
+
+ QString rdocFilePathTemplate = m_capturePath + QStringLiteral("/") + m_capturePrefix;
+ s_rdocApi->SetCaptureFilePathTemplate(rdocFilePathTemplate.toUtf8().constData());
+}
+
+/*!
+ Returns true if the API is loaded and can capture frames or not.
+*/
+bool QGraphicsFrameCaptureRenderDoc::initialized() const
+{
+ return s_rdocApi && m_nativeHandlesSet;
+}
+
+bool QGraphicsFrameCaptureRenderDoc::isCapturing() const
+{
+ if (!initialized()) {
+ qCWarning(lcGraphicsFrameCapture) << "RenderDoc was not initialized."
+ " Can not query if capturing is in progress or not.";
+ return false;
+ }
+
+ return s_rdocApi->IsFrameCapturing();
+}
+
+void QGraphicsFrameCaptureRenderDoc::openCapture()
+{
+ if (!initialized()) {
+ qCWarning(lcGraphicsFrameCapture) << "RenderDoc was not initialized."
+ " Can not open RenderDoc UI tool.";
+ return;
+ }
+
+#if QT_CONFIG(thread)
+ // There is a single instance of RenderDoc library and it needs mutex for multithreading access.
+ QMutexLocker locker(&s_frameCaptureMutex);
+#endif
+ if (s_rdocApi->IsTargetControlConnected())
+ s_rdocApi->ShowReplayUI();
+ else
+ s_rdocApi->LaunchReplayUI(1, nullptr);
+}
+
+void QGraphicsFrameCaptureRenderDoc::init()
+{
+#if QT_CONFIG(thread)
+ // There is a single instance of RenderDoc library and it needs mutex for multithreading access.
+ QMutexLocker locker(&s_frameCaptureMutex);
+#endif
+
+ QLibrary renderDocLib(QStringLiteral("renderdoc"));
+ pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI) renderDocLib.resolve("RENDERDOC_GetAPI");
+ if (!renderDocLib.isLoaded() || (RENDERDOC_GetAPI == nullptr)) {
+ qCWarning(lcGraphicsFrameCapture) << renderDocLib.errorString().toLatin1();
+ return;
+ }
+
+ int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_6_0, static_cast<void **>(static_cast<void *>(&s_rdocApi)));
+
+ if (ret == 0) {
+ qCWarning(lcGraphicsFrameCapture) << "The requested RenderDoc API is invalid or not supported";
+ return;
+ }
+
+ s_rdocApi->MaskOverlayBits(RENDERDOC_OverlayBits::eRENDERDOC_Overlay_None,
+ RENDERDOC_OverlayBits::eRENDERDOC_Overlay_None);
+ s_rdocApi->SetCaptureKeys(nullptr, 0);
+ s_rdocApi->SetFocusToggleKeys(nullptr, 0);
+
+ QString rdocFilePathTemplate = m_capturePath + QStringLiteral("/") + m_capturePrefix;
+ s_rdocApi->SetCaptureFilePathTemplate(rdocFilePathTemplate.toUtf8().constData());
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/util/qgraphicsframecapturerenderdoc_p_p.h b/src/gui/util/qgraphicsframecapturerenderdoc_p_p.h
new file mode 100644
index 0000000000..c0d92ea733
--- /dev/null
+++ b/src/gui/util/qgraphicsframecapturerenderdoc_p_p.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QGRAPHICSFRAMECAPTURERENDERDOC_P_P_H
+#define QGRAPHICSFRAMECAPTURERENDERDOC_P_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 <renderdoc_app.h>
+#include "qgraphicsframecapture_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#if QT_CONFIG(thread)
+class QBasicMutex;
+#endif
+
+class QGraphicsFrameCaptureRenderDoc : public QGraphicsFrameCapturePrivate
+{
+public:
+ QGraphicsFrameCaptureRenderDoc();
+ ~QGraphicsFrameCaptureRenderDoc() = default;
+
+ void setRhi(QRhi *rhi) override;
+ void startCaptureFrame() override;
+ void endCaptureFrame() override;
+ bool initialized() const override;
+ bool isCapturing() const override;
+ void openCapture() override;
+
+private:
+ void init();
+ void updateCapturePathAndTemplate();
+ static RENDERDOC_API_1_5_0 *s_rdocApi;
+#if QT_CONFIG(thread)
+ static QBasicMutex s_frameCaptureMutex;
+#endif
+ bool m_nativeHandlesSet;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGRAPHICSFRAMECAPTURERENDERDOC_P_P_H
diff --git a/src/gui/util/qgridlayoutengine.cpp b/src/gui/util/qgridlayoutengine.cpp
index 25c02cba51..07981e8388 100644
--- a/src/gui/util/qgridlayoutengine.cpp
+++ b/src/gui/util/qgridlayoutengine.cpp
@@ -13,10 +13,12 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
+#define LAYOUTITEMSIZE_MAX (1 << 24)
+
template<typename T>
static void insertOrRemoveItems(QList<T> &items, int index, int delta)
{
- int count = items.count();
+ int count = items.size();
if (index < count) {
if (delta > 0) {
items.insert(index, delta, T());
@@ -194,7 +196,8 @@ void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSiz
sumAvailable = targetSize - totalBox.q_minimumSize;
if (sumAvailable > 0.0) {
- qreal sumDesired = totalBox.q_preferredSize - totalBox.q_minimumSize;
+ const qreal totalBox_preferredSize = qMin(totalBox.q_preferredSize, qreal(LAYOUTITEMSIZE_MAX));
+ qreal sumDesired = totalBox_preferredSize - totalBox.q_minimumSize;
for (int i = 0; i < n; ++i) {
if (ignore.testBit(start + i)) {
@@ -203,7 +206,8 @@ void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSiz
}
const QGridLayoutBox &box = boxes.at(start + i);
- qreal desired = box.q_preferredSize - box.q_minimumSize;
+ const qreal box_preferredSize = qMin(box.q_preferredSize, qreal(LAYOUTITEMSIZE_MAX));
+ qreal desired = box_preferredSize - box.q_minimumSize;
factors[i] = growthFactorBelowPreferredSize(desired, sumAvailable, sumDesired);
sumFactors += factors[i];
}
@@ -716,7 +720,7 @@ void QGridLayoutItem::dump(int indent) const
if (q_alignment != 0)
qDebug("%*s Alignment: %x", indent, "", uint(q_alignment));
qDebug("%*s Horizontal size policy: %x Vertical size policy: %x",
- indent, "", sizePolicy(Qt::Horizontal), sizePolicy(Qt::Vertical));
+ indent, "", (unsigned int)sizePolicy(Qt::Horizontal), (unsigned int)sizePolicy(Qt::Vertical));
}
#endif
@@ -758,6 +762,8 @@ QGridLayoutEngine::QGridLayoutEngine(Qt::Alignment defaultAlignment, bool snapTo
m_visualDirection = Qt::LeftToRight;
m_defaultAlignment = defaultAlignment;
m_snapToPixelGrid = snapToPixelGrid;
+ m_uniformCellWidths = false;
+ m_uniformCellHeights = false;
invalidate();
}
@@ -773,7 +779,7 @@ int QGridLayoutEngine::columnCount(Qt::Orientation orientation) const
int QGridLayoutEngine::itemCount() const
{
- return q_items.count();
+ return q_items.size();
}
QGridLayoutItem *QGridLayoutEngine::itemAt(int index) const
@@ -818,7 +824,7 @@ void QGridLayoutEngine::setRowSpacing(int row, qreal spacing, Qt::Orientation or
Q_ASSERT(row >= 0);
QGridLayoutRowInfo &rowInfo = q_infos[orientation];
- if (row >= rowInfo.spacings.count())
+ if (row >= rowInfo.spacings.size())
rowInfo.spacings.resize(row + 1);
if (spacing >= 0)
rowInfo.spacings[row].setUserValue(spacing);
@@ -843,7 +849,7 @@ void QGridLayoutEngine::setRowStretchFactor(int row, int stretch, Qt::Orientatio
maybeExpandGrid(row, -1, orientation);
QGridLayoutRowInfo &rowInfo = q_infos[orientation];
- if (row >= rowInfo.stretches.count())
+ if (row >= rowInfo.stretches.size())
rowInfo.stretches.resize(row + 1);
rowInfo.stretches[row].setUserValue(stretch);
}
@@ -865,7 +871,7 @@ void QGridLayoutEngine::setRowSizeHint(Qt::SizeHint which, int row, qreal size,
maybeExpandGrid(row, -1, orientation);
QGridLayoutRowInfo &rowInfo = q_infos[orientation];
- if (row >= rowInfo.boxes.count())
+ if (row >= rowInfo.boxes.size())
rowInfo.boxes.resize(row + 1);
rowInfo.boxes[row].q_sizes(which) = size;
}
@@ -875,6 +881,34 @@ qreal QGridLayoutEngine::rowSizeHint(Qt::SizeHint which, int row, Qt::Orientatio
return q_infos[orientation].boxes.value(row).q_sizes(which);
}
+bool QGridLayoutEngine::uniformCellWidths() const
+{
+ return m_uniformCellWidths;
+}
+
+void QGridLayoutEngine::setUniformCellWidths(bool uniformCellWidths)
+{
+ if (m_uniformCellWidths == uniformCellWidths)
+ return;
+
+ m_uniformCellWidths = uniformCellWidths;
+ invalidate();
+}
+
+bool QGridLayoutEngine::uniformCellHeights() const
+{
+ return m_uniformCellHeights;
+}
+
+void QGridLayoutEngine::setUniformCellHeights(bool uniformCellHeights)
+{
+ if (m_uniformCellHeights == uniformCellHeights)
+ return;
+
+ m_uniformCellHeights = uniformCellHeights;
+ invalidate();
+}
+
void QGridLayoutEngine::setRowAlignment(int row, Qt::Alignment alignment,
Qt::Orientation orientation)
{
@@ -883,7 +917,7 @@ void QGridLayoutEngine::setRowAlignment(int row, Qt::Alignment alignment,
maybeExpandGrid(row, -1, orientation);
QGridLayoutRowInfo &rowInfo = q_infos[orientation];
- if (row >= rowInfo.alignments.count())
+ if (row >= rowInfo.alignments.size())
rowInfo.alignments.resize(row + 1);
rowInfo.alignments[row] = alignment;
}
@@ -930,8 +964,11 @@ void QGridLayoutEngine::insertItem(QGridLayoutItem *item, int index)
for (int i = item->firstRow(); i <= item->lastRow(); ++i) {
for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) {
- if (itemAt(i, j))
- qWarning("QGridLayoutEngine::addItem: Cell (%d, %d) already taken", i, j);
+ const auto existingItem = itemAt(i, j);
+ if (existingItem) {
+ qWarning("QGridLayoutEngine::addItem: Can't add %s at cell (%d, %d) because it's already taken by %s",
+ qPrintable(item->toString()), i, j, qPrintable(existingItem->toString()));
+ }
setItemAt(i, j, item);
}
}
@@ -992,7 +1029,7 @@ void QGridLayoutEngine::setGeometries(const QRectF &contentsGeometry, const QAbs
ensureGeometries(contentsGeometry.size(), styleInfo);
- for (int i = q_items.count() - 1; i >= 0; --i) {
+ for (int i = q_items.size() - 1; i >= 0; --i) {
QGridLayoutItem *item = q_items.at(i);
qreal x = q_xx.at(item->firstColumn());
@@ -1121,7 +1158,7 @@ void QGridLayoutEngine::transpose()
{
invalidate();
- for (int i = q_items.count() - 1; i >= 0; --i)
+ for (int i = q_items.size() - 1; i >= 0; --i)
q_items.at(i)->transpose();
q_defaultSpacings.transpose();
@@ -1145,7 +1182,7 @@ void QGridLayoutEngine::dump(int indent) const
{
qDebug("%*sEngine", indent, "");
- qDebug("%*s Items (%d)", indent, "", q_items.count());
+ qDebug("%*s Items (%lld)", indent, "", q_items.count());
int i;
for (i = 0; i < q_items.count(); ++i)
q_items.at(i)->dump(indent + 2);
@@ -1210,7 +1247,7 @@ void QGridLayoutEngine::maybeExpandGrid(int row, int column, Qt::Orientation ori
int newGridColumnCount = internalGridColumnCount();
int newGridSize = newGridRowCount * newGridColumnCount;
- if (newGridSize != q_grid.count()) {
+ if (newGridSize != q_grid.size()) {
q_grid.resize(newGridSize);
if (newGridColumnCount != oldGridColumnCount) {
@@ -1232,7 +1269,7 @@ void QGridLayoutEngine::regenerateGrid()
{
q_grid.fill(nullptr);
- for (int i = q_items.count() - 1; i >= 0; --i) {
+ for (int i = q_items.size() - 1; i >= 0; --i) {
QGridLayoutItem *item = q_items.at(i);
for (int j = item->firstRow(); j <= item->lastRow(); ++j) {
@@ -1265,7 +1302,7 @@ void QGridLayoutEngine::insertOrRemoveRows(int row, int delta, Qt::Orientation o
q_infos[orientation].insertOrRemoveRows(row, delta);
- for (int i = q_items.count() - 1; i >= 0; --i)
+ for (int i = q_items.size() - 1; i >= 0; --i)
q_items.at(i)->insertOrRemoveRows(row, delta, orientation);
q_grid.resize(internalGridRowCount() * internalGridColumnCount());
@@ -1406,7 +1443,7 @@ void QGridLayoutEngine::fillRowData(QGridLayoutRowData *rowData,
}
}
}
- if (row < rowInfo.boxes.count()) {
+ if (row < rowInfo.boxes.size()) {
QGridLayoutBox rowBoxInfo = rowInfo.boxes.at(row);
rowBoxInfo.normalize();
rowBox.q_minimumSize = qMax(rowBox.q_minimumSize, rowBoxInfo.q_minimumSize);
@@ -1499,6 +1536,27 @@ void QGridLayoutEngine::fillRowData(QGridLayoutRowData *rowData,
rowSpacing = qMax(windowMargin, rowSpacing);
}
}
+
+ if (rowData->boxes.size() > 1 &&
+ ((orientation == Qt::Horizontal && m_uniformCellWidths) ||
+ (orientation == Qt::Vertical && m_uniformCellHeights))) {
+ qreal averagePreferredSize = 0.;
+ qreal minimumMaximumSize = std::numeric_limits<qreal>::max();
+ qreal maximumMinimumSize = 0.;
+ for (const auto &box : rowData->boxes) {
+ averagePreferredSize += box.q_preferredSize;
+ minimumMaximumSize = qMin(minimumMaximumSize, box.q_maximumSize);
+ maximumMinimumSize = qMax(maximumMinimumSize, box.q_minimumSize);
+ }
+ averagePreferredSize /= rowData->boxes.size();
+ minimumMaximumSize = qMax(minimumMaximumSize, maximumMinimumSize);
+ averagePreferredSize = qBound(maximumMinimumSize, averagePreferredSize, minimumMaximumSize);
+ for (auto &box : rowData->boxes) {
+ box.q_preferredSize = averagePreferredSize;
+ box.q_minimumSize = maximumMinimumSize;
+ box.q_maximumSize = minimumMaximumSize;
+ }
+ }
}
void QGridLayoutEngine::ensureEffectiveFirstAndLastRows() const
@@ -1510,7 +1568,7 @@ void QGridLayoutEngine::ensureEffectiveFirstAndLastRows() const
q_cachedEffectiveFirstRows = {columnCount, rowCount};
q_cachedEffectiveLastRows = {-1, -1};
- for (int i = q_items.count() - 1; i >= 0; --i) {
+ for (int i = q_items.size() - 1; i >= 0; --i) {
const QGridLayoutItem *item = q_items.at(i);
for (Qt::Orientation o : {Qt::Horizontal, Qt::Vertical}) {
@@ -1556,7 +1614,7 @@ void QGridLayoutEngine::ensureColumnAndRowData(QGridLayoutRowData *rowData, QGri
bool QGridLayoutEngine::ensureDynamicConstraint() const
{
if (q_cachedConstraintOrientation == UnknownConstraint) {
- for (int i = q_items.count() - 1; i >= 0; --i) {
+ for (int i = q_items.size() - 1; i >= 0; --i) {
QGridLayoutItem *item = q_items.at(i);
if (item->hasDynamicConstraint()) {
Qt::Orientation itemConstraintOrientation = item->dynamicConstraintOrientation();
diff --git a/src/gui/util/qgridlayoutengine_p.h b/src/gui/util/qgridlayoutengine_p.h
index 8344d488c1..2f60cb99fd 100644
--- a/src/gui/util/qgridlayoutengine_p.h
+++ b/src/gui/util/qgridlayoutengine_p.h
@@ -24,6 +24,7 @@
#include <QtCore/qpair.h>
#include <QtCore/qsize.h>
#include <QtCore/qrect.h>
+#include <QtCore/qdebug.h>
#include <float.h>
#include "qlayoutpolicy_p.h"
@@ -283,6 +284,8 @@ public:
virtual QLayoutPolicy::ControlTypes controlTypes(LayoutSide side) const;
+ inline virtual QString toString() const { return QDebug::toString(this); }
+
QRectF geometryWithin(qreal x, qreal y, qreal width, qreal height, qreal rowDescent, Qt::Alignment align, bool snapToPixelGrid) const;
QGridLayoutBox box(Qt::Orientation orientation, bool snapToPixelGrid, qreal constraint = -1.0) const;
@@ -334,6 +337,12 @@ public:
qreal rowSizeHint(Qt::SizeHint which, int row,
Qt::Orientation orientation = Qt::Vertical) const;
+ bool uniformCellWidths() const;
+ void setUniformCellWidths(bool uniformCellWidths);
+
+ bool uniformCellHeights() const;
+ void setUniformCellHeights(bool uniformCellHeights);
+
void setRowAlignment(int row, Qt::Alignment alignment, Qt::Orientation orientation);
Qt::Alignment rowAlignment(int row, Qt::Orientation orientation) const;
@@ -415,6 +424,8 @@ private:
// Configuration
Qt::Alignment m_defaultAlignment;
unsigned m_snapToPixelGrid : 1;
+ unsigned m_uniformCellWidths : 1;
+ unsigned m_uniformCellHeights : 1;
// Lazily computed from the above user input
mutable QHVContainer<int> q_cachedEffectiveFirstRows;
diff --git a/src/gui/util/qktxhandler.cpp b/src/gui/util/qktxhandler.cpp
index 85e3428bf0..35f1df1185 100644
--- a/src/gui/util/qktxhandler.cpp
+++ b/src/gui/util/qktxhandler.cpp
@@ -41,7 +41,7 @@ struct KTXHeader {
quint32 bytesOfKeyValueData;
};
-static const quint32 headerSize = sizeof(KTXHeader);
+static constexpr quint32 qktxh_headerSize = sizeof(KTXHeader);
// Currently unused, declared for future reference
struct KTXKeyValuePairItem {
@@ -71,11 +71,24 @@ struct KTXMipmapLevel {
*/
};
-// Returns the nearest multiple of 'rounding' greater than or equal to 'value'
-constexpr quint32 withPadding(quint32 value, quint32 rounding)
+// Returns the nearest multiple of 4 greater than or equal to 'value'
+static const std::optional<quint32> nearestMultipleOf4(quint32 value)
{
- Q_ASSERT(rounding > 1);
- return value + (rounding - 1) - ((value + (rounding - 1)) % rounding);
+ constexpr quint32 rounding = 4;
+ quint32 result = 0;
+ if (qAddOverflow(value, rounding - 1, &result))
+ return std::nullopt;
+ result &= ~(rounding - 1);
+ return result;
+}
+
+// Returns a view with prechecked bounds
+static QByteArrayView safeView(QByteArrayView view, quint32 start, quint32 length)
+{
+ quint32 end = 0;
+ if (qAddOverflow(start, length, &end) || end > quint32(view.length()))
+ return {};
+ return view.sliced(start, length);
}
QKtxHandler::~QKtxHandler() = default;
@@ -83,8 +96,7 @@ QKtxHandler::~QKtxHandler() = default;
bool QKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block)
{
Q_UNUSED(suffix);
-
- return (qstrncmp(block.constData(), ktxIdentifier, KTX_IDENTIFIER_LENGTH) == 0);
+ return block.startsWith(ktxIdentifier);
}
QTextureFileData QKtxHandler::read()
@@ -93,55 +105,122 @@ QTextureFileData QKtxHandler::read()
return QTextureFileData();
const QByteArray buf = device()->readAll();
- const quint32 dataSize = quint32(buf.size());
- if (dataSize < headerSize || !canRead(QByteArray(), buf)) {
- qCDebug(lcQtGuiTextureIO, "Invalid KTX file %s", logName().constData());
+ if (static_cast<size_t>(buf.size()) > std::numeric_limits<quint32>::max()) {
+ qWarning(lcQtGuiTextureIO, "Too big KTX file %s", logName().constData());
+ return QTextureFileData();
+ }
+
+ if (!canRead(QByteArray(), buf)) {
+ qWarning(lcQtGuiTextureIO, "Invalid KTX file %s", logName().constData());
return QTextureFileData();
}
- const KTXHeader *header = reinterpret_cast<const KTXHeader *>(buf.data());
- if (!checkHeader(*header)) {
- qCDebug(lcQtGuiTextureIO, "Unsupported KTX file format in %s", logName().constData());
+ if (buf.size() < qsizetype(qktxh_headerSize)) {
+ qWarning(lcQtGuiTextureIO, "Invalid KTX header size in %s", logName().constData());
+ return QTextureFileData();
+ }
+
+ KTXHeader header;
+ memcpy(&header, buf.data(), qktxh_headerSize);
+ if (!checkHeader(header)) {
+ qWarning(lcQtGuiTextureIO, "Unsupported KTX file format in %s", logName().constData());
return QTextureFileData();
}
QTextureFileData texData;
texData.setData(buf);
- texData.setSize(QSize(decode(header->pixelWidth), decode(header->pixelHeight)));
- texData.setGLFormat(decode(header->glFormat));
- texData.setGLInternalFormat(decode(header->glInternalFormat));
- texData.setGLBaseInternalFormat(decode(header->glBaseInternalFormat));
+ texData.setSize(QSize(decode(header.pixelWidth), decode(header.pixelHeight)));
+ texData.setGLFormat(decode(header.glFormat));
+ texData.setGLInternalFormat(decode(header.glInternalFormat));
+ texData.setGLBaseInternalFormat(decode(header.glBaseInternalFormat));
- texData.setNumLevels(decode(header->numberOfMipmapLevels));
- texData.setNumFaces(decode(header->numberOfFaces));
+ texData.setNumLevels(decode(header.numberOfMipmapLevels));
+ texData.setNumFaces(decode(header.numberOfFaces));
+
+ const quint32 bytesOfKeyValueData = decode(header.bytesOfKeyValueData);
+ quint32 headerKeyValueSize;
+ if (qAddOverflow(qktxh_headerSize, bytesOfKeyValueData, &headerKeyValueSize)) {
+ qWarning(lcQtGuiTextureIO, "Overflow in size of key value data in header of KTX file %s",
+ logName().constData());
+ return QTextureFileData();
+ }
- const quint32 bytesOfKeyValueData = decode(header->bytesOfKeyValueData);
- if (headerSize + bytesOfKeyValueData < quint64(buf.length())) // oob check
- texData.setKeyValueMetadata(
- decodeKeyValues(QByteArrayView(buf.data() + headerSize, bytesOfKeyValueData)));
- quint32 offset = headerSize + bytesOfKeyValueData;
+ if (headerKeyValueSize >= quint32(buf.size())) {
+ qWarning(lcQtGuiTextureIO, "OOB request in KTX file %s", logName().constData());
+ return QTextureFileData();
+ }
+
+ // File contains key/values
+ if (bytesOfKeyValueData > 0) {
+ auto keyValueDataView = safeView(buf, qktxh_headerSize, bytesOfKeyValueData);
+ if (keyValueDataView.isEmpty()) {
+ qWarning(lcQtGuiTextureIO, "Invalid view in KTX file %s", logName().constData());
+ return QTextureFileData();
+ }
+
+ auto keyValues = decodeKeyValues(keyValueDataView);
+ if (!keyValues) {
+ qWarning(lcQtGuiTextureIO, "Could not parse key values in KTX file %s",
+ logName().constData());
+ return QTextureFileData();
+ }
+
+ texData.setKeyValueMetadata(*keyValues);
+ }
+
+ // Technically, any number of levels is allowed but if the value is bigger than
+ // what is possible in KTX V2 (and what makes sense) we return an error.
+ // maxLevels = log2(max(width, height, depth))
+ const int maxLevels = (sizeof(quint32) * 8)
+ - qCountLeadingZeroBits(std::max(
+ { header.pixelWidth, header.pixelHeight, header.pixelDepth }));
+
+ if (texData.numLevels() > maxLevels) {
+ qWarning(lcQtGuiTextureIO, "Too many levels in KTX file %s", logName().constData());
+ return QTextureFileData();
+ }
- constexpr int MAX_ITERATIONS = 32; // cap iterations in case of corrupt data
+ if (texData.numFaces() != 1 && texData.numFaces() != 6) {
+ qWarning(lcQtGuiTextureIO, "Invalid number of faces in KTX file %s", logName().constData());
+ return QTextureFileData();
+ }
- for (int level = 0; level < qMin(texData.numLevels(), MAX_ITERATIONS); level++) {
- if (offset + sizeof(quint32) > dataSize) // Corrupt file; avoid oob read
- break;
+ quint32 offset = headerKeyValueSize;
+ for (int level = 0; level < texData.numLevels(); level++) {
+ const auto imageSizeView = safeView(buf, offset, sizeof(quint32));
+ if (imageSizeView.isEmpty()) {
+ qWarning(lcQtGuiTextureIO, "OOB request in KTX file %s", logName().constData());
+ return QTextureFileData();
+ }
- const quint32 imageSize = decode(qFromUnaligned<quint32>(buf.data() + offset));
- offset += sizeof(quint32);
+ const quint32 imageSize = decode(qFromUnaligned<quint32>(imageSizeView.data()));
+ offset += sizeof(quint32); // overflow checked indirectly above
- for (int face = 0; face < qMin(texData.numFaces(), MAX_ITERATIONS); face++) {
+ for (int face = 0; face < texData.numFaces(); face++) {
texData.setDataOffset(offset, level, face);
texData.setDataLength(imageSize, level, face);
// Add image data and padding to offset
- offset += withPadding(imageSize, 4);
+ const auto padded = nearestMultipleOf4(imageSize);
+ if (!padded) {
+ qWarning(lcQtGuiTextureIO, "Overflow in KTX file %s", logName().constData());
+ return QTextureFileData();
+ }
+
+ quint32 offsetNext;
+ if (qAddOverflow(offset, *padded, &offsetNext)) {
+ qWarning(lcQtGuiTextureIO, "OOB request in KTX file %s", logName().constData());
+ return QTextureFileData();
+ }
+
+ offset = offsetNext;
}
}
if (!texData.isValid()) {
- qCDebug(lcQtGuiTextureIO, "Invalid values in header of KTX file %s", logName().constData());
+ qWarning(lcQtGuiTextureIO, "Invalid values in header of KTX file %s",
+ logName().constData());
return QTextureFileData();
}
@@ -187,33 +266,83 @@ bool QKtxHandler::checkHeader(const KTXHeader &header)
return is2D && (isCubeMap || isCompressedImage);
}
-QMap<QByteArray, QByteArray> QKtxHandler::decodeKeyValues(QByteArrayView view) const
+std::optional<QMap<QByteArray, QByteArray>> QKtxHandler::decodeKeyValues(QByteArrayView view) const
{
QMap<QByteArray, QByteArray> output;
quint32 offset = 0;
- while (offset < view.size() + sizeof(quint32)) {
+ while (offset < quint32(view.size())) {
+ const auto keyAndValueByteSizeView = safeView(view, offset, sizeof(quint32));
+ if (keyAndValueByteSizeView.isEmpty()) {
+ qWarning(lcQtGuiTextureIO, "Invalid view in KTX key-value");
+ return std::nullopt;
+ }
+
const quint32 keyAndValueByteSize =
- decode(qFromUnaligned<quint32>(view.constData() + offset));
- offset += sizeof(quint32);
+ decode(qFromUnaligned<quint32>(keyAndValueByteSizeView.data()));
- if (offset + keyAndValueByteSize > quint64(view.size()))
- break; // oob read
+ quint32 offsetKeyAndValueStart;
+ if (qAddOverflow(offset, quint32(sizeof(quint32)), &offsetKeyAndValueStart)) {
+ qWarning(lcQtGuiTextureIO, "Overflow in KTX key-value");
+ return std::nullopt;
+ }
+
+ quint32 offsetKeyAndValueEnd;
+ if (qAddOverflow(offsetKeyAndValueStart, keyAndValueByteSize, &offsetKeyAndValueEnd)) {
+ qWarning(lcQtGuiTextureIO, "Overflow in KTX key-value");
+ return std::nullopt;
+ }
+
+ const auto keyValueView = safeView(view, offsetKeyAndValueStart, keyAndValueByteSize);
+ if (keyValueView.isEmpty()) {
+ qWarning(lcQtGuiTextureIO, "Invalid view in KTX key-value");
+ return std::nullopt;
+ }
// 'key' is a UTF-8 string ending with a null terminator, 'value' is the rest.
// To separate the key and value we convert the complete data to utf-8 and find the first
// null terminator from the left, here we split the data into two.
- const auto str = QString::fromUtf8(view.constData() + offset, keyAndValueByteSize);
- const int idx = str.indexOf('\0'_L1);
- if (idx == -1)
- continue;
-
- const QByteArray key = str.left(idx).toUtf8();
- const size_t keySize = key.size() + 1; // Actual data size
- const QByteArray value = QByteArray::fromRawData(view.constData() + offset + keySize,
- keyAndValueByteSize - keySize);
-
- offset = withPadding(offset + keyAndValueByteSize, 4);
- output.insert(key, value);
+
+ const int idx = keyValueView.indexOf('\0');
+ if (idx == -1) {
+ qWarning(lcQtGuiTextureIO, "Invalid key in KTX key-value");
+ return std::nullopt;
+ }
+
+ const QByteArrayView keyView = safeView(view, offsetKeyAndValueStart, idx);
+ if (keyView.isEmpty()) {
+ qWarning(lcQtGuiTextureIO, "Overflow in KTX key-value");
+ return std::nullopt;
+ }
+
+ const quint32 keySize = idx + 1; // Actual data size
+
+ quint32 offsetValueStart;
+ if (qAddOverflow(offsetKeyAndValueStart, keySize, &offsetValueStart)) {
+ qWarning(lcQtGuiTextureIO, "Overflow in KTX key-value");
+ return std::nullopt;
+ }
+
+ quint32 valueSize;
+ if (qSubOverflow(keyAndValueByteSize, keySize, &valueSize)) {
+ qWarning(lcQtGuiTextureIO, "Underflow in KTX key-value");
+ return std::nullopt;
+ }
+
+ const QByteArrayView valueView = safeView(view, offsetValueStart, valueSize);
+ if (valueView.isEmpty()) {
+ qWarning(lcQtGuiTextureIO, "Invalid view in KTX key-value");
+ return std::nullopt;
+ }
+
+ output.insert(keyView.toByteArray(), valueView.toByteArray());
+
+ const auto offsetNext = nearestMultipleOf4(offsetKeyAndValueEnd);
+ if (!offsetNext) {
+ qWarning(lcQtGuiTextureIO, "Overflow in KTX key-value");
+ return std::nullopt;
+ }
+
+ offset = *offsetNext;
}
return output;
diff --git a/src/gui/util/qktxhandler_p.h b/src/gui/util/qktxhandler_p.h
index 7d54b20922..3a0b8fcf7e 100644
--- a/src/gui/util/qktxhandler_p.h
+++ b/src/gui/util/qktxhandler_p.h
@@ -17,6 +17,8 @@
#include "qtexturefilehandler_p.h"
+#include <optional>
+
QT_BEGIN_NAMESPACE
struct KTXHeader;
@@ -33,7 +35,7 @@ public:
private:
bool checkHeader(const KTXHeader &header);
- QMap<QByteArray, QByteArray> decodeKeyValues(QByteArrayView view) const;
+ std::optional<QMap<QByteArray, QByteArray>> decodeKeyValues(QByteArrayView view) const;
quint32 decode(quint32 val) const;
bool inverseEndian = false;
diff --git a/src/gui/util/qpkmhandler.cpp b/src/gui/util/qpkmhandler.cpp
index 7b0fb020df..d84ce2ce7f 100644
--- a/src/gui/util/qpkmhandler.cpp
+++ b/src/gui/util/qpkmhandler.cpp
@@ -13,7 +13,7 @@
QT_BEGIN_NAMESPACE
-static const int headerSize = 16;
+static const int qpkmh_headerSize = 16;
struct PkmType
{
@@ -46,7 +46,7 @@ QTextureFileData QPkmHandler::read()
return texData;
QByteArray fileData = device()->readAll();
- if (fileData.size() < headerSize || !canRead(QByteArray(), fileData)) {
+ if (fileData.size() < qpkmh_headerSize || !canRead(QByteArray(), fileData)) {
qCDebug(lcQtGuiTextureIO, "Invalid PKM file %s", logName().constData());
return QTextureFileData();
}
@@ -75,7 +75,7 @@ QTextureFileData QPkmHandler::read()
QSize texSize(qFromBigEndian<quint16>(rawData + 12), qFromBigEndian<quint16>(rawData + 14));
texData.setSize(texSize);
- texData.setDataOffset(headerSize);
+ texData.setDataOffset(qpkmh_headerSize);
if (!texData.isValid()) {
qCDebug(lcQtGuiTextureIO, "Invalid values in header of PKM file %s", logName().constData());
diff --git a/src/gui/util/qtexturefiledata.cpp b/src/gui/util/qtexturefiledata.cpp
index ddd99b0ffc..e1fa900b84 100644
--- a/src/gui/util/qtexturefiledata.cpp
+++ b/src/gui/util/qtexturefiledata.cpp
@@ -117,8 +117,8 @@ bool QTextureFileData::isValid() const
if (d->data.isEmpty() || d->size.isEmpty() || (!d->format && !d->internalFormat))
return false;
- const int numFacesOffset = d->offsets.length();
- const int numFacesLength = d->lengths.length();
+ const int numFacesOffset = d->offsets.size();
+ const int numFacesLength = d->lengths.size();
if (numFacesOffset == 0 || numFacesLength == 0 || d->numFaces != numFacesOffset
|| d->numFaces != numFacesLength)
return false;
diff --git a/src/gui/util/qundostack.cpp b/src/gui/util/qundostack.cpp
index 94d97216c4..403833d421 100644
--- a/src/gui/util/qundostack.cpp
+++ b/src/gui/util/qundostack.cpp
@@ -286,7 +286,7 @@ void QUndoCommand::setText(const QString &text)
int QUndoCommand::childCount() const
{
- return d->child_list.count();
+ return d->child_list.size();
}
/*!
@@ -299,7 +299,7 @@ int QUndoCommand::childCount() const
const QUndoCommand *QUndoCommand::child(int index) const
{
- if (index < 0 || index >= d->child_list.count())
+ if (index < 0 || index >= d->child_list.size())
return nullptr;
return d->child_list.at(index);
}
@@ -444,10 +444,10 @@ void QUndoStackPrivate::setIndex(int idx, bool clean)
bool QUndoStackPrivate::checkUndoLimit()
{
- if (undo_limit <= 0 || !macro_stack.isEmpty() || undo_limit >= command_list.count())
+ if (undo_limit <= 0 || !macro_stack.isEmpty() || undo_limit >= command_list.size())
return false;
- int del_count = command_list.count() - undo_limit;
+ int del_count = command_list.size() - undo_limit;
for (int i = 0; i < del_count; ++i)
delete command_list.takeFirst();
@@ -1142,7 +1142,7 @@ void QUndoStack::beginMacro(const QString &text)
}
d->macro_stack.append(cmd);
- if (d->macro_stack.count() == 1) {
+ if (d->macro_stack.size() == 1) {
emit canUndoChanged(false);
emit undoTextChanged(QString());
emit canRedoChanged(false);
@@ -1191,7 +1191,7 @@ const QUndoCommand *QUndoStack::command(int index) const
{
Q_D(const QUndoStack);
- if (index < 0 || index >= d->command_list.count())
+ if (index < 0 || index >= d->command_list.size())
return nullptr;
return d->command_list.at(index);
}
diff --git a/src/gui/util/qvalidator.cpp b/src/gui/util/qvalidator.cpp
index f71a66c98c..2a81006657 100644
--- a/src/gui/util/qvalidator.cpp
+++ b/src/gui/util/qvalidator.cpp
@@ -331,12 +331,12 @@ QIntValidator::~QIntValidator()
or is a prefix of an integer in the valid range, returns \l Intermediate.
Otherwise, returns \l Invalid.
- If the valid range consists of just positive integers (e.g., 32 to 100)
- and \a input is a negative integer, then Invalid is returned. (On the other
- hand, if the range consists of negative integers (e.g., -100 to -32) and
- \a input is a positive integer, then Intermediate is returned, because
- the user might be just about to type the minus (especially for right-to-left
- languages).
+ If the valid range consists of just positive integers (e.g., 32 to 100) and
+ \a input is a negative integer, then Invalid is returned. (On the other
+ hand, if the range consists of negative integers (e.g., -100 to -32) and \a
+ input is a positive integer without leading plus sign, then Intermediate is
+ returned, because the user might be just about to type the minus (especially
+ for right-to-left languages).
Similarly, if the valid range is between 46 and 53, then 41 and 59 will be
evaluated as \l Intermediate, as otherwise the user wouldn't be able to
@@ -362,34 +362,45 @@ static qlonglong pow10(int exp)
return result;
}
-QValidator::State QIntValidator::validate(QString & input, int&) const
+template <typename T> static inline
+std::optional<QValidator::State> initialResultCheck(T min, T max, const ParsingResult &result)
{
- QByteArray buff;
- if (!locale().d->m_data->validateChars(input, QLocaleData::IntegerMode, &buff, -1,
- locale().numberOptions())) {
- return Invalid;
- }
+ if (result.state == ParsingResult::Invalid)
+ return QValidator::Invalid;
+ const CharBuff &buff = result.buff;
if (buff.isEmpty())
- return Intermediate;
+ return QValidator::Intermediate;
- const bool startsWithMinus(buff[0] == '-');
- if (b >= 0 && startsWithMinus)
- return Invalid;
+ char ch = buff[0];
+ const bool signConflicts = (min >= 0 && ch == '-') || (max < 0 && ch == '+');
+ if (signConflicts)
+ return QValidator::Invalid;
- const bool startsWithPlus(buff[0] == '+');
- if (t < 0 && startsWithPlus)
- return Invalid;
+ if (result.state == ParsingResult::Intermediate)
+ return QValidator::Intermediate;
- if (buff.size() == 1 && (startsWithPlus || startsWithMinus))
- return Intermediate;
+ return std::nullopt;
+}
- bool ok;
- qlonglong entered = QLocaleData::bytearrayToLongLong(buff, 10, &ok);
- if (!ok)
+QValidator::State QIntValidator::validate(QString & input, int&) const
+{
+ ParsingResult result =
+ locale().d->m_data->validateChars(input, QLocaleData::IntegerMode, -1,
+ locale().numberOptions());
+
+ std::optional<State> opt = initialResultCheck(b, t, result);
+ if (opt)
+ return *opt;
+
+ const CharBuff &buff = result.buff;
+ QSimpleParsedNumber r = QLocaleData::bytearrayToLongLong(buff, 10);
+ if (!r.ok())
return Invalid;
+ qint64 entered = r.result;
if (entered >= b && entered <= t) {
+ bool ok = false;
locale().toInt(input, &ok);
return ok ? Acceptable : Intermediate;
}
@@ -401,7 +412,7 @@ QValidator::State QIntValidator::validate(QString & input, int&) const
// of a number of digits equal to or less than the max value as intermediate.
int buffLength = buff.size();
- if (startsWithPlus)
+ if (buff[0] == '+')
buffLength--;
const int tLength = t != 0 ? static_cast<int>(std::log10(qAbs(t))) + 1 : 1;
@@ -414,15 +425,15 @@ QValidator::State QIntValidator::validate(QString & input, int&) const
/*! \reimp */
void QIntValidator::fixup(QString &input) const
{
- QByteArray buff;
- if (!locale().d->m_data->validateChars(input, QLocaleData::IntegerMode, &buff, -1,
- locale().numberOptions())) {
+ auto [parseState, buff] =
+ locale().d->m_data->validateChars(input, QLocaleData::IntegerMode, -1,
+ locale().numberOptions());
+ if (parseState == ParsingResult::Invalid)
return;
- }
- bool ok;
- qlonglong entered = QLocaleData::bytearrayToLongLong(buff, 10, &ok);
- if (ok)
- input = locale().toString(entered);
+
+ QSimpleParsedNumber r = QLocaleData::bytearrayToLongLong(buff, 10);
+ if (r.ok())
+ input = locale().toString(r.result);
}
/*!
@@ -531,10 +542,11 @@ public:
in the German locale, "1,234" will be accepted as the fractional number
1.234. In Arabic locales, QDoubleValidator will accept Arabic digits.
- \note The QLocale::NumberOptions set on the locale() also affect the
- way the number is interpreted. For example, since QLocale::RejectGroupSeparator
- is not set by default, the validator will accept group separators. It is thus
- recommended to use QLocale::toDouble() to obtain the numeric value.
+ \note The QLocale::NumberOptions set on the locale() also affect the way the
+ number is interpreted. For example, since QLocale::RejectGroupSeparator is
+ not set by default (except on the \c "C" locale), the validator will accept
+ group separators. If the string passes validation, pass it to
+ locale().toDouble() to obtain its numeric value.
\sa QIntValidator, QRegularExpressionValidator, QLocale::toDouble(), {Line Edits Example}
*/
@@ -544,10 +556,23 @@ public:
\since 4.3
This enum defines the allowed notations for entering a double.
- \value StandardNotation The string is written as a standard number
- (i.e. 0.015).
- \value ScientificNotation The string is written in scientific
- form. It may have an exponent part(i.e. 1.5E-2).
+ \value StandardNotation The string is written in the standard format, a
+ whole number part optionally followed by a separator
+ and fractional part, for example \c{"0.015"}.
+
+ \value ScientificNotation The string is written in scientific form, which
+ optionally appends an exponent part to the
+ standard format, for example \c{"1.5E-2"}.
+
+ The whole number part may, as usual, include a sign. This, along with the
+ separators for fractional part, exponent and any digit-grouping, depend on
+ locale. QDoubleValidator doesn't check the placement (which would also
+ depend on locale) of any digit-grouping separators it finds, but it will
+ reject input that contains them if \l QLocale::RejectGroupSeparator is set
+ in \c locale().numberOptions().
+
+ \sa QLocale::numberOptions(), QLocale::decimalPoint(),
+ QLocale::exponential(), QLocale::negativeSign()
*/
/*!
@@ -589,14 +614,14 @@ QDoubleValidator::~QDoubleValidator()
/*!
\fn QValidator::State QDoubleValidator::validate(QString &input, int &pos) const
- Returns \l Acceptable if the string \a input contains a double
- that is within the valid range and is in the correct format.
+ Returns \l Acceptable if the string \a input is in the correct format and
+ contains a double within the valid range.
- Returns \l Intermediate if \a input contains a double that is
- outside the range or is in the wrong format; e.g. is empty.
+ Returns \l Intermediate if \a input is in the wrong format or contains a
+ double outside the range.
- Returns \l Invalid if the \a input is not a double or with too many
- digits after the decimal point.
+ Returns \l Invalid if the \a input doesn't represent a double or has too
+ many digits after the decimal point.
Note: If the valid range consists of just positive doubles (e.g. 0.0 to 100.0)
and \a input is a negative double then \l Invalid is returned. If notation()
@@ -632,19 +657,12 @@ QValidator::State QDoubleValidator::validate(QString & input, int &) const
QValidator::State QDoubleValidatorPrivate::validateWithLocale(QString &input, QLocaleData::NumberMode numMode, const QLocale &locale) const
{
Q_Q(const QDoubleValidator);
- QByteArray buff;
- if (!locale.d->m_data->validateChars(input, numMode, &buff, q->dec, locale.numberOptions())) {
- return QValidator::Invalid;
- }
-
- if (buff.isEmpty())
- return QValidator::Intermediate;
-
- if (q->b >= 0 && buff.startsWith('-'))
- return QValidator::Invalid;
+ ParsingResult result =
+ locale.d->m_data->validateChars(input, numMode, q->dec, locale.numberOptions());
- if (q->t < 0 && buff.startsWith('+'))
- return QValidator::Invalid;
+ std::optional<QValidator::State> opt = initialResultCheck(q->b, q->t, result);
+ if (opt)
+ return *opt;
bool ok = false;
double i = locale.toDouble(input, &ok); // returns 0.0 if !ok
@@ -658,7 +676,11 @@ QValidator::State QDoubleValidatorPrivate::validateWithLocale(QString &input, QL
if (notation == QDoubleValidator::StandardNotation) {
double max = qMax(qAbs(q->b), qAbs(q->t));
qlonglong v;
- if (convertDoubleTo(max, &v)) {
+ // Need a whole number to pass to convertDoubleTo() or it fails. Use
+ // floor, as max is positive so this has the same number of digits
+ // before the decimal point, where qCeil() might take us up to a power
+ // of ten, adding a digit.
+ if (convertDoubleTo(qFloor(max), &v)) {
qlonglong n = pow10(numDigits(v));
// In order to get the highest possible number in the intermediate
// range we need to get 10 to the power of the number of digits
@@ -719,15 +741,16 @@ void QDoubleValidatorPrivate::fixupWithLocale(QString &input, QLocaleData::Numbe
const QLocale &locale) const
{
Q_Q(const QDoubleValidator);
- QByteArray buff;
// Passing -1 as the number of decimals, because fixup() exists to improve
// an Intermediate value, if it can.
- if (!locale.d->m_data->validateChars(input, numMode, &buff, -1, locale.numberOptions()))
+ auto [parseState, buff] =
+ locale.d->m_data->validateChars(input, numMode, -1, locale.numberOptions());
+ if (parseState == ParsingResult::Invalid)
return;
- // buff now contains data in C locale.
+ // buff contains data in C locale.
bool ok = false;
- const double entered = buff.toDouble(&ok);
+ const double entered = QByteArrayView(buff).toDouble(&ok);
if (ok) {
// Here we need to adjust the output format accordingly
char mode;
@@ -751,7 +774,7 @@ void QDoubleValidatorPrivate::fixupWithLocale(QString &input, QLocaleData::Numbe
if (eIndex < 0)
eIndex = buff.size();
precision = eIndex - (buff.contains('.') ? 1 : 0)
- - (buff.startsWith('-') || buff.startsWith('+') ? 1 : 0);
+ - (buff[0] == '-' || buff[0] == '+' ? 1 : 0);
}
// Use q->dec to limit the number of decimals, because we want the
// fixup() result to pass validate().
diff --git a/src/gui/vulkan/generated_header.txt b/src/gui/vulkan/licenseheader.h.in
index 3b6a0ea72a..3b6a0ea72a 100644
--- a/src/gui/vulkan/generated_header.txt
+++ b/src/gui/vulkan/licenseheader.h.in
diff --git a/src/gui/vulkan/qbasicvulkanplatforminstance.cpp b/src/gui/vulkan/qbasicvulkanplatforminstance.cpp
index 6825da8069..bbe9f9e1cb 100644
--- a/src/gui/vulkan/qbasicvulkanplatforminstance.cpp
+++ b/src/gui/vulkan/qbasicvulkanplatforminstance.cpp
@@ -26,11 +26,6 @@ Q_LOGGING_CATEGORY(lcPlatVk, "qt.vulkan")
*/
QBasicPlatformVulkanInstance::QBasicPlatformVulkanInstance()
- : m_vkInst(VK_NULL_HANDLE),
- m_vkGetInstanceProcAddr(nullptr),
- m_ownsVkInst(false),
- m_errorCode(VK_SUCCESS),
- m_debugCallback(VK_NULL_HANDLE)
{
}
@@ -39,8 +34,10 @@ QBasicPlatformVulkanInstance::~QBasicPlatformVulkanInstance()
if (!m_vkInst)
return;
- if (m_debugCallback && m_vkDestroyDebugReportCallbackEXT)
- m_vkDestroyDebugReportCallbackEXT(m_vkInst, m_debugCallback, nullptr);
+#ifdef VK_EXT_debug_utils
+ if (m_debugMessenger)
+ m_vkDestroyDebugUtilsMessengerEXT(m_vkInst, m_debugMessenger, nullptr);
+#endif
if (m_ownsVkInst)
m_vkDestroyInstance(m_vkInst, nullptr);
@@ -150,7 +147,7 @@ void QBasicPlatformVulkanInstance::init(QLibrary *lib)
QList<VkLayerProperties> layerProps(layerCount);
m_vkEnumerateInstanceLayerProperties(&layerCount, layerProps.data());
m_supportedLayers.reserve(layerCount);
- for (const VkLayerProperties &p : qAsConst(layerProps)) {
+ for (const VkLayerProperties &p : std::as_const(layerProps)) {
QVulkanLayer layer;
layer.name = p.layerName;
layer.version = p.implementationVersion;
@@ -169,7 +166,7 @@ void QBasicPlatformVulkanInstance::init(QLibrary *lib)
QList<VkExtensionProperties> extProps(extCount);
m_vkEnumerateInstanceExtensionProperties(nullptr, &extCount, extProps.data());
m_supportedExtensions.reserve(extCount);
- for (const VkExtensionProperties &p : qAsConst(extProps)) {
+ for (const VkExtensionProperties &p : std::as_const(extProps)) {
QVulkanExtension ext;
ext.name = p.extensionName;
ext.version = p.specVersion;
@@ -208,8 +205,7 @@ void QBasicPlatformVulkanInstance::initInstance(QVulkanInstance *instance, const
m_enabledExtensions = instance->extensions();
if (!m_vkInst) {
- VkApplicationInfo appInfo;
- memset(&appInfo, 0, sizeof(appInfo));
+ VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
QByteArray appName = QCoreApplication::applicationName().toUtf8();
appInfo.pApplicationName = appName.constData();
@@ -220,10 +216,11 @@ void QBasicPlatformVulkanInstance::initInstance(QVulkanInstance *instance, const
apiVersion.microVersion());
}
- if (!flags.testFlag(QVulkanInstance::NoDebugOutputRedirect))
- m_enabledExtensions.append("VK_EXT_debug_report");
-
m_enabledExtensions.append("VK_KHR_surface");
+ if (!flags.testFlag(QVulkanInstance::NoPortabilityDrivers))
+ m_enabledExtensions.append("VK_KHR_portability_enumeration");
+ if (!flags.testFlag(QVulkanInstance::NoDebugOutputRedirect))
+ m_enabledExtensions.append("VK_EXT_debug_utils");
for (const QByteArray &ext : extraExts)
m_enabledExtensions.append(ext);
@@ -246,37 +243,43 @@ void QBasicPlatformVulkanInstance::initInstance(QVulkanInstance *instance, const
// No clever stuff with QSet and friends: the order for layers matters
// and the user-provided order must be kept.
- for (int i = 0; i < m_enabledLayers.count(); ++i) {
+ for (int i = 0; i < m_enabledLayers.size(); ++i) {
const QByteArray &layerName(m_enabledLayers[i]);
if (!m_supportedLayers.contains(layerName))
m_enabledLayers.removeAt(i--);
}
qDebug(lcPlatVk) << "Enabling Vulkan instance layers:" << m_enabledLayers;
- for (int i = 0; i < m_enabledExtensions.count(); ++i) {
+ for (int i = 0; i < m_enabledExtensions.size(); ++i) {
const QByteArray &extName(m_enabledExtensions[i]);
if (!m_supportedExtensions.contains(extName))
m_enabledExtensions.removeAt(i--);
}
qDebug(lcPlatVk) << "Enabling Vulkan instance extensions:" << m_enabledExtensions;
- VkInstanceCreateInfo instInfo;
- memset(&instInfo, 0, sizeof(instInfo));
+ VkInstanceCreateInfo instInfo = {};
instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instInfo.pApplicationInfo = &appInfo;
+ if (!flags.testFlag(QVulkanInstance::NoPortabilityDrivers)) {
+ // With old Vulkan SDKs setting a non-zero flags gives a validation error.
+ // Whereas from 1.3.216 on the portability bit is required for MoltenVK to function.
+ // Hence the version check.
+ if (m_supportedApiVersion >= QVersionNumber(1, 3, 216))
+ instInfo.flags |= 0x00000001; // VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR
+ }
QList<const char *> layerNameVec;
- for (const QByteArray &ba : qAsConst(m_enabledLayers))
+ for (const QByteArray &ba : std::as_const(m_enabledLayers))
layerNameVec.append(ba.constData());
if (!layerNameVec.isEmpty()) {
- instInfo.enabledLayerCount = layerNameVec.count();
+ instInfo.enabledLayerCount = layerNameVec.size();
instInfo.ppEnabledLayerNames = layerNameVec.constData();
}
QList<const char *> extNameVec;
- for (const QByteArray &ba : qAsConst(m_enabledExtensions))
+ for (const QByteArray &ba : std::as_const(m_enabledExtensions))
extNameVec.append(ba.constData());
if (!extNameVec.isEmpty()) {
- instInfo.enabledExtensionCount = extNameVec.count();
+ instInfo.enabledExtensionCount = extNameVec.size();
instInfo.ppEnabledExtensionNames = extNameVec.constData();
}
@@ -365,55 +368,93 @@ void QBasicPlatformVulkanInstance::setDebugFilters(const QList<QVulkanInstance::
m_debugFilters = filters;
}
+void QBasicPlatformVulkanInstance::setDebugUtilsFilters(const QList<QVulkanInstance::DebugUtilsFilter> &filters)
+{
+ m_debugUtilsFilters = filters;
+}
+
void QBasicPlatformVulkanInstance::destroySurface(VkSurfaceKHR surface) const
{
if (m_destroySurface && surface)
m_destroySurface(m_vkInst, surface, nullptr);
}
-static VKAPI_ATTR VkBool32 VKAPI_CALL defaultDebugCallbackFunc(VkDebugReportFlagsEXT flags,
- VkDebugReportObjectTypeEXT objectType,
- uint64_t object,
- size_t location,
- int32_t messageCode,
- const char *pLayerPrefix,
- const char *pMessage,
+#ifdef VK_EXT_debug_utils
+static VKAPI_ATTR VkBool32 VKAPI_CALL defaultDebugCallbackFunc(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
+ VkDebugUtilsMessageTypeFlagsEXT messageType,
+ const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
void *pUserData)
{
QBasicPlatformVulkanInstance *self = static_cast<QBasicPlatformVulkanInstance *>(pUserData);
+
+ // legacy filters
for (QVulkanInstance::DebugFilter filter : *self->debugFilters()) {
- if (filter(flags, objectType, object, location, messageCode, pLayerPrefix, pMessage))
+ // As per docs in qvulkaninstance.cpp we pass object, messageCode,
+ // pMessage to the callback with the legacy signature.
+ uint64_t object = 0;
+ if (pCallbackData->objectCount > 0)
+ object = pCallbackData->pObjects[0].objectHandle;
+ if (filter(0, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, object, 0,
+ pCallbackData->messageIdNumber, "", pCallbackData->pMessage))
+ {
+ return VK_FALSE;
+ }
+ }
+
+ // filters with new signature
+ for (QVulkanInstance::DebugUtilsFilter filter : *self->debugUtilsFilters()) {
+ QVulkanInstance::DebugMessageSeverityFlags severity;
+ if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT)
+ severity |= QVulkanInstance::VerboseSeverity;
+ if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)
+ severity |= QVulkanInstance::InfoSeverity;
+ if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
+ severity |= QVulkanInstance::WarningSeverity;
+ if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
+ severity |= QVulkanInstance::ErrorSeverity;
+ QVulkanInstance::DebugMessageTypeFlags type;
+ if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT)
+ type |= QVulkanInstance::GeneralMessage;
+ if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)
+ type |= QVulkanInstance::ValidationMessage;
+ if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)
+ type |= QVulkanInstance::PerformanceMessage;
+ if (filter(severity, type, pCallbackData))
return VK_FALSE;
}
// not categorized, just route to plain old qDebug
- qDebug("vkDebug: %s: %d: %s", pLayerPrefix, messageCode, pMessage);
+ qDebug("vkDebug: %s", pCallbackData->pMessage);
return VK_FALSE;
}
+#endif
void QBasicPlatformVulkanInstance::setupDebugOutput()
{
- if (!m_enabledExtensions.contains("VK_EXT_debug_report"))
+#ifdef VK_EXT_debug_utils
+ if (!m_enabledExtensions.contains("VK_EXT_debug_utils"))
return;
- PFN_vkCreateDebugReportCallbackEXT createDebugReportCallback = reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(
- m_vkGetInstanceProcAddr(m_vkInst, "vkCreateDebugReportCallbackEXT"));
- m_vkDestroyDebugReportCallbackEXT = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(
- m_vkGetInstanceProcAddr(m_vkInst, "vkDestroyDebugReportCallbackEXT"));
-
- VkDebugReportCallbackCreateInfoEXT dbgCallbackInfo;
- memset(&dbgCallbackInfo, 0, sizeof(dbgCallbackInfo));
- dbgCallbackInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
- dbgCallbackInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT
- | VK_DEBUG_REPORT_WARNING_BIT_EXT
- | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
- dbgCallbackInfo.pfnCallback = defaultDebugCallbackFunc;
- dbgCallbackInfo.pUserData = this;
-
- VkResult err = createDebugReportCallback(m_vkInst, &dbgCallbackInfo, nullptr, &m_debugCallback);
+ PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(
+ m_vkGetInstanceProcAddr(m_vkInst, "vkCreateDebugUtilsMessengerEXT"));
+
+ m_vkDestroyDebugUtilsMessengerEXT = reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>(
+ m_vkGetInstanceProcAddr(m_vkInst, "vkDestroyDebugUtilsMessengerEXT"));
+
+ VkDebugUtilsMessengerCreateInfoEXT messengerInfo = {};
+ messengerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
+ messengerInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
+ | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
+ messengerInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
+ | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
+ | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
+ messengerInfo.pfnUserCallback = defaultDebugCallbackFunc;
+ messengerInfo.pUserData = this;
+ VkResult err = vkCreateDebugUtilsMessengerEXT(m_vkInst, &messengerInfo, nullptr, &m_debugMessenger);
if (err != VK_SUCCESS)
qWarning("Failed to create debug report callback: %d", err);
+#endif
}
QT_END_NAMESPACE
diff --git a/src/gui/vulkan/qbasicvulkanplatforminstance_p.h b/src/gui/vulkan/qbasicvulkanplatforminstance_p.h
index 70e9d65ef3..dfa5003f16 100644
--- a/src/gui/vulkan/qbasicvulkanplatforminstance_p.h
+++ b/src/gui/vulkan/qbasicvulkanplatforminstance_p.h
@@ -40,17 +40,19 @@ public:
PFN_vkVoidFunction getInstanceProcAddr(const char *name) override;
bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) override;
void setDebugFilters(const QList<QVulkanInstance::DebugFilter> &filters) override;
+ void setDebugUtilsFilters(const QList<QVulkanInstance::DebugUtilsFilter> &filters) override;
void destroySurface(VkSurfaceKHR surface) const;
const QList<QVulkanInstance::DebugFilter> *debugFilters() const { return &m_debugFilters; }
+ const QList<QVulkanInstance::DebugUtilsFilter> *debugUtilsFilters() const { return &m_debugUtilsFilters; }
protected:
void loadVulkanLibrary(const QString &defaultLibraryName, int defaultLibraryVersion = -1);
void init(QLibrary *lib);
void initInstance(QVulkanInstance *instance, const QByteArrayList &extraExts);
- VkInstance m_vkInst;
- PFN_vkGetInstanceProcAddr m_vkGetInstanceProcAddr;
+ VkInstance m_vkInst = VK_NULL_HANDLE;
+ PFN_vkGetInstanceProcAddr m_vkGetInstanceProcAddr = nullptr;
PFN_vkGetPhysicalDeviceSurfaceSupportKHR m_getPhysDevSurfaceSupport;
PFN_vkDestroySurfaceKHR m_destroySurface;
@@ -59,8 +61,8 @@ private:
std::unique_ptr<QLibrary> m_vulkanLib;
- bool m_ownsVkInst;
- VkResult m_errorCode;
+ bool m_ownsVkInst = false;
+ VkResult m_errorCode = VK_SUCCESS;
QVulkanInfoVector<QVulkanLayer> m_supportedLayers;
QVulkanInfoVector<QVulkanExtension> m_supportedExtensions;
QVersionNumber m_supportedApiVersion;
@@ -73,9 +75,12 @@ private:
PFN_vkDestroyInstance m_vkDestroyInstance;
- VkDebugReportCallbackEXT m_debugCallback;
- PFN_vkDestroyDebugReportCallbackEXT m_vkDestroyDebugReportCallbackEXT;
+#ifdef VK_EXT_debug_utils
+ VkDebugUtilsMessengerEXT m_debugMessenger = VK_NULL_HANDLE;
+ PFN_vkDestroyDebugUtilsMessengerEXT m_vkDestroyDebugUtilsMessengerEXT;
+#endif
QList<QVulkanInstance::DebugFilter> m_debugFilters;
+ QList<QVulkanInstance::DebugUtilsFilter> m_debugUtilsFilters;
};
QT_END_NAMESPACE
diff --git a/src/gui/vulkan/qplatformvulkaninstance.cpp b/src/gui/vulkan/qplatformvulkaninstance.cpp
index 0349358993..2685a5c6f8 100644
--- a/src/gui/vulkan/qplatformvulkaninstance.cpp
+++ b/src/gui/vulkan/qplatformvulkaninstance.cpp
@@ -59,4 +59,20 @@ void QPlatformVulkanInstance::setDebugFilters(const QList<QVulkanInstance::Debug
Q_UNUSED(filters);
}
+void QPlatformVulkanInstance::setDebugUtilsFilters(const QList<QVulkanInstance::DebugUtilsFilter> &filters)
+{
+ Q_UNUSED(filters);
+}
+
+void QPlatformVulkanInstance::beginFrame(QWindow *window)
+{
+ Q_UNUSED(window);
+}
+
+void QPlatformVulkanInstance::endFrame(QWindow *window)
+{
+ Q_UNUSED(window);
+}
+
+
QT_END_NAMESPACE
diff --git a/src/gui/vulkan/qplatformvulkaninstance.h b/src/gui/vulkan/qplatformvulkaninstance.h
index 2bb89f1018..d34cb77d1b 100644
--- a/src/gui/vulkan/qplatformvulkaninstance.h
+++ b/src/gui/vulkan/qplatformvulkaninstance.h
@@ -15,7 +15,7 @@
#include <QtGui/qtguiglobal.h>
-#if QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(vulkan) || defined(Q_QDOC)
#include <qvulkaninstance.h>
@@ -45,6 +45,9 @@ public:
virtual void presentAboutToBeQueued(QWindow *window);
virtual void presentQueued(QWindow *window);
virtual void setDebugFilters(const QList<QVulkanInstance::DebugFilter> &filters);
+ virtual void setDebugUtilsFilters(const QList<QVulkanInstance::DebugUtilsFilter> &filters);
+ virtual void beginFrame(QWindow *window);
+ virtual void endFrame(QWindow *window);
private:
QScopedPointer<QPlatformVulkanInstancePrivate> d_ptr;
@@ -55,7 +58,7 @@ QT_END_NAMESPACE
#endif // QT_CONFIG(vulkan)
-#if defined(Q_CLANG_QDOC)
+#if defined(Q_QDOC)
/*
The following include file did not exist for clang-qdoc running
in macOS, but the classes are documented in qvulkanfunctions.cpp.
@@ -69,7 +72,7 @@ QT_END_NAMESPACE
#include <QtGui/qtguiglobal.h>
-#if QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(vulkan) || defined(Q_QDOC)
#ifndef VK_NO_PROTOTYPES
#define VK_NO_PROTOTYPES
@@ -112,8 +115,8 @@ private:
QT_END_NAMESPACE
-#endif // QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)
+#endif // QT_CONFIG(vulkan) || defined(Q_QDOC)
#endif // QVULKANFUNCTIONS_H;
-#endif // Q_CLANG_QDOC
+#endif // Q_QDOC
#endif // QPLATFORMVULKANINSTANCE_H
diff --git a/src/gui/vulkan/qt_attribution.json b/src/gui/vulkan/qt_attribution.json
index b46131abf8..b49e59954d 100644
--- a/src/gui/vulkan/qt_attribution.json
+++ b/src/gui/vulkan/qt_attribution.json
@@ -5,13 +5,13 @@
"QDocModule": "qtgui",
"Description": "Vulkan XML API Registry.",
"QtUsage": "Used to dynamically generate the sources for the QVulkan(Device)Functions classes.",
- "Path": "vk.xml",
+ "Files": "vk.xml",
"Homepage": "https://www.khronos.org/",
- "Version": "1.2.166",
+ "Version": "1.3.223",
"License": "Apache License 2.0 or MIT License",
"LicenseId": "Apache-2.0 OR MIT",
"LicenseFile": "LICENSE.txt",
- "Copyright": "Copyright (c) 2015-2020 The Khronos Group Inc."
+ "Copyright": "Copyright (c) 2015-2022 The Khronos Group Inc."
}
]
diff --git a/src/gui/vulkan/qvulkandefaultinstance.cpp b/src/gui/vulkan/qvulkandefaultinstance.cpp
index c106857691..b4f343cf17 100644
--- a/src/gui/vulkan/qvulkandefaultinstance.cpp
+++ b/src/gui/vulkan/qvulkandefaultinstance.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qvulkandefaultinstance_p.h"
-#include <private/qrhivulkan_p.h>
+#include <rhi/qrhi.h>
#include <QLoggingCategory>
QT_BEGIN_NAMESPACE
@@ -10,7 +10,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcGuiVk, "qt.vulkan")
static QVulkanInstance *s_vulkanInstance;
-static QVulkanDefaultInstance::Flags s_vulkanInstanceFlags;
+Q_CONSTINIT static QVulkanDefaultInstance::Flags s_vulkanInstanceFlags;
QVulkanDefaultInstance::Flags QVulkanDefaultInstance::flags()
{
@@ -36,26 +36,28 @@ QVulkanInstance *QVulkanDefaultInstance::instance()
s_vulkanInstance = new QVulkanInstance;
// With a Vulkan implementation >= 1.1 we can check what
- // vkEnumerateInstanceVersion() says and request 1.2 or 1.1 based on the
- // result. To prevent future surprises, be conservative and ignore any > 1.2
+ // vkEnumerateInstanceVersion() says and request 1.3/1.2/1.1 based on the
+ // result. To prevent future surprises, be conservative and ignore any > 1.3
// versions for now. For 1.0 implementations nothing will be requested, the
// default 0 in VkApplicationInfo means 1.0.
//
// Vulkan 1.0 is actually sufficient for 99% of Qt Quick (3D)'s
- // functionality. In addition, Vulkan implementations tend to enable 1.1 and 1.2
+ // functionality. In addition, Vulkan implementations tend to enable 1.1+
// functionality regardless of the VkInstance API request. However, the
// validation layer seems to take this fairly seriously, so we should be
- // prepared for using 1.1 and 1.2 features in a fully correct manner. This also
- // helps custom Vulkan code in applications, which is not under out control; it
- // is ideal if Vulkan 1.1 and 1.2 are usable without requiring such applications
- // to create their own QVulkanInstance just to be able to make an appropriate
- // setApiVersion() call on it.
+ // prepared for using 1.1+ features in a fully correct manner. This also
+ // helps custom Vulkan code in applications, which is not under out
+ // control; it is ideal if Vulkan 1.1+ versions are usable without
+ // requiring such applications to create their own QVulkanInstance just to
+ // be able to make an appropriate setApiVersion() call on it.
const QVersionNumber supportedVersion = s_vulkanInstance->supportedApiVersion();
- if (supportedVersion >= QVersionNumber(1, 2))
+ if (supportedVersion >= QVersionNumber(1, 3))
+ s_vulkanInstance->setApiVersion(QVersionNumber(1, 3));
+ else if (supportedVersion >= QVersionNumber(1, 2))
s_vulkanInstance->setApiVersion(QVersionNumber(1, 2));
else if (supportedVersion >= QVersionNumber(1, 1))
- s_vulkanInstance->setApiVersion(QVersionNumber(1, 2));
+ s_vulkanInstance->setApiVersion(QVersionNumber(1, 1));
qCDebug(lcGuiVk) << "QVulkanDefaultInstance: Creating Vulkan instance"
<< "Requesting Vulkan API" << s_vulkanInstance->apiVersion()
<< "Instance-level version was reported as" << supportedVersion;
diff --git a/src/gui/vulkan/qvulkanfunctions.cpp b/src/gui/vulkan/qvulkanfunctions.cpp
index e6bf10068f..6a30799502 100644
--- a/src/gui/vulkan/qvulkanfunctions.cpp
+++ b/src/gui/vulkan/qvulkanfunctions.cpp
@@ -13,7 +13,7 @@ QT_BEGIN_NAMESPACE
\wrapper
\brief The QVulkanFunctions class provides cross-platform access to the
- instance level core Vulkan 1.2 API.
+ instance level core Vulkan 1.3 API.
Qt and Qt applications do not link to any Vulkan libraries by default.
Instead, all functions are resolved dynamically at run time. Each
@@ -45,17 +45,17 @@ QT_BEGIN_NAMESPACE
\l{https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkGetInstanceProcAddr.html}{the
man page for vkGetInstanceProcAddr} for more information.
- \note The member function prototypes for Vulkan 1.1 and 1.2 commands are
- ifdefed with the appropriate \c{VK_VERSION_1_x} that is defined by the
- Vulkan headers. Therefore these functions will only be callable by an
+ \note The member function prototypes for Vulkan 1.1, 1.2, and 1.3 commands
+ are \c ifdefed with the appropriate \c{VK_VERSION_1_x} that is defined by
+ the Vulkan headers. As such, these functions will only be callable by an
application when the system's (on which the application is built) Vulkan
- header is new enough and it contains 1.1 and 1.2 Vulkan API definitions.
- When building Qt from source, this has an additional consequence: the
- Vulkan headers on the build environment must also be 1.1 and 1.2 capable in
- order to get a Qt build that supports resolving the 1.1 and 1.2 API
- commands. If either of these conditions is not met, applications will only
- be able to call the Vulkan 1.0 commands through QVulkanFunctions and
- QVulkanDeviceFunctions.
+ header is new enough and it contains 1.1, 1.2, or 1.3 Vulkan API
+ definitions. When building Qt from source, this has an additional
+ consequence: the Vulkan headers on the build environment must also be 1.1,
+ 1.2, and 1.3 compatible to get a Qt build that supports resolving
+ the 1.1, 1.2, and 1.3 API commands. If neither of these conditions is met,
+ applications will only be able to call the Vulkan 1.0 commands through
+ QVulkanFunctions and QVulkanDeviceFunctions.
\sa QVulkanInstance, QVulkanDeviceFunctions, QWindow::setVulkanInstance(), QWindow::setSurfaceType()
*/
@@ -68,7 +68,7 @@ QT_BEGIN_NAMESPACE
\wrapper
\brief The QVulkanDeviceFunctions class provides cross-platform access to
- the device level core Vulkan 1.2 API.
+ the device level core Vulkan 1.3 API.
Qt and Qt applications do not link to any Vulkan libraries by default.
Instead, all functions are resolved dynamically at run time. Each
diff --git a/src/gui/vulkan/qvulkaninstance.cpp b/src/gui/vulkan/qvulkaninstance.cpp
index 3b1b715c32..6d3020d62d 100644
--- a/src/gui/vulkan/qvulkaninstance.cpp
+++ b/src/gui/vulkan/qvulkaninstance.cpp
@@ -12,6 +12,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QVulkanInstance
\since 5.10
+ \ingroup painting-3D
\inmodule QtGui
\brief The QVulkanInstance class represents a native Vulkan instance, enabling
@@ -104,7 +105,7 @@ QT_BEGIN_NAMESPACE
\note It is up to the component creating the external instance to ensure
the necessary extensions are enabled on it. These are: \c{VK_KHR_surface},
the WSI-specific \c{VK_KHR_*_surface} that is appropriate for the platform
- in question, and \c{VK_EXT_debug_report} in case QVulkanInstance's debug
+ in question, and \c{VK_EXT_debug_utils} in case QVulkanInstance's debug
output redirection is desired.
\section1 Accessing Core Vulkan Commands
@@ -205,7 +206,8 @@ QT_BEGIN_NAMESPACE
This enum describes the flags that can be passed to setFlags(). These control
the behavior of create().
- \value NoDebugOutputRedirect Disables Vulkan debug output (\c{VK_EXT_debug_report}) redirection to qDebug.
+ \value NoDebugOutputRedirect Disables Vulkan debug output (\c{VK_EXT_debug_utils}) redirection to qDebug.
+ \value [since 6.5] NoPortabilityDrivers Disables enumerating physical devices marked as Vulkan Portability.
*/
bool QVulkanInstancePrivate::ensureVulkan()
@@ -243,7 +245,8 @@ QVulkanInstance::QVulkanInstance()
/*!
Destructor.
- \note current() will return \nullptr once the instance is destroyed.
+ \note \l {QVulkanInstance::}{vkInstance()} will return \nullptr once the
+ instance is destroyed.
*/
QVulkanInstance::~QVulkanInstance()
{
@@ -296,7 +299,7 @@ QVulkanInstance::~QVulkanInstance()
*/
/*!
- \fn size_t qHash(const QVulkanLayer &key, size_t seed)
+ \fn size_t qHash(const QVulkanLayer &key, size_t seed = 0)
\since 5.10
\relates QVulkanLayer
@@ -340,7 +343,7 @@ QVulkanInstance::~QVulkanInstance()
*/
/*!
- \fn size_t qHash(const QVulkanExtension &key, size_t seed)
+ \fn size_t qHash(const QVulkanExtension &key, size_t seed = 0)
\since 5.10
\relates QVulkanExtension
@@ -425,7 +428,7 @@ QVersionNumber QVulkanInstance::supportedApiVersion() const
\note \a existingVkInstance must have at least \c{VK_KHR_surface} and the
appropriate WSI-specific \c{VK_KHR_*_surface} extensions enabled. To ensure
- debug output redirection is functional, \c{VK_EXT_debug_report} is needed as
+ debug output redirection is functional, \c{VK_EXT_debug_utils} is needed as
well.
\note This function can only be called before create() and has no effect if
@@ -478,9 +481,14 @@ void QVulkanInstance::setLayers(const QByteArrayList &layers)
/*!
Specifies the list of additional instance \a extensions to enable. It is
safe to specify unsupported extensions as well because these get ignored
- when not supported at run time. The surface-related extensions required by
- Qt will always be added automatically, no need to include them in this
- list.
+ when not supported at run time.
+
+ \note The surface-related extensions required by Qt (for example, \c
+ VK_KHR_win32_surface) will always be added automatically, no need to
+ include them in this list.
+
+ \note \c VK_KHR_portability_enumeration is added automatically unless the
+ NoPortabilityDrivers flag is set. This value was introduced in Qt 6.5.
\note This function can only be called before create() and has no effect if
called afterwards.
@@ -533,11 +541,16 @@ void QVulkanInstance::setApiVersion(const QVersionNumber &vulkanVersion)
\return true if successful, false on error or when Vulkan is not supported.
- When successful, the pointer to this QVulkanInstance is retrievable via the
- static function current().
+ When successful, the pointer to this QVulkanInstance is retrievable via
+ \l {QVulkanInstance::}{vkInstance()}.
The Vulkan instance and library is available as long as this
QVulkanInstance exists, or until destroy() is called.
+
+ By default the VkInstance is created with the flag
+ \l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkInstanceCreateFlagBits.html}{VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR}
+ set. This means that Vulkan Portability physical devices get enumerated as
+ well. If this is not desired, set the NoPortabilityDrivers flag.
*/
bool QVulkanInstance::create()
{
@@ -556,6 +569,7 @@ bool QVulkanInstance::create()
d_ptr->errorCode = VK_SUCCESS;
d_ptr->funcs.reset(new QVulkanFunctions(this));
d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters);
+ d_ptr->platformInst->setDebugUtilsFilters(d_ptr->debugUtilsFilters);
return true;
}
@@ -810,12 +824,28 @@ void QVulkanInstance::presentQueued(QWindow *window)
/*!
\typedef QVulkanInstance::DebugFilter
- Typedef for debug filtering callback functions.
+ Typedef for debug filtering callback functions, with the following signature:
+
+ \code
+ bool myDebugFilter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object,
+ size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage)
+ \endcode
+
+ Returning \c true suppresses the printing of the message.
+
+ \note Starting with Qt 6.5 \c{VK_EXT_debug_utils} is used instead of the
+ deprecated \c{VK_EXT_debug_report}. The callback signature is based on
+ VK_EXT_debug_report. Therefore, not all arguments can be expected to be
+ valid anymore. Avoid relying on arguments other than \c pMessage, \c
+ messageCode, and \c object. Applications wishing to access all the callback
+ data as specified in VK_EXT_debug_utils should migrate to DebugUtilsFilter.
\sa installDebugOutputFilter(), removeDebugOutputFilter()
*/
/*!
+ \overload
+
Installs a \a filter function that is called for every Vulkan debug
message. When the callback returns \c true, the message is stopped (filtered
out) and will not appear on the debug output.
@@ -837,6 +867,8 @@ void QVulkanInstance::installDebugOutputFilter(DebugFilter filter)
}
/*!
+ \overload
+
Removes a \a filter function previously installed by
installDebugOutputFilter().
@@ -851,6 +883,84 @@ void QVulkanInstance::removeDebugOutputFilter(DebugFilter filter)
d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters);
}
+/*!
+ \typedef QVulkanInstance::DebugUtilsFilter
+
+ Typedef for debug filtering callback functions, with the following signature:
+
+ \code
+ std::function<bool(DebugMessageSeverityFlags severity, DebugMessageTypeFlags type, const void *message)>;
+ \endcode
+
+ The \c message argument is a pointer to the
+ VkDebugUtilsMessengerCallbackDataEXT structure. Refer to the documentation
+ of \c{VK_EXT_debug_utils} for details. The Qt headers do not use the real
+ type in order to avoid introducing a dependency on post-1.0 Vulkan headers.
+
+ Returning \c true suppresses the printing of the message.
+
+ \sa installDebugOutputFilter(), removeDebugOutputFilter()
+ \since 6.5
+ */
+
+/*!
+ \enum QVulkanInstance::DebugMessageSeverityFlag
+ \since 6.5
+
+ \value VerboseSeverity
+ \value InfoSeverity
+ \value WarningSeverity
+ \value ErrorSeverity
+ */
+
+/*!
+ \enum QVulkanInstance::DebugMessageTypeFlag
+ \since 6.5
+
+ \value GeneralMessage
+ \value ValidationMessage
+ \value PerformanceMessage
+ */
+
+/*!
+ Installs a \a filter function that is called for every Vulkan debug
+ message. When the callback returns \c true, the message is stopped (filtered
+ out) and will not appear on the debug output.
+
+ \note Filtering is only effective when NoDebugOutputRedirect is not
+ \l{setFlags()}{set}. Installing filters has no effect otherwise.
+
+ \note This function can be called before create().
+
+ \sa clearDebugOutputFilters()
+ \since 6.5
+ */
+void QVulkanInstance::installDebugOutputFilter(DebugUtilsFilter filter)
+{
+ d_ptr->debugUtilsFilters.append(filter);
+ if (d_ptr->platformInst)
+ d_ptr->platformInst->setDebugUtilsFilters(d_ptr->debugUtilsFilters);
+}
+
+/*!
+ Removes all filter functions installed previously by
+ installDebugOutputFilter().
+
+ \note This function can be called before create().
+
+ \sa installDebugOutputFilter()
+ \since 6.5
+ */
+void QVulkanInstance::clearDebugOutputFilters()
+{
+ d_ptr->debugFilters.clear();
+ d_ptr->debugUtilsFilters.clear();
+ if (d_ptr->platformInst) {
+ d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters);
+ d_ptr->platformInst->setDebugUtilsFilters(d_ptr->debugUtilsFilters);
+ }
+}
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QVulkanLayer &layer)
{
diff --git a/src/gui/vulkan/qvulkaninstance.h b/src/gui/vulkan/qvulkaninstance.h
index c2c5358ca9..221f605fa2 100644
--- a/src/gui/vulkan/qvulkaninstance.h
+++ b/src/gui/vulkan/qvulkaninstance.h
@@ -11,12 +11,12 @@
#pragma qt_sync_skip_header_check
#endif
-#if QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(vulkan) || defined(Q_QDOC)
#ifndef VK_NO_PROTOTYPES
#define VK_NO_PROTOTYPES
#endif
-#if !defined(Q_CLANG_QDOC) && __has_include(<vulkan/vulkan.h>)
+#if !defined(Q_QDOC) && __has_include(<vulkan/vulkan.h>)
#include <vulkan/vulkan.h>
#else
// QT_CONFIG(vulkan) implies vulkan.h being available at Qt build time, but it
@@ -36,6 +36,7 @@ typedef void* VkPhysicalDevice;
typedef void* VkDevice;
// enums
typedef int VkResult;
+typedef int VkFormat;
typedef int VkImageLayout;
typedef int VkDebugReportFlagsEXT;
typedef int VkDebugReportObjectTypeEXT;
@@ -44,7 +45,7 @@ typedef int VkDebugReportObjectTypeEXT;
// QVulkanInstance itself is only applicable if vulkan.h is available, or if
// it's qdoc. An application that is built on a vulkan.h-less system against a
// Vulkan-enabled Qt gets the dummy typedefs but not QVulkan*.
-#if __has_include(<vulkan/vulkan.h>) || defined(Q_CLANG_QDOC)
+#if __has_include(<vulkan/vulkan.h>) || defined(Q_QDOC)
#include <QtCore/qbytearraylist.h>
#include <QtCore/qdebug.h>
@@ -134,7 +135,8 @@ public:
~QVulkanInstance();
enum Flag {
- NoDebugOutputRedirect = 0x01
+ NoDebugOutputRedirect = 0x01,
+ NoPortabilityDrivers = 0x02
};
Q_DECLARE_FLAGS(Flags, Flag)
@@ -186,6 +188,25 @@ public:
void installDebugOutputFilter(DebugFilter filter);
void removeDebugOutputFilter(DebugFilter filter);
+ enum DebugMessageSeverityFlag {
+ VerboseSeverity = 0x01,
+ InfoSeverity = 0x02,
+ WarningSeverity = 0x04,
+ ErrorSeverity = 0x08
+ };
+ Q_DECLARE_FLAGS(DebugMessageSeverityFlags, DebugMessageSeverityFlag)
+
+ enum DebugMessageTypeFlag {
+ GeneralMessage = 0x01,
+ ValidationMessage = 0x02,
+ PerformanceMessage = 0x04
+ };
+ Q_DECLARE_FLAGS(DebugMessageTypeFlags, DebugMessageTypeFlag)
+
+ using DebugUtilsFilter = std::function<bool(DebugMessageSeverityFlags severity, DebugMessageTypeFlags type, const void *message)>;
+ void installDebugOutputFilter(DebugUtilsFilter filter);
+ void clearDebugOutputFilters();
+
private:
friend class QVulkanInstancePrivate;
QScopedPointer<QVulkanInstancePrivate> d_ptr;
@@ -193,11 +214,13 @@ private:
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QVulkanInstance::Flags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QVulkanInstance::DebugMessageTypeFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QVulkanInstance::DebugMessageSeverityFlags)
QT_END_NAMESPACE
-#endif // __has_include(<vulkan/vulkan.h>) || defined(Q_CLANG_QDOC)
+#endif // __has_include(<vulkan/vulkan.h>) || defined(Q_QDOC)
-#endif // QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)
+#endif // QT_CONFIG(vulkan) || defined(Q_QDOC)
#endif // QVULKANINSTANCE_H
diff --git a/src/gui/vulkan/qvulkaninstance_p.h b/src/gui/vulkan/qvulkaninstance_p.h
index 27dc5383cf..d707b301c6 100644
--- a/src/gui/vulkan/qvulkaninstance_p.h
+++ b/src/gui/vulkan/qvulkaninstance_p.h
@@ -6,7 +6,7 @@
#include <QtGui/private/qtguiglobal_p.h>
-#if QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(vulkan) || defined(Q_QDOC)
#include "qvulkaninstance.h"
#include <private/qvulkanfunctions_p.h>
@@ -49,7 +49,8 @@ public:
VkResult errorCode;
QScopedPointer<QVulkanFunctions> funcs;
QHash<VkDevice, QVulkanDeviceFunctions *> deviceFuncs;
- QList<QVulkanInstance::DebugFilter> debugFilters;
+ QList<QVulkanInstance::DebugFilter> debugFilters; // legacy filters based on VK_EXT_debug_report
+ QList<QVulkanInstance::DebugUtilsFilter> debugUtilsFilters; // the modern version based on VK_EXT_debug_utils
};
QT_END_NAMESPACE
diff --git a/src/gui/vulkan/qvulkanwindow.cpp b/src/gui/vulkan/qvulkanwindow.cpp
index 3dc7dd2200..00a5c5f869 100644
--- a/src/gui/vulkan/qvulkanwindow.cpp
+++ b/src/gui/vulkan/qvulkanwindow.cpp
@@ -175,11 +175,14 @@ Q_DECLARE_LOGGING_CATEGORY(lcGuiVk)
When it comes to device features, QVulkanWindow enables all Vulkan 1.0
features that are reported as supported from vkGetPhysicalDeviceFeatures().
- This is always not sufficient, and therefore full control over the
- VkPhysicalDeviceFeatures used for device creation is possible too by
- registering a callback function with setEnabledFeaturesModifier(). When set,
- the callback function is invoked, letting it alter the
- VkPhysicalDeviceFeatures, instead of enabling only the 1.0 features.
+ As an exception to this rule, \c robustBufferAccess is never enabled. Use the
+ callback mechanism described below, if enabling that feature is desired.
+
+ This is not always desirable, and may be insufficient with Vulkan 1.1 and
+ higher. Therefore, full control over the VkPhysicalDeviceFeatures used for
+ device creation is possible too by registering a callback function with
+ setEnabledFeaturesModifier(). When set, the callback function is invoked,
+ letting it alter the VkPhysicalDeviceFeatures or VkPhysicalDeviceFeatures2.
\sa QVulkanInstance, QWindow
*/
@@ -331,7 +334,7 @@ void QVulkanWindow::setPhysicalDeviceIndex(int idx)
qWarning("QVulkanWindow: Attempted to set physical device when already initialized");
return;
}
- const int count = availablePhysicalDevices().count();
+ const int count = availablePhysicalDevices().size();
if (idx < 0 || idx >= count) {
qWarning("QVulkanWindow: Invalid physical device index %d (total physical devices: %d)", idx, count);
return;
@@ -444,7 +447,7 @@ void QVulkanWindow::setPreferredColorFormats(const QList<VkFormat> &formats)
static struct {
VkSampleCountFlagBits mask;
int count;
-} qvk_sampleCounts[] = {
+} q_vk_sampleCounts[] = {
// keep this sorted by 'count'
{ VK_SAMPLE_COUNT_1_BIT, 1 },
{ VK_SAMPLE_COUNT_2_BIT, 2 },
@@ -484,7 +487,7 @@ QList<int> QVulkanWindow::supportedSampleCounts()
VkSampleCountFlags depth = limits->framebufferDepthSampleCounts;
VkSampleCountFlags stencil = limits->framebufferStencilSampleCounts;
- for (const auto &qvk_sampleCount : qvk_sampleCounts) {
+ for (const auto &qvk_sampleCount : q_vk_sampleCounts) {
if ((color & qvk_sampleCount.mask)
&& (depth & qvk_sampleCount.mask)
&& (stencil & qvk_sampleCount.mask))
@@ -533,7 +536,7 @@ void QVulkanWindow::setSampleCount(int sampleCount)
return;
}
- for (const auto &qvk_sampleCount : qvk_sampleCounts) {
+ for (const auto &qvk_sampleCount : q_vk_sampleCounts) {
if (qvk_sampleCount.count == sampleCount) {
d->sampleCount = qvk_sampleCount.mask;
return;
@@ -577,7 +580,7 @@ void QVulkanWindowPrivate::init()
return;
}
- if (physDevIndex < 0 || physDevIndex >= physDevs.count()) {
+ if (physDevIndex < 0 || physDevIndex >= physDevs.size()) {
qWarning("QVulkanWindow: Invalid physical device index; defaulting to 0");
physDevIndex = 0;
}
@@ -596,7 +599,7 @@ void QVulkanWindowPrivate::init()
f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueFamilyProps.data());
gfxQueueFamilyIdx = uint32_t(-1);
presQueueFamilyIdx = uint32_t(-1);
- for (int i = 0; i < queueFamilyProps.count(); ++i) {
+ for (int i = 0; i < queueFamilyProps.size(); ++i) {
const bool supportsPresent = inst->supportsPresent(physDev, i, q);
qCDebug(lcGuiVk, "queue family %d: flags=0x%x count=%d supportsPresent=%d", i,
queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount, supportsPresent);
@@ -609,7 +612,7 @@ void QVulkanWindowPrivate::init()
presQueueFamilyIdx = gfxQueueFamilyIdx;
} else {
qCDebug(lcGuiVk, "No queue with graphics+present; trying separate queues");
- for (int i = 0; i < queueFamilyProps.count(); ++i) {
+ for (int i = 0; i < queueFamilyProps.size(); ++i) {
if (gfxQueueFamilyIdx == uint32_t(-1) && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))
gfxQueueFamilyIdx = i;
if (presQueueFamilyIdx == uint32_t(-1) && inst->supportsPresent(physDev, i, q))
@@ -653,7 +656,7 @@ void QVulkanWindowPrivate::init()
queueCreateInfoModifier(queueFamilyProps.constData(), queueCount, queueInfo);
bool foundGfxQueue = false;
bool foundPresQueue = false;
- for (const VkDeviceQueueCreateInfo& createInfo : qAsConst(queueInfo)) {
+ for (const VkDeviceQueueCreateInfo& createInfo : std::as_const(queueInfo)) {
foundGfxQueue |= createInfo.queueFamilyIndex == gfxQueueFamilyIdx;
foundPresQueue |= createInfo.queueFamilyIndex == presQueueFamilyIdx;
}
@@ -695,18 +698,25 @@ void QVulkanWindowPrivate::init()
devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
devInfo.queueCreateInfoCount = queueInfo.size();
devInfo.pQueueCreateInfos = queueInfo.constData();
- devInfo.enabledExtensionCount = devExts.count();
+ devInfo.enabledExtensionCount = devExts.size();
devInfo.ppEnabledExtensionNames = devExts.constData();
- VkPhysicalDeviceFeatures features;
- memset(&features, 0, sizeof(features));
- if (enabledFeaturesModifier) {
+ VkPhysicalDeviceFeatures features = {};
+ VkPhysicalDeviceFeatures2 features2 = {};
+ if (enabledFeatures2Modifier) {
+ features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+ enabledFeatures2Modifier(features2);
+ devInfo.pNext = &features2;
+ } else if (enabledFeaturesModifier) {
enabledFeaturesModifier(features);
+ devInfo.pEnabledFeatures = &features;
} else {
- // Enable all 1.0 features.
+ // Enable all supported 1.0 core features, except ones that likely
+ // involve a performance penalty.
f->vkGetPhysicalDeviceFeatures(physDev, &features);
+ features.robustBufferAccess = VK_FALSE;
+ devInfo.pEnabledFeatures = &features;
}
- devInfo.pEnabledFeatures = &features;
// Device layers are not supported by QVulkanWindow since that's an already deprecated
// API. However, have a workaround for systems with older API and layers (f.ex. L4T
@@ -851,7 +861,7 @@ void QVulkanWindowPrivate::init()
// Try to honor the user request.
if (!formats.isEmpty() && !requestedColorFormats.isEmpty()) {
- for (VkFormat reqFmt : qAsConst(requestedColorFormats)) {
+ for (VkFormat reqFmt : std::as_const(requestedColorFormats)) {
auto r = std::find_if(formats.cbegin(), formats.cend(),
[reqFmt](const VkSurfaceFormatKHR &sfmt) { return sfmt.format == reqFmt; });
if (r != formats.cend()) {
@@ -1614,18 +1624,17 @@ void QVulkanWindow::setQueueCreateInfoModifier(const QueueCreateInfoModifier &mo
VkPhysicalDeviceFeatures that is passed in when creating a Vulkan device
object.
- By default QVulkanWindow enables all Vulkan 1.0 features the physical
- device reports as supported. That is not always sufficient when working
- with Vulkan 1.1 or 1.2 features and extensions. Hence this callback
- mechanism.
+ By default QVulkanWindow enables all Vulkan 1.0 core features that the
+ physical device reports as supported, with certain exceptions. In
+ praticular, \c robustBufferAccess is always disabled in order to avoid
+ unexpected performance hits.
The VkPhysicalDeviceFeatures reference passed in is all zeroed out at the
point when the function is invoked. It is up to the function to change
- members to true, or set up \c pNext chains as it sees fit.
+ members as it sees fit.
- \note When setting up \c pNext chains, make sure the referenced objects
- have a long enough lifetime, for example by storing them as member
- variables in the QVulkanWindow subclass.
+ \note To control Vulkan 1.1, 1.2, or 1.3 features, use
+ EnabledFeatures2Modifier instead.
\sa setEnabledFeaturesModifier()
*/
@@ -1633,9 +1642,14 @@ void QVulkanWindow::setQueueCreateInfoModifier(const QueueCreateInfoModifier &mo
/*!
Sets the enabled device features modification function \a modifier.
- \sa EnabledFeaturesModifier
+ \note To control Vulkan 1.1, 1.2, or 1.3 features, use
+ the overload taking a EnabledFeatures2Modifier instead.
- \since 6.4
+ \note \a modifier is passed to the callback function with all members set
+ to false. It is up to the function to change members as it sees fit.
+
+ \since 6.7
+ \sa EnabledFeaturesModifier
*/
void QVulkanWindow::setEnabledFeaturesModifier(const EnabledFeaturesModifier &modifier)
{
@@ -1644,6 +1658,45 @@ void QVulkanWindow::setEnabledFeaturesModifier(const EnabledFeaturesModifier &mo
}
/*!
+ \typedef QVulkanWindow::EnabledFeatures2Modifier
+
+ A function that is called during graphics initialization to alter the
+ VkPhysicalDeviceFeatures2 that is changed to the VkDeviceCreateInfo.
+
+ By default QVulkanWindow enables all Vulkan 1.0 core features that the
+ physical device reports as supported, with certain exceptions. In
+ praticular, \c robustBufferAccess is always disabled in order to avoid
+ unexpected performance hits.
+
+ This however is not always sufficient when working with Vulkan 1.1, 1.2, or
+ 1.3 features and extensions. Hence this callback mechanism. If only Vulkan
+ 1.0 is relevant at run time, use setEnabledFeaturesModifier() instead.
+
+ The VkPhysicalDeviceFeatures2 reference passed to the callback function
+ with \c sType set, but the rest zeroed out. It is up to the function to
+ change members to true, or set up \c pNext chains as it sees fit.
+
+ \note When setting up \c pNext chains, make sure the referenced objects
+ have a long enough lifetime, for example by storing them as member
+ variables in the QVulkanWindow subclass.
+
+ \since 6.7
+ \sa setEnabledFeaturesModifier()
+ */
+
+/*!
+ Sets the enabled device features modification function \a modifier.
+ \overload
+ \since 6.7
+ \sa EnabledFeatures2Modifier
+*/
+void QVulkanWindow::setEnabledFeaturesModifier(EnabledFeatures2Modifier modifier)
+{
+ Q_D(QVulkanWindow);
+ d->enabledFeatures2Modifier = std::move(modifier);
+}
+
+/*!
Returns true if this window has successfully initialized all Vulkan
resources, including the swapchain.
@@ -1843,13 +1896,26 @@ void QVulkanWindowRenderer::logicalDeviceLost()
{
}
+QSize QVulkanWindowPrivate::surfacePixelSize() const
+{
+ Q_Q(const QVulkanWindow);
+ VkSurfaceCapabilitiesKHR surfaceCaps = {};
+ vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDevs.at(physDevIndex), surface, &surfaceCaps);
+ VkExtent2D bufferSize = surfaceCaps.currentExtent;
+ if (bufferSize.width == uint32_t(-1)) {
+ Q_ASSERT(bufferSize.height == uint32_t(-1));
+ return q->size() * q->devicePixelRatio();
+ }
+ return QSize(int(bufferSize.width), int(bufferSize.height));
+}
+
void QVulkanWindowPrivate::beginFrame()
{
if (!swapChain || framePending)
return;
Q_Q(QVulkanWindow);
- if (q->size() * q->devicePixelRatio() != swapChainImageSize) {
+ if (swapChainImageSize != surfacePixelSize()) {
recreateSwapChain();
if (!swapChain)
return;
@@ -1918,7 +1984,7 @@ void QVulkanWindowPrivate::beginFrame()
}
if (frameGrabbing)
- frameGrabTargetImage = QImage(swapChainImageSize, QImage::Format_RGBA8888);
+ frameGrabTargetImage = QImage(swapChainImageSize, QImage::Format_RGBA8888); // the format is as documented
if (renderer) {
framePending = true;
@@ -2264,7 +2330,7 @@ void QVulkanWindowPrivate::finishBlockingReadback()
VkPhysicalDevice QVulkanWindow::physicalDevice() const
{
Q_D(const QVulkanWindow);
- if (d->physDevIndex < d->physDevs.count())
+ if (d->physDevIndex < d->physDevs.size())
return d->physDevs[d->physDevIndex];
qWarning("QVulkanWindow: Physical device not available");
return VK_NULL_HANDLE;
@@ -2280,7 +2346,7 @@ VkPhysicalDevice QVulkanWindow::physicalDevice() const
const VkPhysicalDeviceProperties *QVulkanWindow::physicalDeviceProperties() const
{
Q_D(const QVulkanWindow);
- if (d->physDevIndex < d->physDevProps.count())
+ if (d->physDevIndex < d->physDevProps.size())
return &d->physDevProps[d->physDevIndex];
qWarning("QVulkanWindow: Physical device properties not available");
return nullptr;
@@ -2434,6 +2500,19 @@ VkFormat QVulkanWindow::depthStencilFormat() const
This usually matches the size of the window, but may also differ in case
\c vkGetPhysicalDeviceSurfaceCapabilitiesKHR reports a fixed size.
+ In addition, it has been observed on some platforms that the
+ Vulkan-reported surface size is different with high DPI scaling active,
+ meaning the QWindow-reported
+ \l{QWindow::}{size()} multiplied with the \l{QWindow::}{devicePixelRatio()}
+ was 1 pixel less or more when compared to the value returned from here,
+ presumably due to differences in rounding. Rendering code should be aware
+ of this, and any related rendering logic must be based in the value returned
+ from here, never on the QWindow-reported size. Regardless of which pixel size
+ is correct in theory, Vulkan rendering must only ever rely on the Vulkan
+ API-reported surface size. Otherwise validation errors may occur, e.g. when
+ setting the viewport, because the application-provided values may become
+ out-of-bounds from Vulkan's perspective.
+
\note Calling this function is only valid from the invocation of
QVulkanWindowRenderer::initSwapChainResources() up until
QVulkanWindowRenderer::releaseSwapChainResources().
@@ -2705,6 +2784,12 @@ bool QVulkanWindow::supportsGrab() const
incomplete image, that has the correct size but not the content yet. The
content will be delivered via the frameGrabbed() signal in the latter case.
+ The returned QImage always has a format of QImage::Format_RGBA8888. If the
+ colorFormat() is \c VK_FORMAT_B8G8R8A8_UNORM, the red and blue channels are
+ swapped automatically since this format is commonly used as the default
+ choice for swapchain color buffers. With any other color buffer format,
+ there is no conversion performed by this function.
+
\note This function should not be called when a frame is in progress
(that is, frameReady() has not yet been called back by the application).
@@ -2733,6 +2818,9 @@ QImage QVulkanWindow::grab()
d->frameGrabbing = true;
d->beginFrame();
+ if (d->colorFormat == VK_FORMAT_B8G8R8A8_UNORM)
+ d->frameGrabTargetImage = std::move(d->frameGrabTargetImage).rgbSwapped();
+
return d->frameGrabTargetImage;
}
diff --git a/src/gui/vulkan/qvulkanwindow.h b/src/gui/vulkan/qvulkanwindow.h
index a2b538ed54..537dbc4ae1 100644
--- a/src/gui/vulkan/qvulkanwindow.h
+++ b/src/gui/vulkan/qvulkanwindow.h
@@ -11,7 +11,7 @@
#pragma qt_sync_skip_header_check
#endif
-#if QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(vulkan) || defined(Q_QDOC)
#include <QtGui/qvulkaninstance.h>
#include <QtGui/qwindow.h>
@@ -19,7 +19,7 @@
#include <QtGui/qmatrix4x4.h>
#include <QtCore/qset.h>
-#ifdef Q_CLANG_QDOC
+#ifdef Q_QDOC
typedef void* VkQueue;
typedef void* VkCommandPool;
typedef void* VkRenderPass;
@@ -54,6 +54,14 @@ public:
virtual void logicalDeviceLost();
};
+#ifndef VK_VERSION_1_1
+typedef struct VkPhysicalDeviceFeatures2 {
+ VkStructureType sType;
+ void* pNext;
+ VkPhysicalDeviceFeatures features;
+} VkPhysicalDeviceFeatures2;
+#endif
+
class Q_GUI_EXPORT QVulkanWindow : public QWindow
{
Q_OBJECT
@@ -79,6 +87,8 @@ public:
typedef std::function<void(VkPhysicalDeviceFeatures &)> EnabledFeaturesModifier;
void setEnabledFeaturesModifier(const EnabledFeaturesModifier &modifier);
+ typedef std::function<void(VkPhysicalDeviceFeatures2 &)> EnabledFeatures2Modifier;
+ void setEnabledFeaturesModifier(EnabledFeatures2Modifier modifier);
void setPreferredColorFormats(const QList<VkFormat> &formats);
diff --git a/src/gui/vulkan/qvulkanwindow_p.h b/src/gui/vulkan/qvulkanwindow_p.h
index 6327334222..7a0ee091e6 100644
--- a/src/gui/vulkan/qvulkanwindow_p.h
+++ b/src/gui/vulkan/qvulkanwindow_p.h
@@ -6,7 +6,7 @@
#include <QtGui/private/qtguiglobal_p.h>
-#if QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(vulkan) || defined(Q_QDOC)
#include "qvulkanwindow.h"
#include <QtCore/QHash>
@@ -36,6 +36,7 @@ public:
void init();
void reset();
bool createDefaultRenderPass();
+ QSize surfacePixelSize() const;
void recreateSwapChain();
uint32_t chooseTransientImageMemType(VkImage img, uint32_t startIndex);
bool createTransientImage(VkFormat format, VkImageUsageFlags usage, VkImageAspectFlags aspectMask,
@@ -68,6 +69,7 @@ public:
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT;
QVulkanWindow::QueueCreateInfoModifier queueCreateInfoModifier;
QVulkanWindow::EnabledFeaturesModifier enabledFeaturesModifier;
+ QVulkanWindow::EnabledFeatures2Modifier enabledFeatures2Modifier;
VkDevice dev = VK_NULL_HANDLE;
QVulkanDeviceFunctions *devFuncs;
diff --git a/src/gui/vulkan/vk.xml b/src/gui/vulkan/vk.xml
index dcba2c17cc..84690f07b2 100644
--- a/src/gui/vulkan/vk.xml
+++ b/src/gui/vulkan/vk.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<registry>
<comment>
-Copyright (c) 2015-2020 The Khronos Group Inc.
+Copyright 2015-2022 The Khronos Group Inc.
SPDX-License-Identifier: Apache-2.0 OR MIT
</comment>
@@ -51,11 +51,12 @@ branch of the member gitlab server.
<tag name="KDAB" author="KDAB" contact="Sean Harmer @seanharmer"/>
<tag name="ANDROID" author="Google LLC" contact="Jesse Hall @critsec"/>
<tag name="CHROMIUM" author="Google LLC" contact="Jesse Hall @critsec"/>
- <tag name="FUCHSIA" author="Google LLC" contact="Craig Stout @cdotstout, Jesse Hall @critsec"/>
+ <tag name="FUCHSIA" author="Google LLC" contact="Craig Stout @cdotstout, Jesse Hall @critsec, John Rosasco @rosasco"/>
<tag name="GGP" author="Google, LLC" contact="Jean-Francois Roy @jfroy, Hai Nguyen @chaoticbob, Jesse Hall @critsec"/>
<tag name="GOOGLE" author="Google LLC" contact="Jesse Hall @critsec"/>
<tag name="QCOM" author="Qualcomm Technologies, Inc." contact="Jeff Leger @jackohounhd"/>
<tag name="LUNARG" author="LunarG, Inc." contact="Karen Ghavam @karenghavam-lunarg"/>
+ <tag name="NZXT" author="NZXT Inc." contact="Jacob Kiesel @xaeroxe"/>
<tag name="SAMSUNG" author="Samsung Electronics Co., Ltd." contact="Alon Or-bach @alonorbach"/>
<tag name="SEC" author="Samsung Electronics Co., Ltd." contact="Alon Or-bach @alonorbach"/>
<tag name="TIZEN" author="Samsung Electronics Co., Ltd." contact="Alon Or-bach @alonorbach"/>
@@ -67,9 +68,12 @@ branch of the member gitlab server.
<tag name="EXT" author="Multivendor" contact="Jon Leech @oddhack"/>
<tag name="MESA" author="Mesa open source project" contact="Chad Versace @chadversary, Daniel Stone @fooishbar, David Airlie @airlied, Jason Ekstrand @jekstrand"/>
<tag name="INTEL" author="Intel Corporation" contact="Slawek Grajewski @sgrajewski"/>
- <tag name="HUAWEI" author="Huawei Technologies Co. Ltd." contact="Hueilong Wang @wyvernathuawei"/>
+ <tag name="HUAWEI" author="Huawei Technologies Co. Ltd." contact="Pan Gao @PanGao-h, Juntao Li @Lawrenceleehw"/>
<tag name="VALVE" author="Valve Corporation" contact="Pierre-Loup Griffais @plagman, Joshua Ashton @Joshua-Ashton, Hans-Kristian Arntzen @HansKristian-Work"/>
<tag name="QNX" author="BlackBerry Limited" contact="Mike Gorchak @mgorchak-blackberry"/>
+ <tag name="JUICE" author="Juice Technologies, Inc." contact="David McCloskey @damcclos, Dean Beeler @canadacow"/>
+ <tag name="FB" author="Facebook, Inc" contact="Artem Bolgar @artyom17"/>
+ <tag name="RASTERGRID" author="RasterGrid Kft." contact="Daniel Rakos @aqnuep1"/>
</tags>
<types comment="Vulkan type definitions">
@@ -85,6 +89,7 @@ branch of the member gitlab server.
<type category="include" name="directfb.h"/>
<type category="include" name="zircon/types.h"/>
<type category="include" name="ggp_c/vulkan_types.h"/>
+ <type category="include" name="screen/screen.h"/>
<comment>
In the current header structure, each platform's interfaces
are confined to a platform-specific header (vulkan_xlib.h,
@@ -123,53 +128,119 @@ branch of the member gitlab server.
<type requires="zircon/types.h" name="zx_handle_t"/>
<type requires="ggp_c/vulkan_types.h" name="GgpStreamDescriptor"/>
<type requires="ggp_c/vulkan_types.h" name="GgpFrameToken"/>
+ <type requires="screen/screen.h" name="_screen_context"/>
+ <type requires="screen/screen.h" name="_screen_window"/>
- <type category="define">#define <name>VK_MAKE_VERSION</name>(major, minor, patch) \
+ <type category="define">// DEPRECATED: This define is deprecated. VK_MAKE_API_VERSION should be used instead.
+#define <name>VK_MAKE_VERSION</name>(major, minor, patch) \
((((uint32_t)(major)) &lt;&lt; 22) | (((uint32_t)(minor)) &lt;&lt; 12) | ((uint32_t)(patch)))</type>
- <type category="define">#define <name>VK_VERSION_MAJOR</name>(version) ((uint32_t)(version) &gt;&gt; 22)</type>
- <type category="define">#define <name>VK_VERSION_MINOR</name>(version) (((uint32_t)(version) &gt;&gt; 12) &amp; 0x3ff)</type>
- <type category="define">#define <name>VK_VERSION_PATCH</name>(version) ((uint32_t)(version) &amp; 0xfff)</type>
+ <type category="define">// DEPRECATED: This define is deprecated. VK_API_VERSION_MAJOR should be used instead.
+#define <name>VK_VERSION_MAJOR</name>(version) ((uint32_t)(version) &gt;&gt; 22)</type>
+ <type category="define">// DEPRECATED: This define is deprecated. VK_API_VERSION_MINOR should be used instead.
+#define <name>VK_VERSION_MINOR</name>(version) (((uint32_t)(version) &gt;&gt; 12) &amp; 0x3FFU)</type>
+ <type category="define">// DEPRECATED: This define is deprecated. VK_API_VERSION_PATCH should be used instead.
+#define <name>VK_VERSION_PATCH</name>(version) ((uint32_t)(version) &amp; 0xFFFU)</type>
+
+ <type category="define">#define <name>VK_MAKE_API_VERSION</name>(variant, major, minor, patch) \
+ ((((uint32_t)(variant)) &lt;&lt; 29) | (((uint32_t)(major)) &lt;&lt; 22) | (((uint32_t)(minor)) &lt;&lt; 12) | ((uint32_t)(patch)))</type>
+ <type category="define">#define <name>VK_API_VERSION_VARIANT</name>(version) ((uint32_t)(version) &gt;&gt; 29)</type>
+ <type category="define">#define <name>VK_API_VERSION_MAJOR</name>(version) (((uint32_t)(version) &gt;&gt; 22) &amp; 0x7FU)</type>
+ <type category="define">#define <name>VK_API_VERSION_MINOR</name>(version) (((uint32_t)(version) &gt;&gt; 12) &amp; 0x3FFU)</type>
+ <type category="define">#define <name>VK_API_VERSION_PATCH</name>(version) ((uint32_t)(version) &amp; 0xFFFU)</type>
<type category="define">// DEPRECATED: This define has been removed. Specific version defines (e.g. VK_API_VERSION_1_0), or the VK_MAKE_VERSION macro, should be used instead.
//#define <name>VK_API_VERSION</name> <type>VK_MAKE_VERSION</type>(1, 0, 0) // Patch version should always be set to 0</type>
- <type category="define">// Vulkan 1.0 version number
-#define <name>VK_API_VERSION_1_0</name> <type>VK_MAKE_VERSION</type>(1, 0, 0)// Patch version should always be set to 0</type>
- <type category="define">// Vulkan 1.1 version number
-#define <name>VK_API_VERSION_1_1</name> <type>VK_MAKE_VERSION</type>(1, 1, 0)// Patch version should always be set to 0</type>
- <type category="define">// Vulkan 1.2 version number
-#define <name>VK_API_VERSION_1_2</name> <type>VK_MAKE_VERSION</type>(1, 2, 0)// Patch version should always be set to 0</type>
+ <type category="define" requires="VK_MAKE_API_VERSION">// Vulkan 1.0 version number
+#define <name>VK_API_VERSION_1_0</name> <type>VK_MAKE_API_VERSION</type>(0, 1, 0, 0)// Patch version should always be set to 0</type>
+ <type category="define" requires="VK_MAKE_API_VERSION">// Vulkan 1.1 version number
+#define <name>VK_API_VERSION_1_1</name> <type>VK_MAKE_API_VERSION</type>(0, 1, 1, 0)// Patch version should always be set to 0</type>
+ <type category="define" requires="VK_MAKE_API_VERSION">// Vulkan 1.2 version number
+#define <name>VK_API_VERSION_1_2</name> <type>VK_MAKE_API_VERSION</type>(0, 1, 2, 0)// Patch version should always be set to 0</type>
+ <type category="define" requires="VK_MAKE_API_VERSION">// Vulkan 1.3 version number
+#define <name>VK_API_VERSION_1_3</name> <type>VK_MAKE_API_VERSION</type>(0, 1, 3, 0)// Patch version should always be set to 0</type>
<type category="define">// Version of this file
-#define <name>VK_HEADER_VERSION</name> 166</type>
+#define <name>VK_HEADER_VERSION</name> 223</type>
<type category="define" requires="VK_HEADER_VERSION">// Complete version of this file
-#define <name>VK_HEADER_VERSION_COMPLETE</name> <type>VK_MAKE_VERSION</type>(1, 2, VK_HEADER_VERSION)</type>
+#define <name>VK_HEADER_VERSION_COMPLETE</name> <type>VK_MAKE_API_VERSION</type>(0, 1, 3, VK_HEADER_VERSION)</type>
<type category="define">
#define <name>VK_DEFINE_HANDLE</name>(object) typedef struct object##_T* object;</type>
- <type category="define" name="VK_DEFINE_NON_DISPATCHABLE_HANDLE">
-#if !defined(VK_DEFINE_NON_DISPATCHABLE_HANDLE)
-#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) &amp;&amp; !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
+ <type category="define" name="VK_USE_64_BIT_PTR_DEFINES">
+#ifndef VK_USE_64_BIT_PTR_DEFINES
+ #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) &amp;&amp; !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
+ #define VK_USE_64_BIT_PTR_DEFINES 1
+ #else
+ #define VK_USE_64_BIT_PTR_DEFINES 0
+ #endif
+#endif</type>
+ <type category="define" requires="VK_USE_64_BIT_PTR_DEFINES" name="VK_NULL_HANDLE">
+#ifndef VK_DEFINE_NON_DISPATCHABLE_HANDLE
+ #if (VK_USE_64_BIT_PTR_DEFINES==1)
+ #if (defined(__cplusplus) &amp;&amp; (__cplusplus >= 201103L)) || (defined(_MSVC_LANG) &amp;&amp; (_MSVC_LANG >= 201103L))
+ #define VK_NULL_HANDLE nullptr
+ #else
+ #define VK_NULL_HANDLE ((void*)0)
+ #endif
+ #else
+ #define VK_NULL_HANDLE 0ULL
+ #endif
+#endif
+#ifndef VK_NULL_HANDLE
+ #define VK_NULL_HANDLE 0
+#endif</type>
+ <type category="define" requires="VK_NULL_HANDLE" name="VK_DEFINE_NON_DISPATCHABLE_HANDLE">
+#ifndef VK_DEFINE_NON_DISPATCHABLE_HANDLE
+ #if (VK_USE_64_BIT_PTR_DEFINES==1)
#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object;
-#else
+ #else
#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
-#endif
+ #endif
#endif</type>
- <type category="define">
-#define <name>VK_NULL_HANDLE</name> 0</type>
-
<type category="basetype">struct <name>ANativeWindow</name>;</type>
<type category="basetype">struct <name>AHardwareBuffer</name>;</type>
- <type category="basetype">
-#ifdef __OBJC__
+ <type category="basetype">#ifdef __OBJC__
@class CAMetalLayer;
#else
typedef void <name>CAMetalLayer</name>;
#endif</type>
+ <type category="basetype">#ifdef __OBJC__
+@protocol MTLDevice;
+typedef id&lt;MTLDevice&gt; MTLDevice_id;
+#else
+typedef void* <name>MTLDevice_id</name>;
+#endif</type>
+ <type category="basetype">#ifdef __OBJC__
+@protocol MTLCommandQueue;
+typedef id&lt;MTLCommandQueue&gt; MTLCommandQueue_id;
+#else
+typedef void* <name>MTLCommandQueue_id</name>;
+#endif</type>
+ <type category="basetype">#ifdef __OBJC__
+@protocol MTLBuffer;
+typedef id&lt;MTLBuffer&gt; MTLBuffer_id;
+#else
+typedef void* <name>MTLBuffer_id</name>;
+#endif</type>
+ <type category="basetype">#ifdef __OBJC__
+@protocol MTLTexture;
+typedef id&lt;MTLTexture&gt; MTLTexture_id;
+#else
+typedef void* <name>MTLTexture_id</name>;
+#endif</type>
+ <type category="basetype">#ifdef __OBJC__
+@protocol MTLSharedEvent;
+typedef id&lt;MTLSharedEvent&gt; MTLSharedEvent_id;
+#else
+typedef void* <name>MTLSharedEvent_id</name>;
+#endif</type>
+ <type category="basetype">typedef struct __IOSurface* <name>IOSurfaceRef</name>;</type>
<type category="basetype">typedef <type>uint32_t</type> <name>VkSampleMask</name>;</type>
<type category="basetype">typedef <type>uint32_t</type> <name>VkBool32</name>;</type>
<type category="basetype">typedef <type>uint32_t</type> <name>VkFlags</name>;</type>
+ <type category="basetype">typedef <type>uint64_t</type> <name>VkFlags64</name>;</type>
<type category="basetype">typedef <type>uint64_t</type> <name>VkDeviceSize</name>;</type>
<type category="basetype">typedef <type>uint64_t</type> <name>VkDeviceAddress</name>;</type>
@@ -178,7 +249,9 @@ typedef void <name>CAMetalLayer</name>;
<type requires="vk_platform" name="char"/>
<type requires="vk_platform" name="float"/>
<type requires="vk_platform" name="double"/>
+ <type requires="vk_platform" name="int8_t"/>
<type requires="vk_platform" name="uint8_t"/>
+ <type requires="vk_platform" name="int16_t"/>
<type requires="vk_platform" name="uint16_t"/>
<type requires="vk_platform" name="uint32_t"/>
<type requires="vk_platform" name="uint64_t"/>
@@ -192,11 +265,11 @@ typedef void <name>CAMetalLayer</name>;
<type category="bitmask">typedef <type>VkFlags</type> <name>VkQueryPoolCreateFlags</name>;</type>
<type requires="VkRenderPassCreateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkRenderPassCreateFlags</name>;</type>
<type requires="VkSamplerCreateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkSamplerCreateFlags</name>;</type>
- <type category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineLayoutCreateFlags</name>;</type>
+ <type requires="VkPipelineLayoutCreateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineLayoutCreateFlags</name>;</type>
<type requires="VkPipelineCacheCreateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineCacheCreateFlags</name>;</type>
- <type category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineDepthStencilStateCreateFlags</name>;</type>
+ <type requires="VkPipelineDepthStencilStateCreateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineDepthStencilStateCreateFlags</name>;</type>
<type category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineDynamicStateCreateFlags</name>;</type>
- <type category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineColorBlendStateCreateFlags</name>;</type>
+ <type requires="VkPipelineColorBlendStateCreateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineColorBlendStateCreateFlags</name>;</type>
<type category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineMultisampleStateCreateFlags</name>;</type>
<type category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineRasterizationStateCreateFlags</name>;</type>
<type category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineViewportStateCreateFlags</name>;</type>
@@ -206,7 +279,7 @@ typedef void <name>CAMetalLayer</name>;
<type requires="VkPipelineShaderStageCreateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineShaderStageCreateFlags</name>;</type>
<type requires="VkDescriptorSetLayoutCreateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkDescriptorSetLayoutCreateFlags</name>;</type>
<type category="bitmask">typedef <type>VkFlags</type> <name>VkBufferViewCreateFlags</name>;</type>
- <type category="bitmask">typedef <type>VkFlags</type> <name>VkInstanceCreateFlags</name>;</type>
+ <type requires="VkInstanceCreateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkInstanceCreateFlags</name>;</type>
<type category="bitmask">typedef <type>VkFlags</type> <name>VkDeviceCreateFlags</name>;</type>
<type requires="VkDeviceQueueCreateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkDeviceQueueCreateFlags</name>;</type>
<type requires="VkQueueFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkQueueFlags</name>;</type>
@@ -222,13 +295,12 @@ typedef void <name>CAMetalLayer</name>;
<type requires="VkPipelineCreateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineCreateFlags</name>;</type>
<type requires="VkColorComponentFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkColorComponentFlags</name>;</type>
<type requires="VkFenceCreateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkFenceCreateFlags</name>;</type>
- <comment>When VkSemaphoreCreateFlagBits is first extended, need to add a requires= attribute for it to VkSemaphoreCreateFlags</comment>
<type category="bitmask">typedef <type>VkFlags</type> <name>VkSemaphoreCreateFlags</name>;</type>
<type requires="VkFormatFeatureFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkFormatFeatureFlags</name>;</type>
<type requires="VkQueryControlFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkQueryControlFlags</name>;</type>
<type requires="VkQueryResultFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkQueryResultFlags</name>;</type>
- <type requires="VkShaderModuleCreateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkShaderModuleCreateFlags</name>;</type>
- <type category="bitmask">typedef <type>VkFlags</type> <name>VkEventCreateFlags</name>;</type>
+ <type category="bitmask">typedef <type>VkFlags</type> <name>VkShaderModuleCreateFlags</name>;</type>
+ <type requires="VkEventCreateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkEventCreateFlags</name>;</type>
<type requires="VkCommandPoolCreateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkCommandPoolCreateFlags</name>;</type>
<type requires="VkCommandPoolResetFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkCommandPoolResetFlags</name>;</type>
<type requires="VkCommandBufferResetFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkCommandBufferResetFlags</name>;</type>
@@ -256,11 +328,13 @@ typedef void <name>CAMetalLayer</name>;
<type category="bitmask" name="VkGeometryInstanceFlagsNV" alias="VkGeometryInstanceFlagsKHR"/>
<type requires="VkBuildAccelerationStructureFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkBuildAccelerationStructureFlagsKHR</name>;</type>
<type category="bitmask" name="VkBuildAccelerationStructureFlagsNV" alias="VkBuildAccelerationStructureFlagsKHR"/>
- <type requires="VkPrivateDataSlotCreateFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkPrivateDataSlotCreateFlagsEXT</name>;</type>
+ <type category="bitmask">typedef <type>VkFlags</type> <name>VkPrivateDataSlotCreateFlags</name>;</type>
+ <type category="bitmask" name="VkPrivateDataSlotCreateFlagsEXT" alias="VkPrivateDataSlotCreateFlags"/>
<type requires="VkAccelerationStructureCreateFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkAccelerationStructureCreateFlagsKHR</name>;</type>
<type category="bitmask">typedef <type>VkFlags</type> <name>VkDescriptorUpdateTemplateCreateFlags</name>;</type>
<type category="bitmask" name="VkDescriptorUpdateTemplateCreateFlagsKHR" alias="VkDescriptorUpdateTemplateCreateFlags"/>
- <type requires="VkPipelineCreationFeedbackFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineCreationFeedbackFlagsEXT</name>;</type>
+ <type requires="VkPipelineCreationFeedbackFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineCreationFeedbackFlags</name>;</type>
+ <type category="bitmask" name="VkPipelineCreationFeedbackFlagsEXT" alias="VkPipelineCreationFeedbackFlags"/>
<type requires="VkPerformanceCounterDescriptionFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkPerformanceCounterDescriptionFlagsKHR</name>;</type>
<type requires="VkAcquireProfilingLockFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkAcquireProfilingLockFlagsKHR</name>;</type>
<type requires="VkSemaphoreWaitFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkSemaphoreWaitFlags</name>;</type>
@@ -268,6 +342,17 @@ typedef void <name>CAMetalLayer</name>;
<type requires="VkPipelineCompilerControlFlagBitsAMD" category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineCompilerControlFlagsAMD</name>;</type>
<type requires="VkShaderCorePropertiesFlagBitsAMD" category="bitmask">typedef <type>VkFlags</type> <name>VkShaderCorePropertiesFlagsAMD</name>;</type>
<type requires="VkDeviceDiagnosticsConfigFlagBitsNV" category="bitmask">typedef <type>VkFlags</type> <name>VkDeviceDiagnosticsConfigFlagsNV</name>;</type>
+ <type bitvalues="VkAccessFlagBits2" category="bitmask">typedef <type>VkFlags64</type> <name>VkAccessFlags2</name>;</type>
+ <type category="bitmask" name="VkAccessFlags2KHR" alias="VkAccessFlags2"/>
+ <type bitvalues="VkPipelineStageFlagBits2" category="bitmask">typedef <type>VkFlags64</type> <name>VkPipelineStageFlags2</name>;</type>
+ <type category="bitmask" name="VkPipelineStageFlags2KHR" alias="VkPipelineStageFlags2"/>
+ <type category="bitmask">typedef <type>VkFlags</type> <name>VkAccelerationStructureMotionInfoFlagsNV</name>;</type>
+ <type category="bitmask">typedef <type>VkFlags</type> <name>VkAccelerationStructureMotionInstanceFlagsNV</name>;</type>
+ <type bitvalues="VkFormatFeatureFlagBits2" category="bitmask">typedef <type>VkFlags64</type> <name>VkFormatFeatureFlags2</name>;</type>
+ <type category="bitmask" name="VkFormatFeatureFlags2KHR" alias="VkFormatFeatureFlags2"/>
+ <type requires="VkRenderingFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkRenderingFlags</name>;</type>
+ <type category="bitmask" name="VkRenderingFlagsKHR" alias="VkRenderingFlags"/>
+
<comment>WSI extensions</comment>
<type requires="VkCompositeAlphaFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkCompositeAlphaFlagsKHR</name>;</type>
@@ -289,6 +374,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="bitmask">typedef <type>VkFlags</type> <name>VkImagePipeSurfaceCreateFlagsFUCHSIA</name>;</type>
<type category="bitmask">typedef <type>VkFlags</type> <name>VkStreamDescriptorSurfaceCreateFlagsGGP</name>;</type>
<type category="bitmask">typedef <type>VkFlags</type> <name>VkHeadlessSurfaceCreateFlagsEXT</name>;</type>
+ <type category="bitmask">typedef <type>VkFlags</type> <name>VkScreenSurfaceCreateFlagsQNX</name>;</type>
<type requires="VkPeerMemoryFeatureFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkPeerMemoryFeatureFlags</name>;</type>
<type category="bitmask" name="VkPeerMemoryFeatureFlagsKHR" alias="VkPeerMemoryFeatureFlags"/>
<type requires="VkMemoryAllocateFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkMemoryAllocateFlags</name>;</type>
@@ -337,7 +423,54 @@ typedef void <name>CAMetalLayer</name>;
<type category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineRasterizationStateStreamCreateFlagsEXT</name>;</type>
<type category="bitmask">typedef <type>VkFlags</type> <name>VkPipelineRasterizationDepthClipStateCreateFlagsEXT</name>;</type>
<type requires="VkSwapchainImageUsageFlagBitsANDROID" category="bitmask">typedef <type>VkFlags</type> <name>VkSwapchainImageUsageFlagsANDROID</name>;</type>
- <type requires="VkToolPurposeFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkToolPurposeFlagsEXT</name>;</type>
+ <type requires="VkToolPurposeFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkToolPurposeFlags</name>;</type>
+ <type category="bitmask" name="VkToolPurposeFlagsEXT" alias="VkToolPurposeFlags"/>
+ <type requires="VkSubmitFlagBits" category="bitmask">typedef <type>VkFlags</type> <name>VkSubmitFlags</name>;</type>
+ <type category="bitmask" name="VkSubmitFlagsKHR" alias="VkSubmitFlags"/>
+ <type category="bitmask">typedef <type>VkFlags</type> <name>VkImageFormatConstraintsFlagsFUCHSIA</name>;</type>
+ <type requires="VkImageConstraintsInfoFlagBitsFUCHSIA" category="bitmask">typedef <type>VkFlags</type> <name>VkImageConstraintsInfoFlagsFUCHSIA</name>;</type>
+ <type requires="VkGraphicsPipelineLibraryFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkGraphicsPipelineLibraryFlagsEXT</name>;</type>
+ <type requires="VkImageCompressionFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkImageCompressionFlagsEXT</name>;</type>
+ <type requires="VkImageCompressionFixedRateFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkImageCompressionFixedRateFlagsEXT</name>;</type>
+ <type requires="VkExportMetalObjectTypeFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkExportMetalObjectTypeFlagsEXT</name>;</type>
+
+ <comment>Video Core extension</comment>
+ <type requires="VkVideoCodecOperationFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoCodecOperationFlagsKHR</name>;</type>
+ <type requires="VkVideoCapabilityFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoCapabilityFlagsKHR</name>;</type>
+ <type requires="VkVideoSessionCreateFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoSessionCreateFlagsKHR</name>;</type>
+ <type category="bitmask">typedef <type>VkFlags</type> <name>VkVideoBeginCodingFlagsKHR</name>;</type>
+ <type category="bitmask">typedef <type>VkFlags</type> <name>VkVideoEndCodingFlagsKHR</name>;</type>
+ <type requires="VkVideoCodingQualityPresetFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoCodingQualityPresetFlagsKHR</name>;</type>
+ <type requires="VkVideoCodingControlFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoCodingControlFlagsKHR</name>;</type>
+
+ <comment>Video Decode Core extension</comment>
+ <type requires="VkVideoDecodeCapabilityFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoDecodeCapabilityFlagsKHR</name>;</type>
+ <type requires="VkVideoDecodeFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoDecodeFlagsKHR</name>;</type>
+
+ <comment>Video Decode H.264 extension</comment>
+ <type requires="VkVideoDecodeH264PictureLayoutFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoDecodeH264PictureLayoutFlagsEXT</name>;</type>
+
+ <comment>Video Encode Core extension</comment>
+ <type requires="VkVideoEncodeFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoEncodeFlagsKHR</name>;</type>
+ <type requires="VkVideoEncodeCapabilityFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoEncodeCapabilityFlagsKHR</name>;</type>
+ <type requires="VkVideoEncodeRateControlFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoEncodeRateControlFlagsKHR</name>;</type>
+ <type requires="VkVideoEncodeRateControlModeFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoEncodeRateControlModeFlagsKHR</name>;</type>
+ <type requires="VkVideoChromaSubsamplingFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoChromaSubsamplingFlagsKHR</name>;</type>
+ <type requires="VkVideoComponentBitDepthFlagBitsKHR" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoComponentBitDepthFlagsKHR</name>;</type>
+
+ <comment>Video Encode H.264 extension</comment>
+ <type requires="VkVideoEncodeH264CapabilityFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoEncodeH264CapabilityFlagsEXT</name>;</type>
+ <type requires="VkVideoEncodeH264InputModeFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoEncodeH264InputModeFlagsEXT</name>;</type>
+ <type requires="VkVideoEncodeH264OutputModeFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoEncodeH264OutputModeFlagsEXT</name>;</type>
+ <type requires="VkVideoEncodeH264RateControlStructureFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoEncodeH264RateControlStructureFlagsEXT</name>;</type>
+
+ <comment>Video Encode H.265 extension</comment>
+ <type requires="VkVideoEncodeH265CapabilityFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoEncodeH265CapabilityFlagsEXT</name>;</type>
+ <type requires="VkVideoEncodeH265InputModeFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoEncodeH265InputModeFlagsEXT</name>;</type>
+ <type requires="VkVideoEncodeH265OutputModeFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoEncodeH265OutputModeFlagsEXT</name>;</type>
+ <type requires="VkVideoEncodeH265RateControlStructureFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoEncodeH265RateControlStructureFlagsEXT</name>;</type>
+ <type requires="VkVideoEncodeH265CtbSizeFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoEncodeH265CtbSizeFlagsEXT</name>;</type>
+ <type requires="VkVideoEncodeH265TransformBlockSizeFlagBitsEXT" category="bitmask">typedef <type>VkFlags</type> <name>VkVideoEncodeH265TransformBlockSizeFlagsEXT</name>;</type>
<comment>Types which can be void pointers or class pointers, selected at compile time</comment>
<type category="handle" objtypeenum="VK_OBJECT_TYPE_INSTANCE"><type>VK_DEFINE_HANDLE</type>(<name>VkInstance</name>)</type>
@@ -374,8 +507,12 @@ typedef void <name>CAMetalLayer</name>;
<type category="handle" parent="VkDevice" objtypeenum="VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkAccelerationStructureKHR</name>)</type>
<type category="handle" parent="VkDevice" objtypeenum="VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkAccelerationStructureNV</name>)</type>
<type category="handle" parent="VkDevice" objtypeenum="VK_OBJECT_TYPE_PERFORMANCE_CONFIGURATION_INTEL"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkPerformanceConfigurationINTEL</name>)</type>
+ <type category="handle" parent="VkDevice" objtypeenum="VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkBufferCollectionFUCHSIA</name>)</type>
<type category="handle" parent="VkDevice" objtypeenum="VK_OBJECT_TYPE_DEFERRED_OPERATION_KHR"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkDeferredOperationKHR</name>)</type>
- <type category="handle" parent="VkDevice" objtypeenum="VK_OBJECT_TYPE_PRIVATE_DATA_SLOT_EXT"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkPrivateDataSlotEXT</name>)</type>
+ <type category="handle" parent="VkDevice" objtypeenum="VK_OBJECT_TYPE_PRIVATE_DATA_SLOT"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkPrivateDataSlot</name>)</type>
+ <type category="handle" name="VkPrivateDataSlotEXT" alias="VkPrivateDataSlot"/>
+ <type category="handle" parent="VkDevice" objtypeenum="VK_OBJECT_TYPE_CU_MODULE_NVX"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkCuModuleNVX</name>)</type>
+ <type category="handle" parent="VkDevice" objtypeenum="VK_OBJECT_TYPE_CU_FUNCTION_NVX"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkCuFunctionNVX</name>)</type>
<comment>WSI extensions</comment>
<type category="handle" parent="VkPhysicalDevice" objtypeenum="VK_OBJECT_TYPE_DISPLAY_KHR"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkDisplayKHR</name>)</type>
@@ -385,6 +522,10 @@ typedef void <name>CAMetalLayer</name>;
<type category="handle" parent="VkInstance" objtypeenum="VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkDebugReportCallbackEXT</name>)</type>
<type category="handle" parent="VkInstance" objtypeenum="VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkDebugUtilsMessengerEXT</name>)</type>
+ <comment>Video extensions</comment>
+ <type category="handle" parent="VkDevice" objtypeenum="VK_OBJECT_TYPE_VIDEO_SESSION_KHR"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkVideoSessionKHR</name>)</type>
+ <type category="handle" parent="VkVideoSessionKHR" objtypeenum="VK_OBJECT_TYPE_VIDEO_SESSION_PARAMETERS_KHR"><type>VK_DEFINE_NON_DISPATCHABLE_HANDLE</type>(<name>VkVideoSessionParametersKHR</name>)</type>
+
<comment>Types generated from corresponding enums tags below</comment>
<type name="VkAttachmentLoadOp" category="enum"/>
<type name="VkAttachmentStoreOp" category="enum"/>
@@ -463,13 +604,16 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkDescriptorPoolCreateFlagBits" category="enum"/>
<type name="VkDependencyFlagBits" category="enum"/>
<type name="VkObjectType" category="enum"/>
- <comment>When VkSemaphoreCreateFlagBits is first extended, need to add a type enum tag for it here</comment>
+ <type name="VkEventCreateFlagBits" category="enum"/>
+ <type name="VkPipelineLayoutCreateFlagBits" category="enum"/>
+ <type name="VkSemaphoreCreateFlagBits" category="enum"/>
<comment>Extensions</comment>
<type name="VkIndirectCommandsLayoutUsageFlagBitsNV" category="enum"/>
<type name="VkIndirectCommandsTokenTypeNV" category="enum"/>
<type name="VkIndirectStateFlagBitsNV" category="enum"/>
- <type name="VkPrivateDataSlotCreateFlagBitsEXT" category="enum"/>
+ <type name="VkPrivateDataSlotCreateFlagBits" category="enum"/>
+ <type category="enum" name="VkPrivateDataSlotCreateFlagBitsEXT" alias="VkPrivateDataSlotCreateFlagBits"/>
<type name="VkDescriptorUpdateTemplateType" category="enum"/>
<type category="enum" name="VkDescriptorUpdateTemplateTypeKHR" alias="VkDescriptorUpdateTemplateType"/>
<type name="VkViewportCoordinateSwizzleNV" category="enum"/>
@@ -481,7 +625,8 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkCoverageReductionModeNV" category="enum"/>
<type name="VkValidationCacheHeaderVersionEXT" category="enum"/>
<type name="VkShaderInfoTypeAMD" category="enum"/>
- <type name="VkQueueGlobalPriorityEXT" category="enum"/>
+ <type name="VkQueueGlobalPriorityKHR" category="enum"/>
+ <type name="VkQueueGlobalPriorityEXT" category="enum" alias="VkQueueGlobalPriorityKHR"/>
<type name="VkTimeDomainEXT" category="enum"/>
<type name="VkConservativeRasterizationModeEXT" category="enum"/>
<type name="VkResolveModeFlagBits" category="enum"/>
@@ -515,7 +660,8 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkScopeNV" category="enum"/>
<type name="VkComponentTypeNV" category="enum"/>
<type name="VkDeviceDiagnosticsConfigFlagBitsNV" category="enum"/>
- <type name="VkPipelineCreationFeedbackFlagBitsEXT" category="enum"/>
+ <type name="VkPipelineCreationFeedbackFlagBits" category="enum"/>
+ <type category="enum" name="VkPipelineCreationFeedbackFlagBitsEXT" alias="VkPipelineCreationFeedbackFlagBits"/>
<type name="VkPerformanceCounterScopeKHR" category="enum"/>
<type name="VkPerformanceCounterUnitKHR" category="enum"/>
<type name="VkPerformanceCounterStorageKHR" category="enum"/>
@@ -532,9 +678,29 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkShaderModuleCreateFlagBits" category="enum"/>
<type name="VkPipelineCompilerControlFlagBitsAMD" category="enum"/>
<type name="VkShaderCorePropertiesFlagBitsAMD" category="enum"/>
- <type name="VkToolPurposeFlagBitsEXT" category="enum"/>
+ <type name="VkToolPurposeFlagBits" category="enum"/>
+ <type category="enum" name="VkToolPurposeFlagBitsEXT" alias="VkToolPurposeFlagBits"/>
<type name="VkFragmentShadingRateNV" category="enum"/>
<type name="VkFragmentShadingRateTypeNV" category="enum"/>
+ <type name="VkSubpassMergeStatusEXT" category="enum"/>
+ <type name="VkAccessFlagBits2" category="enum"/>
+ <type category="enum" name="VkAccessFlagBits2KHR" alias="VkAccessFlagBits2"/>
+ <type name="VkPipelineStageFlagBits2" category="enum"/>
+ <type category="enum" name="VkPipelineStageFlagBits2KHR" alias="VkPipelineStageFlagBits2"/>
+ <type name="VkProvokingVertexModeEXT" category="enum"/>
+ <type name="VkImageFormatConstraintsFlagBitsFUCHSIA" category="enum"/>
+ <type name="VkImageConstraintsInfoFlagBitsFUCHSIA" category="enum"/>
+ <type name="VkFormatFeatureFlagBits2" category="enum"/>
+ <type category="enum" name="VkFormatFeatureFlagBits2KHR" alias="VkFormatFeatureFlagBits2"/>
+ <type name="VkRenderingFlagBits" category="enum"/>
+ <type category="enum" name="VkRenderingFlagBitsKHR" alias="VkRenderingFlagBits"/>
+ <type name="VkPipelineDepthStencilStateCreateFlagBits" category="enum"/>
+ <type name="VkPipelineColorBlendStateCreateFlagBits" category="enum"/>
+ <type name="VkImageCompressionFlagBitsEXT" category="enum"/>
+ <type name="VkImageCompressionFixedRateFlagBitsEXT" category="enum"/>
+ <type name="VkExportMetalObjectTypeFlagBitsEXT" category="enum"/>
+ <type name="VkPipelineRobustnessBufferBehaviorEXT" category="enum"/>
+ <type name="VkPipelineRobustnessImageBehaviorEXT" category="enum"/>
<comment>WSI extensions</comment>
<type name="VkColorSpaceKHR" category="enum"/>
@@ -596,6 +762,9 @@ typedef void <name>CAMetalLayer</name>;
<type category="enum" name="VkShaderFloatControlsIndependenceKHR" alias="VkShaderFloatControlsIndependence"/>
<type name="VkSwapchainImageUsageFlagBitsANDROID" category="enum"/>
<type name="VkFragmentShadingRateCombinerOpKHR" category="enum"/>
+ <type name="VkSubmitFlagBits" category="enum"/>
+ <type category="enum" name="VkSubmitFlagBitsKHR" alias="VkSubmitFlagBits"/>
+ <type name="VkGraphicsPipelineLibraryFlagBitsEXT" category="enum"/>
<comment>Enumerated types in the header, but not used by the API</comment>
<type name="VkVendorId" category="enum"/>
@@ -605,6 +774,45 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkCoarseSampleOrderTypeNV" category="enum"/>
<type name="VkPipelineExecutableStatisticFormatKHR" category="enum"/>
+ <comment>Video Core extensions</comment>
+ <type name="VkVideoCodecOperationFlagBitsKHR" category="enum"/>
+ <type name="VkVideoChromaSubsamplingFlagBitsKHR" category="enum"/>
+ <type name="VkVideoComponentBitDepthFlagBitsKHR" category="enum"/>
+ <type name="VkVideoCapabilityFlagBitsKHR" category="enum"/>
+ <type name="VkVideoSessionCreateFlagBitsKHR" category="enum"/>
+ <type name="VkVideoCodingQualityPresetFlagBitsKHR" category="enum"/>
+ <type name="VkVideoCodingControlFlagBitsKHR" category="enum"/>
+ <type name="VkQueryResultStatusKHR" category="enum"/>
+
+ <comment>Video Decode extensions</comment>
+ <type name="VkVideoDecodeCapabilityFlagBitsKHR" category="enum"/>
+ <type name="VkVideoDecodeFlagBitsKHR" category="enum"/>
+
+ <comment>Video H.264 Decode extensions</comment>
+ <type name="VkVideoDecodeH264PictureLayoutFlagBitsEXT" category="enum"/>
+
+ <comment>Video H.265 Decode extensions</comment>
+
+ <comment>Video Encode extensions</comment>
+ <type name="VkVideoEncodeFlagBitsKHR" category="enum"/>
+ <type name="VkVideoEncodeCapabilityFlagBitsKHR" category="enum"/>
+ <type name="VkVideoEncodeRateControlFlagBitsKHR" category="enum"/>
+ <type name="VkVideoEncodeRateControlModeFlagBitsKHR" category="enum"/>
+
+ <comment>Video H.264 Encode extensions</comment>
+ <type name="VkVideoEncodeH264CapabilityFlagBitsEXT" category="enum"/>
+ <type name="VkVideoEncodeH264InputModeFlagBitsEXT" category="enum"/>
+ <type name="VkVideoEncodeH264OutputModeFlagBitsEXT" category="enum"/>
+ <type name="VkVideoEncodeH264RateControlStructureFlagBitsEXT" category="enum"/>
+
+ <comment>Video H.265 Encode extensions</comment>
+ <type name="VkVideoEncodeH265CapabilityFlagBitsEXT" category="enum"/>
+ <type name="VkVideoEncodeH265InputModeFlagBitsEXT" category="enum"/>
+ <type name="VkVideoEncodeH265OutputModeFlagBitsEXT" category="enum"/>
+ <type name="VkVideoEncodeH265RateControlStructureFlagBitsEXT" category="enum"/>
+ <type name="VkVideoEncodeH265CtbSizeFlagBitsEXT" category="enum"/>
+ <type name="VkVideoEncodeH265TransformBlockSizeFlagBitsEXT" category="enum"/>
+
<comment>The PFN_vk*Function types are used by VkAllocationCallbacks below</comment>
<type category="funcpointer">typedef void (VKAPI_PTR *<name>PFN_vkInternalAllocationNotification</name>)(
<type>void</type>* pUserData,
@@ -708,15 +916,15 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkComponentSwizzle</type> <name>a</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceProperties" returnedonly="true">
- <member><type>uint32_t</type> <name>apiVersion</name></member>
- <member><type>uint32_t</type> <name>driverVersion</name></member>
- <member><type>uint32_t</type> <name>vendorID</name></member>
- <member><type>uint32_t</type> <name>deviceID</name></member>
- <member><type>VkPhysicalDeviceType</type> <name>deviceType</name></member>
- <member><type>char</type> <name>deviceName</name>[<enum>VK_MAX_PHYSICAL_DEVICE_NAME_SIZE</enum>]</member>
- <member><type>uint8_t</type> <name>pipelineCacheUUID</name>[<enum>VK_UUID_SIZE</enum>]</member>
- <member><type>VkPhysicalDeviceLimits</type> <name>limits</name></member>
- <member><type>VkPhysicalDeviceSparseProperties</type> <name>sparseProperties</name></member>
+ <member limittype="noauto"><type>uint32_t</type> <name>apiVersion</name></member>
+ <member limittype="noauto"><type>uint32_t</type> <name>driverVersion</name></member>
+ <member limittype="noauto"><type>uint32_t</type> <name>vendorID</name></member>
+ <member limittype="noauto"><type>uint32_t</type> <name>deviceID</name></member>
+ <member limittype="noauto"><type>VkPhysicalDeviceType</type> <name>deviceType</name></member>
+ <member limittype="noauto"><type>char</type> <name>deviceName</name>[<enum>VK_MAX_PHYSICAL_DEVICE_NAME_SIZE</enum>]</member>
+ <member limittype="noauto"><type>uint8_t</type> <name>pipelineCacheUUID</name>[<enum>VK_UUID_SIZE</enum>]</member>
+ <member limittype="struct"><type>VkPhysicalDeviceLimits</type> <name>limits</name></member>
+ <member limittype="struct"><type>VkPhysicalDeviceSparseProperties</type> <name>sparseProperties</name></member>
</type>
<type category="struct" name="VkExtensionProperties" returnedonly="true">
<member><type>char</type> <name>extensionName</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]<comment>extension name</comment></member>
@@ -776,10 +984,10 @@ typedef void <name>CAMetalLayer</name>;
<member len="enabledExtensionCount,null-terminated">const <type>char</type>* const* <name>ppEnabledExtensionNames</name><comment>Extension names to be enabled</comment></member>
</type>
<type category="struct" name="VkQueueFamilyProperties" returnedonly="true">
- <member optional="true"><type>VkQueueFlags</type> <name>queueFlags</name><comment>Queue flags</comment></member>
- <member><type>uint32_t</type> <name>queueCount</name></member>
- <member><type>uint32_t</type> <name>timestampValidBits</name></member>
- <member><type>VkExtent3D</type> <name>minImageTransferGranularity</name><comment>Minimum alignment requirement for image transfers</comment></member>
+ <member optional="true" limittype="bitmask"><type>VkQueueFlags</type> <name>queueFlags</name><comment>Queue flags</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>queueCount</name></member>
+ <member limittype="bits"><type>uint32_t</type> <name>timestampValidBits</name></member>
+ <member limittype="min,mul"><type>VkExtent3D</type> <name>minImageTransferGranularity</name><comment>Minimum alignment requirement for image transfers</comment></member>
</type>
<type category="struct" name="VkPhysicalDeviceMemoryProperties" returnedonly="true">
<member><type>uint32_t</type> <name>memoryTypeCount</name></member>
@@ -799,9 +1007,9 @@ typedef void <name>CAMetalLayer</name>;
<member><type>uint32_t</type> <name>memoryTypeBits</name><comment>Bitmask of the allowed memory type indices into memoryTypes[] for this object</comment></member>
</type>
<type category="struct" name="VkSparseImageFormatProperties" returnedonly="true">
- <member optional="true"><type>VkImageAspectFlags</type> <name>aspectMask</name></member>
- <member><type>VkExtent3D</type> <name>imageGranularity</name></member>
- <member optional="true"><type>VkSparseImageFormatFlags</type> <name>flags</name></member>
+ <member limittype="bitmask" optional="true"><type>VkImageAspectFlags</type> <name>aspectMask</name></member>
+ <member limittype="min,mul"><type>VkExtent3D</type> <name>imageGranularity</name></member>
+ <member limittype="bitmask" optional="true"><type>VkSparseImageFormatFlags</type> <name>flags</name></member>
</type>
<type category="struct" name="VkSparseImageMemoryRequirements" returnedonly="true">
<member><type>VkSparseImageFormatProperties</type> <name>formatProperties</name></member>
@@ -826,9 +1034,9 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkDeviceSize</type> <name>size</name><comment>Size of the range within the memory object</comment></member>
</type>
<type category="struct" name="VkFormatProperties" returnedonly="true">
- <member optional="true"><type>VkFormatFeatureFlags</type> <name>linearTilingFeatures</name><comment>Format features in case of linear tiling</comment></member>
- <member optional="true"><type>VkFormatFeatureFlags</type> <name>optimalTilingFeatures</name><comment>Format features in case of optimal tiling</comment></member>
- <member optional="true"><type>VkFormatFeatureFlags</type> <name>bufferFeatures</name><comment>Format features supported by buffers</comment></member>
+ <member optional="true" limittype="bitmask"><type>VkFormatFeatureFlags</type> <name>linearTilingFeatures</name><comment>Format features in case of linear tiling</comment></member>
+ <member optional="true" limittype="bitmask"><type>VkFormatFeatureFlags</type> <name>optimalTilingFeatures</name><comment>Format features in case of optimal tiling</comment></member>
+ <member optional="true" limittype="bitmask"><type>VkFormatFeatureFlags</type> <name>bufferFeatures</name><comment>Format features supported by buffers</comment></member>
</type>
<type category="struct" name="VkImageFormatProperties" returnedonly="true">
<member><type>VkExtent3D</type> <name>maxExtent</name><comment>max image dimensions for this resource type</comment></member>
@@ -883,7 +1091,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkBufferViewCreateInfo">
<member values="VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member optional="true"><type>VkBufferViewCreateFlags</type><name>flags</name></member>
+ <member optional="true"><type>VkBufferViewCreateFlags</type> <name>flags</name></member>
<member><type>VkBuffer</type> <name>buffer</name></member>
<member><type>VkFormat</type> <name>format</name><comment>Optionally specifies format of elements</comment></member>
<member><type>VkDeviceSize</type> <name>offset</name><comment>Specified in bytes</comment></member>
@@ -980,7 +1188,7 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkDeviceSize</type> <name>size</name><comment>Specified in bytes</comment></member>
<member optional="true"><type>VkDeviceMemory</type> <name>memory</name></member>
<member><type>VkDeviceSize</type> <name>memoryOffset</name><comment>Specified in bytes</comment></member>
- <member optional="true"><type>VkSparseMemoryBindFlags</type><name>flags</name></member>
+ <member optional="true"><type>VkSparseMemoryBindFlags</type> <name>flags</name></member>
</type>
<type category="struct" name="VkSparseImageMemoryBind">
<member><type>VkImageSubresource</type> <name>subresource</name></member>
@@ -988,7 +1196,7 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkExtent3D</type> <name>extent</name></member>
<member optional="true"><type>VkDeviceMemory</type> <name>memory</name></member>
<member><type>VkDeviceSize</type> <name>memoryOffset</name><comment>Specified in bytes</comment></member>
- <member optional="true"><type>VkSparseMemoryBindFlags</type><name>flags</name></member>
+ <member optional="true"><type>VkSparseMemoryBindFlags</type> <name>flags</name></member>
</type>
<type category="struct" name="VkSparseBufferMemoryBindInfo">
<member><type>VkBuffer</type> <name>buffer</name></member>
@@ -1047,9 +1255,9 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkOffset3D</type> <name>dstOffset</name></member>
<member><type>VkExtent3D</type> <name>extent</name></member>
</type>
- <type category="struct" name="VkShaderModuleCreateInfo">
+ <type category="struct" name="VkShaderModuleCreateInfo" structextends="VkPipelineShaderStageCreateInfo">
<member values="VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member noautovalidity="true" optional="true">const <type>void</type>* <name>pNext</name><comment>noautovalidity because this structure can be either an explicit parameter, or passed in a pNext chain</comment></member>
<member optional="true"><type>VkShaderModuleCreateFlags</type> <name>flags</name></member>
<member><type>size_t</type> <name>codeSize</name><comment>Specified in bytes</comment></member>
<member len="latexmath:[\textrm{codeSize} \over 4]" altlen="codeSize / 4">const <type>uint32_t</type>* <name>pCode</name><comment>Binary code of size codeSize</comment></member>
@@ -1077,7 +1285,7 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member optional="true"><type>VkDescriptorPoolCreateFlags</type> <name>flags</name></member>
<member><type>uint32_t</type> <name>maxSets</name></member>
- <member><type>uint32_t</type> <name>poolSizeCount</name></member>
+ <member optional="true"><type>uint32_t</type> <name>poolSizeCount</name></member>
<member len="poolSizeCount">const <type>VkDescriptorPoolSize</type>* <name>pPoolSizes</name></member>
</type>
<type category="struct" name="VkDescriptorSetAllocateInfo">
@@ -1103,7 +1311,7 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member optional="true"><type>VkPipelineShaderStageCreateFlags</type> <name>flags</name></member>
<member><type>VkShaderStageFlagBits</type> <name>stage</name><comment>Shader stage</comment></member>
- <member><type>VkShaderModule</type> <name>module</name><comment>Module containing entry point</comment></member>
+ <member optional="true"><type>VkShaderModule</type> <name>module</name><comment>Module containing entry point</comment></member>
<member len="null-terminated">const <type>char</type>* <name>pName</name><comment>Null-terminated entry point name</comment></member>
<member optional="true">const <type>VkSpecializationInfo</type>* <name>pSpecializationInfo</name></member>
</type>
@@ -1238,20 +1446,20 @@ typedef void <name>CAMetalLayer</name>;
<member values="VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member optional="true"><type>VkPipelineCreateFlags</type> <name>flags</name><comment>Pipeline creation flags</comment></member>
- <member><type>uint32_t</type> <name>stageCount</name></member>
- <member len="stageCount">const <type>VkPipelineShaderStageCreateInfo</type>* <name>pStages</name><comment>One entry for each active shader stage</comment></member>
+ <member noautovalidity="true" optional="true"><type>uint32_t</type> <name>stageCount</name></member>
+ <member noautovalidity="true" len="stageCount" optional="true">const <type>VkPipelineShaderStageCreateInfo</type>* <name>pStages</name><comment>One entry for each active shader stage</comment></member>
<member noautovalidity="true" optional="true">const <type>VkPipelineVertexInputStateCreateInfo</type>* <name>pVertexInputState</name></member>
<member noautovalidity="true" optional="true">const <type>VkPipelineInputAssemblyStateCreateInfo</type>* <name>pInputAssemblyState</name></member>
<member noautovalidity="true" optional="true">const <type>VkPipelineTessellationStateCreateInfo</type>* <name>pTessellationState</name></member>
<member noautovalidity="true" optional="true">const <type>VkPipelineViewportStateCreateInfo</type>* <name>pViewportState</name></member>
- <member>const <type>VkPipelineRasterizationStateCreateInfo</type>* <name>pRasterizationState</name></member>
+ <member noautovalidity="true" optional="true">const <type>VkPipelineRasterizationStateCreateInfo</type>* <name>pRasterizationState</name></member>
<member noautovalidity="true" optional="true">const <type>VkPipelineMultisampleStateCreateInfo</type>* <name>pMultisampleState</name></member>
<member noautovalidity="true" optional="true">const <type>VkPipelineDepthStencilStateCreateInfo</type>* <name>pDepthStencilState</name></member>
<member noautovalidity="true" optional="true">const <type>VkPipelineColorBlendStateCreateInfo</type>* <name>pColorBlendState</name></member>
<member optional="true">const <type>VkPipelineDynamicStateCreateInfo</type>* <name>pDynamicState</name></member>
- <member><type>VkPipelineLayout</type> <name>layout</name><comment>Interface layout of the pipeline</comment></member>
- <member><type>VkRenderPass</type> <name>renderPass</name></member>
- <member><type>uint32_t</type> <name>subpass</name></member>
+ <member noautovalidity="true" optional="true"><type>VkPipelineLayout</type> <name>layout</name><comment>Interface layout of the pipeline</comment></member>
+ <member noautovalidity="true" optional="true"><type>VkRenderPass</type> <name>renderPass</name></member>
+ <member noautovalidity="true"><type>uint32_t</type> <name>subpass</name></member>
<member noautovalidity="true" optional="true"><type>VkPipeline</type> <name>basePipelineHandle</name><comment>If VK_PIPELINE_CREATE_DERIVATIVE_BIT is set and this value is nonzero, it specifies the handle of the base pipeline this is a derivative of</comment></member>
<member><type>int32_t</type> <name>basePipelineIndex</name><comment>If VK_PIPELINE_CREATE_DERIVATIVE_BIT is set and this value is not -1, it specifies an index into pCreateInfos of the base pipeline this is a derivative of</comment></member>
</type>
@@ -1262,6 +1470,14 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true"><type>size_t</type> <name>initialDataSize</name><comment>Size of initial data to populate cache, in bytes</comment></member>
<member len="initialDataSize">const <type>void</type>* <name>pInitialData</name><comment>Initial data to populate cache</comment></member>
</type>
+ <type category="struct" name="VkPipelineCacheHeaderVersionOne">
+ <comment>The fields in this structure are non-normative since structure packing is implementation-defined in C. The specification defines the normative layout.</comment>
+ <member><type>uint32_t</type> <name>headerSize</name></member>
+ <member><type>VkPipelineCacheHeaderVersion</type> <name>headerVersion</name></member>
+ <member><type>uint32_t</type> <name>vendorID</name></member>
+ <member><type>uint32_t</type> <name>deviceID</name></member>
+ <member><type>uint8_t</type> <name>pipelineCacheUUID</name>[<enum>VK_UUID_SIZE</enum>]</member>
+ </type>
<type category="struct" name="VkPushConstantRange">
<member><type>VkShaderStageFlags</type> <name>stageFlags</name><comment>Which stages use the range</comment></member>
<member><type>uint32_t</type> <name>offset</name><comment>Start of the range, in bytes</comment></member>
@@ -1272,7 +1488,7 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member optional="true"><type>VkPipelineLayoutCreateFlags</type> <name>flags</name></member>
<member optional="true"><type>uint32_t</type> <name>setLayoutCount</name><comment>Number of descriptor sets interfaced by the pipeline</comment></member>
- <member len="setLayoutCount">const <type>VkDescriptorSetLayout</type>* <name>pSetLayouts</name><comment>Array of setCount number of descriptor set layout objects defining the layout of the</comment></member>
+ <member optional="false,true" len="setLayoutCount">const <type>VkDescriptorSetLayout</type>* <name>pSetLayouts</name><comment>Array of setCount number of descriptor set layout objects defining the layout of the</comment></member>
<member optional="true"><type>uint32_t</type> <name>pushConstantRangeCount</name><comment>Number of push-constant ranges used by the pipeline</comment></member>
<member len="pushConstantRangeCount">const <type>VkPushConstantRange</type>* <name>pPushConstantRanges</name><comment>Array of pushConstantRangeCount number of ranges used by various shader stages</comment></member>
</type>
@@ -1332,7 +1548,7 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkFramebuffer</type> <name>framebuffer</name></member>
<member><type>VkRect2D</type> <name>renderArea</name></member>
<member optional="true"><type>uint32_t</type> <name>clearValueCount</name></member>
- <member len="clearValueCount">const <type>VkClearValue</type>* <name>pClearValues</name></member>
+ <member len="clearValueCount" noautovalidity="true">const <type>VkClearValue</type>* <name>pClearValues</name></member>
</type>
<type category="union" name="VkClearColorValue" comment="// Union allowing specification of floating point, integer, or unsigned integer color data. Actual value selected is based on image/attachment being cleared.">
<member><type>float</type> <name>float32</name>[4]</member>
@@ -1344,13 +1560,13 @@ typedef void <name>CAMetalLayer</name>;
<member><type>uint32_t</type> <name>stencil</name></member>
</type>
<type category="union" name="VkClearValue" comment="// Union allowing specification of color or depth and stencil values. Actual value selected is based on attachment being cleared.">
- <member><type>VkClearColorValue</type> <name>color</name></member>
+ <member noautovalidity="true"><type>VkClearColorValue</type> <name>color</name></member>
<member><type>VkClearDepthStencilValue</type> <name>depthStencil</name></member>
</type>
<type category="struct" name="VkClearAttachment">
<member><type>VkImageAspectFlags</type> <name>aspectMask</name></member>
<member><type>uint32_t</type> <name>colorAttachment</name></member>
- <member><type>VkClearValue</type> <name>clearValue</name></member>
+ <member noautovalidity="true"><type>VkClearValue</type> <name>clearValue</name></member>
</type>
<type category="struct" name="VkAttachmentDescription">
<member optional="true"><type>VkAttachmentDescriptionFlags</type> <name>flags</name></member>
@@ -1382,8 +1598,8 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkSubpassDependency">
<member><type>uint32_t</type> <name>srcSubpass</name></member>
<member><type>uint32_t</type> <name>dstSubpass</name></member>
- <member><type>VkPipelineStageFlags</type> <name>srcStageMask</name></member>
- <member><type>VkPipelineStageFlags</type> <name>dstStageMask</name></member>
+ <member optional="true"><type>VkPipelineStageFlags</type> <name>srcStageMask</name></member>
+ <member optional="true"><type>VkPipelineStageFlags</type> <name>dstStageMask</name></member>
<member optional="true"><type>VkAccessFlags</type> <name>srcAccessMask</name><comment>Memory accesses from the source of the dependency to synchronize</comment></member>
<member optional="true"><type>VkAccessFlags</type> <name>dstAccessMask</name><comment>Memory accesses from the destination of the dependency to synchronize</comment></member>
<member optional="true"><type>VkDependencyFlags</type> <name>dependencyFlags</name></member>
@@ -1420,7 +1636,7 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkBool32</type> <name>dualSrcBlend</name><comment>blend operations which take two sources</comment></member>
<member><type>VkBool32</type> <name>logicOp</name><comment>logic operations</comment></member>
<member><type>VkBool32</type> <name>multiDrawIndirect</name><comment>multi draw indirect</comment></member>
- <member><type>VkBool32</type> <name>drawIndirectFirstInstance</name><comment>indirect draws can use non-zero firstInstance</comment></member>
+ <member><type>VkBool32</type> <name>drawIndirectFirstInstance</name><comment>indirect drawing can use non-zero firstInstance</comment></member>
<member><type>VkBool32</type> <name>depthClamp</name><comment>depth clamping</comment></member>
<member><type>VkBool32</type> <name>depthBiasClamp</name><comment>depth bias clamping</comment></member>
<member><type>VkBool32</type> <name>fillModeNonSolid</name><comment>point and wireframe fill modes</comment></member>
@@ -1467,128 +1683,128 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkBool32</type> <name>inheritedQueries</name><comment>Queries may be inherited from primary to secondary command buffers</comment></member>
</type>
<type category="struct" name="VkPhysicalDeviceSparseProperties" returnedonly="true">
- <member><type>VkBool32</type> <name>residencyStandard2DBlockShape</name><comment>Sparse resources support: GPU will access all 2D (single sample) sparse resources using the standard sparse image block shapes (based on pixel format)</comment></member>
- <member><type>VkBool32</type> <name>residencyStandard2DMultisampleBlockShape</name><comment>Sparse resources support: GPU will access all 2D (multisample) sparse resources using the standard sparse image block shapes (based on pixel format)</comment></member>
- <member><type>VkBool32</type> <name>residencyStandard3DBlockShape</name><comment>Sparse resources support: GPU will access all 3D sparse resources using the standard sparse image block shapes (based on pixel format)</comment></member>
- <member><type>VkBool32</type> <name>residencyAlignedMipSize</name><comment>Sparse resources support: Images with mip level dimensions that are NOT a multiple of the sparse image block dimensions will be placed in the mip tail</comment></member>
- <member><type>VkBool32</type> <name>residencyNonResidentStrict</name><comment>Sparse resources support: GPU can consistently access non-resident regions of a resource, all reads return as if data is 0, writes are discarded</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>residencyStandard2DBlockShape</name><comment>Sparse resources support: GPU will access all 2D (single sample) sparse resources using the standard sparse image block shapes (based on pixel format)</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>residencyStandard2DMultisampleBlockShape</name><comment>Sparse resources support: GPU will access all 2D (multisample) sparse resources using the standard sparse image block shapes (based on pixel format)</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>residencyStandard3DBlockShape</name><comment>Sparse resources support: GPU will access all 3D sparse resources using the standard sparse image block shapes (based on pixel format)</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>residencyAlignedMipSize</name><comment>Sparse resources support: Images with mip level dimensions that are NOT a multiple of the sparse image block dimensions will be placed in the mip tail</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>residencyNonResidentStrict</name><comment>Sparse resources support: GPU can consistently access non-resident regions of a resource, all reads return as if data is 0, writes are discarded</comment></member>
</type>
<type category="struct" name="VkPhysicalDeviceLimits" returnedonly="true">
<comment>resource maximum sizes</comment>
- <member><type>uint32_t</type> <name>maxImageDimension1D</name><comment>max 1D image dimension</comment></member>
- <member><type>uint32_t</type> <name>maxImageDimension2D</name><comment>max 2D image dimension</comment></member>
- <member><type>uint32_t</type> <name>maxImageDimension3D</name><comment>max 3D image dimension</comment></member>
- <member><type>uint32_t</type> <name>maxImageDimensionCube</name><comment>max cubemap image dimension</comment></member>
- <member><type>uint32_t</type> <name>maxImageArrayLayers</name><comment>max layers for image arrays</comment></member>
- <member><type>uint32_t</type> <name>maxTexelBufferElements</name><comment>max texel buffer size (fstexels)</comment></member>
- <member><type>uint32_t</type> <name>maxUniformBufferRange</name><comment>max uniform buffer range (bytes)</comment></member>
- <member><type>uint32_t</type> <name>maxStorageBufferRange</name><comment>max storage buffer range (bytes)</comment></member>
- <member><type>uint32_t</type> <name>maxPushConstantsSize</name><comment>max size of the push constants pool (bytes)</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxImageDimension1D</name><comment>max 1D image dimension</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxImageDimension2D</name><comment>max 2D image dimension</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxImageDimension3D</name><comment>max 3D image dimension</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxImageDimensionCube</name><comment>max cubemap image dimension</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxImageArrayLayers</name><comment>max layers for image arrays</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTexelBufferElements</name><comment>max texel buffer size (fstexels)</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxUniformBufferRange</name><comment>max uniform buffer range (bytes)</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxStorageBufferRange</name><comment>max storage buffer range (bytes)</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPushConstantsSize</name><comment>max size of the push constants pool (bytes)</comment></member>
<comment>memory limits</comment>
- <member><type>uint32_t</type> <name>maxMemoryAllocationCount</name><comment>max number of device memory allocations supported</comment></member>
- <member><type>uint32_t</type> <name>maxSamplerAllocationCount</name><comment>max number of samplers that can be allocated on a device</comment></member>
- <member><type>VkDeviceSize</type> <name>bufferImageGranularity</name><comment>Granularity (in bytes) at which buffers and images can be bound to adjacent memory for simultaneous usage</comment></member>
- <member><type>VkDeviceSize</type> <name>sparseAddressSpaceSize</name><comment>Total address space available for sparse allocations (bytes)</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxMemoryAllocationCount</name><comment>max number of device memory allocations supported</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxSamplerAllocationCount</name><comment>max number of samplers that can be allocated on a device</comment></member>
+ <member limittype="min,mul"><type>VkDeviceSize</type> <name>bufferImageGranularity</name><comment>Granularity (in bytes) at which buffers and images can be bound to adjacent memory for simultaneous usage</comment></member>
+ <member limittype="max"><type>VkDeviceSize</type> <name>sparseAddressSpaceSize</name><comment>Total address space available for sparse allocations (bytes)</comment></member>
<comment>descriptor set limits</comment>
- <member><type>uint32_t</type> <name>maxBoundDescriptorSets</name><comment>max number of descriptors sets that can be bound to a pipeline</comment></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorSamplers</name><comment>max number of samplers allowed per-stage in a descriptor set</comment></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorUniformBuffers</name><comment>max number of uniform buffers allowed per-stage in a descriptor set</comment></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorStorageBuffers</name><comment>max number of storage buffers allowed per-stage in a descriptor set</comment></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorSampledImages</name><comment>max number of sampled images allowed per-stage in a descriptor set</comment></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorStorageImages</name><comment>max number of storage images allowed per-stage in a descriptor set</comment></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorInputAttachments</name><comment>max number of input attachments allowed per-stage in a descriptor set</comment></member>
- <member><type>uint32_t</type> <name>maxPerStageResources</name><comment>max number of resources allowed by a single stage</comment></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetSamplers</name><comment>max number of samplers allowed in all stages in a descriptor set</comment></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUniformBuffers</name><comment>max number of uniform buffers allowed in all stages in a descriptor set</comment></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUniformBuffersDynamic</name><comment>max number of dynamic uniform buffers allowed in all stages in a descriptor set</comment></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetStorageBuffers</name><comment>max number of storage buffers allowed in all stages in a descriptor set</comment></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetStorageBuffersDynamic</name><comment>max number of dynamic storage buffers allowed in all stages in a descriptor set</comment></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetSampledImages</name><comment>max number of sampled images allowed in all stages in a descriptor set</comment></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetStorageImages</name><comment>max number of storage images allowed in all stages in a descriptor set</comment></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetInputAttachments</name><comment>max number of input attachments allowed in all stages in a descriptor set</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxBoundDescriptorSets</name><comment>max number of descriptors sets that can be bound to a pipeline</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorSamplers</name><comment>max number of samplers allowed per-stage in a descriptor set</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUniformBuffers</name><comment>max number of uniform buffers allowed per-stage in a descriptor set</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorStorageBuffers</name><comment>max number of storage buffers allowed per-stage in a descriptor set</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorSampledImages</name><comment>max number of sampled images allowed per-stage in a descriptor set</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorStorageImages</name><comment>max number of storage images allowed per-stage in a descriptor set</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorInputAttachments</name><comment>max number of input attachments allowed per-stage in a descriptor set</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageResources</name><comment>max number of resources allowed by a single stage</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetSamplers</name><comment>max number of samplers allowed in all stages in a descriptor set</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUniformBuffers</name><comment>max number of uniform buffers allowed in all stages in a descriptor set</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUniformBuffersDynamic</name><comment>max number of dynamic uniform buffers allowed in all stages in a descriptor set</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetStorageBuffers</name><comment>max number of storage buffers allowed in all stages in a descriptor set</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetStorageBuffersDynamic</name><comment>max number of dynamic storage buffers allowed in all stages in a descriptor set</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetSampledImages</name><comment>max number of sampled images allowed in all stages in a descriptor set</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetStorageImages</name><comment>max number of storage images allowed in all stages in a descriptor set</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetInputAttachments</name><comment>max number of input attachments allowed in all stages in a descriptor set</comment></member>
<comment>vertex stage limits</comment>
- <member><type>uint32_t</type> <name>maxVertexInputAttributes</name><comment>max number of vertex input attribute slots</comment></member>
- <member><type>uint32_t</type> <name>maxVertexInputBindings</name><comment>max number of vertex input binding slots</comment></member>
- <member><type>uint32_t</type> <name>maxVertexInputAttributeOffset</name><comment>max vertex input attribute offset added to vertex buffer offset</comment></member>
- <member><type>uint32_t</type> <name>maxVertexInputBindingStride</name><comment>max vertex input binding stride</comment></member>
- <member><type>uint32_t</type> <name>maxVertexOutputComponents</name><comment>max number of output components written by vertex shader</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxVertexInputAttributes</name><comment>max number of vertex input attribute slots</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxVertexInputBindings</name><comment>max number of vertex input binding slots</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxVertexInputAttributeOffset</name><comment>max vertex input attribute offset added to vertex buffer offset</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxVertexInputBindingStride</name><comment>max vertex input binding stride</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxVertexOutputComponents</name><comment>max number of output components written by vertex shader</comment></member>
<comment>tessellation control stage limits</comment>
- <member><type>uint32_t</type> <name>maxTessellationGenerationLevel</name><comment>max level supported by tessellation primitive generator</comment></member>
- <member><type>uint32_t</type> <name>maxTessellationPatchSize</name><comment>max patch size (vertices)</comment></member>
- <member><type>uint32_t</type> <name>maxTessellationControlPerVertexInputComponents</name><comment>max number of input components per-vertex in TCS</comment></member>
- <member><type>uint32_t</type> <name>maxTessellationControlPerVertexOutputComponents</name><comment>max number of output components per-vertex in TCS</comment></member>
- <member><type>uint32_t</type> <name>maxTessellationControlPerPatchOutputComponents</name><comment>max number of output components per-patch in TCS</comment></member>
- <member><type>uint32_t</type> <name>maxTessellationControlTotalOutputComponents</name><comment>max total number of per-vertex and per-patch output components in TCS</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTessellationGenerationLevel</name><comment>max level supported by tessellation primitive generator</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTessellationPatchSize</name><comment>max patch size (vertices)</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTessellationControlPerVertexInputComponents</name><comment>max number of input components per-vertex in TCS</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTessellationControlPerVertexOutputComponents</name><comment>max number of output components per-vertex in TCS</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTessellationControlPerPatchOutputComponents</name><comment>max number of output components per-patch in TCS</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTessellationControlTotalOutputComponents</name><comment>max total number of per-vertex and per-patch output components in TCS</comment></member>
<comment>tessellation evaluation stage limits</comment>
- <member><type>uint32_t</type> <name>maxTessellationEvaluationInputComponents</name><comment>max number of input components per vertex in TES</comment></member>
- <member><type>uint32_t</type> <name>maxTessellationEvaluationOutputComponents</name><comment>max number of output components per vertex in TES</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTessellationEvaluationInputComponents</name><comment>max number of input components per vertex in TES</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTessellationEvaluationOutputComponents</name><comment>max number of output components per vertex in TES</comment></member>
<comment>geometry stage limits</comment>
- <member><type>uint32_t</type> <name>maxGeometryShaderInvocations</name><comment>max invocation count supported in geometry shader</comment></member>
- <member><type>uint32_t</type> <name>maxGeometryInputComponents</name><comment>max number of input components read in geometry stage</comment></member>
- <member><type>uint32_t</type> <name>maxGeometryOutputComponents</name><comment>max number of output components written in geometry stage</comment></member>
- <member><type>uint32_t</type> <name>maxGeometryOutputVertices</name><comment>max number of vertices that can be emitted in geometry stage</comment></member>
- <member><type>uint32_t</type> <name>maxGeometryTotalOutputComponents</name><comment>max total number of components (all vertices) written in geometry stage</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxGeometryShaderInvocations</name><comment>max invocation count supported in geometry shader</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxGeometryInputComponents</name><comment>max number of input components read in geometry stage</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxGeometryOutputComponents</name><comment>max number of output components written in geometry stage</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxGeometryOutputVertices</name><comment>max number of vertices that can be emitted in geometry stage</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxGeometryTotalOutputComponents</name><comment>max total number of components (all vertices) written in geometry stage</comment></member>
<comment>fragment stage limits</comment>
- <member><type>uint32_t</type> <name>maxFragmentInputComponents</name><comment>max number of input components read in fragment stage</comment></member>
- <member><type>uint32_t</type> <name>maxFragmentOutputAttachments</name><comment>max number of output attachments written in fragment stage</comment></member>
- <member><type>uint32_t</type> <name>maxFragmentDualSrcAttachments</name><comment>max number of output attachments written when using dual source blending</comment></member>
- <member><type>uint32_t</type> <name>maxFragmentCombinedOutputResources</name><comment>max total number of storage buffers, storage images and output buffers</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxFragmentInputComponents</name><comment>max number of input components read in fragment stage</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxFragmentOutputAttachments</name><comment>max number of output attachments written in fragment stage</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxFragmentDualSrcAttachments</name><comment>max number of output attachments written when using dual source blending</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxFragmentCombinedOutputResources</name><comment>max total number of storage buffers, storage images and output buffers</comment></member>
<comment>compute stage limits</comment>
- <member><type>uint32_t</type> <name>maxComputeSharedMemorySize</name><comment>max total storage size of work group local storage (bytes)</comment></member>
- <member><type>uint32_t</type> <name>maxComputeWorkGroupCount</name>[3]<comment>max num of compute work groups that may be dispatched by a single command (x,y,z)</comment></member>
- <member><type>uint32_t</type> <name>maxComputeWorkGroupInvocations</name><comment>max total compute invocations in a single local work group</comment></member>
- <member><type>uint32_t</type> <name>maxComputeWorkGroupSize</name>[3]<comment>max local size of a compute work group (x,y,z)</comment></member>
- <member><type>uint32_t</type> <name>subPixelPrecisionBits</name><comment>number bits of subpixel precision in screen x and y</comment></member>
- <member><type>uint32_t</type> <name>subTexelPrecisionBits</name><comment>number bits of precision for selecting texel weights</comment></member>
- <member><type>uint32_t</type> <name>mipmapPrecisionBits</name><comment>number bits of precision for selecting mipmap weights</comment></member>
- <member><type>uint32_t</type> <name>maxDrawIndexedIndexValue</name><comment>max index value for indexed draw calls (for 32-bit indices)</comment></member>
- <member><type>uint32_t</type> <name>maxDrawIndirectCount</name><comment>max draw count for indirect draw calls</comment></member>
- <member><type>float</type> <name>maxSamplerLodBias</name><comment>max absolute sampler LOD bias</comment></member>
- <member><type>float</type> <name>maxSamplerAnisotropy</name><comment>max degree of sampler anisotropy</comment></member>
- <member><type>uint32_t</type> <name>maxViewports</name><comment>max number of active viewports</comment></member>
- <member><type>uint32_t</type> <name>maxViewportDimensions</name>[2]<comment>max viewport dimensions (x,y)</comment></member>
- <member><type>float</type> <name>viewportBoundsRange</name>[2]<comment>viewport bounds range (min,max)</comment></member>
- <member><type>uint32_t</type> <name>viewportSubPixelBits</name><comment>number bits of subpixel precision for viewport</comment></member>
- <member><type>size_t</type> <name>minMemoryMapAlignment</name><comment>min required alignment of pointers returned by MapMemory (bytes)</comment></member>
- <member><type>VkDeviceSize</type> <name>minTexelBufferOffsetAlignment</name><comment>min required alignment for texel buffer offsets (bytes) </comment></member>
- <member><type>VkDeviceSize</type> <name>minUniformBufferOffsetAlignment</name><comment>min required alignment for uniform buffer sizes and offsets (bytes)</comment></member>
- <member><type>VkDeviceSize</type> <name>minStorageBufferOffsetAlignment</name><comment>min required alignment for storage buffer offsets (bytes)</comment></member>
- <member><type>int32_t</type> <name>minTexelOffset</name><comment>min texel offset for OpTextureSampleOffset</comment></member>
- <member><type>uint32_t</type> <name>maxTexelOffset</name><comment>max texel offset for OpTextureSampleOffset</comment></member>
- <member><type>int32_t</type> <name>minTexelGatherOffset</name><comment>min texel offset for OpTextureGatherOffset</comment></member>
- <member><type>uint32_t</type> <name>maxTexelGatherOffset</name><comment>max texel offset for OpTextureGatherOffset</comment></member>
- <member><type>float</type> <name>minInterpolationOffset</name><comment>furthest negative offset for interpolateAtOffset</comment></member>
- <member><type>float</type> <name>maxInterpolationOffset</name><comment>furthest positive offset for interpolateAtOffset</comment></member>
- <member><type>uint32_t</type> <name>subPixelInterpolationOffsetBits</name><comment>number of subpixel bits for interpolateAtOffset</comment></member>
- <member><type>uint32_t</type> <name>maxFramebufferWidth</name><comment>max width for a framebuffer</comment></member>
- <member><type>uint32_t</type> <name>maxFramebufferHeight</name><comment>max height for a framebuffer</comment></member>
- <member><type>uint32_t</type> <name>maxFramebufferLayers</name><comment>max layer count for a layered framebuffer</comment></member>
- <member optional="true"><type>VkSampleCountFlags</type> <name>framebufferColorSampleCounts</name><comment>supported color sample counts for a framebuffer</comment></member>
- <member optional="true"><type>VkSampleCountFlags</type> <name>framebufferDepthSampleCounts</name><comment>supported depth sample counts for a framebuffer</comment></member>
- <member optional="true"><type>VkSampleCountFlags</type> <name>framebufferStencilSampleCounts</name><comment>supported stencil sample counts for a framebuffer</comment></member>
- <member optional="true"><type>VkSampleCountFlags</type> <name>framebufferNoAttachmentsSampleCounts</name><comment>supported sample counts for a subpass which uses no attachments</comment></member>
- <member><type>uint32_t</type> <name>maxColorAttachments</name><comment>max number of color attachments per subpass</comment></member>
- <member optional="true"><type>VkSampleCountFlags</type> <name>sampledImageColorSampleCounts</name><comment>supported color sample counts for a non-integer sampled image</comment></member>
- <member optional="true"><type>VkSampleCountFlags</type> <name>sampledImageIntegerSampleCounts</name><comment>supported sample counts for an integer image</comment></member>
- <member optional="true"><type>VkSampleCountFlags</type> <name>sampledImageDepthSampleCounts</name><comment>supported depth sample counts for a sampled image</comment></member>
- <member optional="true"><type>VkSampleCountFlags</type> <name>sampledImageStencilSampleCounts</name><comment>supported stencil sample counts for a sampled image</comment></member>
- <member optional="true"><type>VkSampleCountFlags</type> <name>storageImageSampleCounts</name><comment>supported sample counts for a storage image</comment></member>
- <member><type>uint32_t</type> <name>maxSampleMaskWords</name><comment>max number of sample mask words</comment></member>
- <member><type>VkBool32</type> <name>timestampComputeAndGraphics</name><comment>timestamps on graphics and compute queues</comment></member>
- <member><type>float</type> <name>timestampPeriod</name><comment>number of nanoseconds it takes for timestamp query value to increment by 1</comment></member>
- <member><type>uint32_t</type> <name>maxClipDistances</name><comment>max number of clip distances</comment></member>
- <member><type>uint32_t</type> <name>maxCullDistances</name><comment>max number of cull distances</comment></member>
- <member><type>uint32_t</type> <name>maxCombinedClipAndCullDistances</name><comment>max combined number of user clipping</comment></member>
- <member><type>uint32_t</type> <name>discreteQueuePriorities</name><comment>distinct queue priorities available </comment></member>
- <member><type>float</type> <name>pointSizeRange</name>[2]<comment>range (min,max) of supported point sizes</comment></member>
- <member><type>float</type> <name>lineWidthRange</name>[2]<comment>range (min,max) of supported line widths</comment></member>
- <member><type>float</type> <name>pointSizeGranularity</name><comment>granularity of supported point sizes</comment></member>
- <member><type>float</type> <name>lineWidthGranularity</name><comment>granularity of supported line widths</comment></member>
- <member><type>VkBool32</type> <name>strictLines</name><comment>line rasterization follows preferred rules</comment></member>
- <member><type>VkBool32</type> <name>standardSampleLocations</name><comment>supports standard sample locations for all supported sample counts</comment></member>
- <member><type>VkDeviceSize</type> <name>optimalBufferCopyOffsetAlignment</name><comment>optimal offset of buffer copies</comment></member>
- <member><type>VkDeviceSize</type> <name>optimalBufferCopyRowPitchAlignment</name><comment>optimal pitch of buffer copies</comment></member>
- <member><type>VkDeviceSize</type> <name>nonCoherentAtomSize</name><comment>minimum size and alignment for non-coherent host-mapped device memory access</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxComputeSharedMemorySize</name><comment>max total storage size of work group local storage (bytes)</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxComputeWorkGroupCount</name>[3]<comment>max num of compute work groups that may be dispatched by a single command (x,y,z)</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxComputeWorkGroupInvocations</name><comment>max total compute invocations in a single local work group</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxComputeWorkGroupSize</name>[3]<comment>max local size of a compute work group (x,y,z)</comment></member>
+ <member limittype="bits"><type>uint32_t</type> <name>subPixelPrecisionBits</name><comment>number bits of subpixel precision in screen x and y</comment></member>
+ <member limittype="bits"><type>uint32_t</type> <name>subTexelPrecisionBits</name><comment>number bits of precision for selecting texel weights</comment></member>
+ <member limittype="bits"><type>uint32_t</type> <name>mipmapPrecisionBits</name><comment>number bits of precision for selecting mipmap weights</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDrawIndexedIndexValue</name><comment>max index value for indexed draw calls (for 32-bit indices)</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDrawIndirectCount</name><comment>max draw count for indirect drawing calls</comment></member>
+ <member limittype="max"><type>float</type> <name>maxSamplerLodBias</name><comment>max absolute sampler LOD bias</comment></member>
+ <member limittype="max"><type>float</type> <name>maxSamplerAnisotropy</name><comment>max degree of sampler anisotropy</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxViewports</name><comment>max number of active viewports</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxViewportDimensions</name>[2]<comment>max viewport dimensions (x,y)</comment></member>
+ <member limittype="range"><type>float</type> <name>viewportBoundsRange</name>[2]<comment>viewport bounds range (min,max)</comment></member>
+ <member limittype="bits"><type>uint32_t</type> <name>viewportSubPixelBits</name><comment>number bits of subpixel precision for viewport</comment></member>
+ <member limittype="min,pot"><type>size_t</type> <name>minMemoryMapAlignment</name><comment>min required alignment of pointers returned by MapMemory (bytes)</comment></member>
+ <member limittype="min,pot"><type>VkDeviceSize</type> <name>minTexelBufferOffsetAlignment</name><comment>min required alignment for texel buffer offsets (bytes) </comment></member>
+ <member limittype="min,pot"><type>VkDeviceSize</type> <name>minUniformBufferOffsetAlignment</name><comment>min required alignment for uniform buffer sizes and offsets (bytes)</comment></member>
+ <member limittype="min,pot"><type>VkDeviceSize</type> <name>minStorageBufferOffsetAlignment</name><comment>min required alignment for storage buffer offsets (bytes)</comment></member>
+ <member limittype="min"><type>int32_t</type> <name>minTexelOffset</name><comment>min texel offset for OpTextureSampleOffset</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTexelOffset</name><comment>max texel offset for OpTextureSampleOffset</comment></member>
+ <member limittype="min"><type>int32_t</type> <name>minTexelGatherOffset</name><comment>min texel offset for OpTextureGatherOffset</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTexelGatherOffset</name><comment>max texel offset for OpTextureGatherOffset</comment></member>
+ <member limittype="min"><type>float</type> <name>minInterpolationOffset</name><comment>furthest negative offset for interpolateAtOffset</comment></member>
+ <member limittype="max"><type>float</type> <name>maxInterpolationOffset</name><comment>furthest positive offset for interpolateAtOffset</comment></member>
+ <member limittype="bits"><type>uint32_t</type> <name>subPixelInterpolationOffsetBits</name><comment>number of subpixel bits for interpolateAtOffset</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxFramebufferWidth</name><comment>max width for a framebuffer</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxFramebufferHeight</name><comment>max height for a framebuffer</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxFramebufferLayers</name><comment>max layer count for a layered framebuffer</comment></member>
+ <member limittype="bitmask" optional="true"><type>VkSampleCountFlags</type> <name>framebufferColorSampleCounts</name><comment>supported color sample counts for a framebuffer</comment></member>
+ <member limittype="bitmask" optional="true"><type>VkSampleCountFlags</type> <name>framebufferDepthSampleCounts</name><comment>supported depth sample counts for a framebuffer</comment></member>
+ <member limittype="bitmask" optional="true"><type>VkSampleCountFlags</type> <name>framebufferStencilSampleCounts</name><comment>supported stencil sample counts for a framebuffer</comment></member>
+ <member limittype="bitmask" optional="true"><type>VkSampleCountFlags</type> <name>framebufferNoAttachmentsSampleCounts</name><comment>supported sample counts for a subpass which uses no attachments</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxColorAttachments</name><comment>max number of color attachments per subpass</comment></member>
+ <member limittype="bitmask" optional="true"><type>VkSampleCountFlags</type> <name>sampledImageColorSampleCounts</name><comment>supported color sample counts for a non-integer sampled image</comment></member>
+ <member limittype="bitmask" optional="true"><type>VkSampleCountFlags</type> <name>sampledImageIntegerSampleCounts</name><comment>supported sample counts for an integer image</comment></member>
+ <member limittype="bitmask" optional="true"><type>VkSampleCountFlags</type> <name>sampledImageDepthSampleCounts</name><comment>supported depth sample counts for a sampled image</comment></member>
+ <member limittype="bitmask" optional="true"><type>VkSampleCountFlags</type> <name>sampledImageStencilSampleCounts</name><comment>supported stencil sample counts for a sampled image</comment></member>
+ <member limittype="bitmask" optional="true"><type>VkSampleCountFlags</type> <name>storageImageSampleCounts</name><comment>supported sample counts for a storage image</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxSampleMaskWords</name><comment>max number of sample mask words</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>timestampComputeAndGraphics</name><comment>timestamps on graphics and compute queues</comment></member>
+ <member limittype="min,mul"><type>float</type> <name>timestampPeriod</name><comment>number of nanoseconds it takes for timestamp query value to increment by 1</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxClipDistances</name><comment>max number of clip distances</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxCullDistances</name><comment>max number of cull distances</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxCombinedClipAndCullDistances</name><comment>max combined number of user clipping</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>discreteQueuePriorities</name><comment>distinct queue priorities available </comment></member>
+ <member limittype="range"><type>float</type> <name>pointSizeRange</name>[2]<comment>range (min,max) of supported point sizes</comment></member>
+ <member limittype="range"><type>float</type> <name>lineWidthRange</name>[2]<comment>range (min,max) of supported line widths</comment></member>
+ <member limittype="min,mul"><type>float</type> <name>pointSizeGranularity</name><comment>granularity of supported point sizes</comment></member>
+ <member limittype="min,mul"><type>float</type> <name>lineWidthGranularity</name><comment>granularity of supported line widths</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>strictLines</name><comment>line rasterization follows preferred rules</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>standardSampleLocations</name><comment>supports standard sample locations for all supported sample counts</comment></member>
+ <member limittype="min,pot"><type>VkDeviceSize</type> <name>optimalBufferCopyOffsetAlignment</name><comment>optimal offset of buffer copies</comment></member>
+ <member limittype="min,pot"><type>VkDeviceSize</type> <name>optimalBufferCopyRowPitchAlignment</name><comment>optimal pitch of buffer copies</comment></member>
+ <member limittype="min,pot"><type>VkDeviceSize</type> <name>nonCoherentAtomSize</name><comment>minimum size and alignment for non-coherent host-mapped device memory access</comment></member>
</type>
<type category="struct" name="VkSemaphoreCreateInfo">
<member values="VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO"><type>VkStructureType</type> <name>sType</name></member>
@@ -1607,7 +1823,7 @@ typedef void <name>CAMetalLayer</name>;
<member values="VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member optional="true"><type>VkFramebufferCreateFlags</type> <name>flags</name></member>
- <member><type>VkRenderPass</type> <name>renderPass</name></member>
+ <member><type>VkRenderPass</type> <name>renderPass</name></member>
<member optional="true"><type>uint32_t</type> <name>attachmentCount</name></member>
<member noautovalidity="true" len="attachmentCount">const <type>VkImageView</type>* <name>pAttachments</name></member>
<member><type>uint32_t</type> <name>width</name></member>
@@ -1632,6 +1848,15 @@ typedef void <name>CAMetalLayer</name>;
<member noautovalidity="true"><type>uint32_t</type> <name>y</name></member>
<member noautovalidity="true"><type>uint32_t</type> <name>z</name></member>
</type>
+ <type category="struct" name="VkMultiDrawInfoEXT">
+ <member><type>uint32_t</type> <name>firstVertex</name></member>
+ <member><type>uint32_t</type> <name>vertexCount</name></member>
+ </type>
+ <type category="struct" name="VkMultiDrawIndexedInfoEXT">
+ <member><type>uint32_t</type> <name>firstIndex</name></member>
+ <member><type>uint32_t</type> <name>indexCount</name></member>
+ <member><type>int32_t</type> <name>vertexOffset</name></member>
+ </type>
<type category="struct" name="VkSubmitInfo">
<member values="VK_STRUCTURE_TYPE_SUBMIT_INFO"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
@@ -1708,10 +1933,10 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkExtent2D</type> <name>minImageExtent</name><comment>Supported minimum image width and height for the surface</comment></member>
<member><type>VkExtent2D</type> <name>maxImageExtent</name><comment>Supported maximum image width and height for the surface</comment></member>
<member><type>uint32_t</type> <name>maxImageArrayLayers</name><comment>Supported maximum number of image layers for the surface</comment></member>
- <member optional="true"><type>VkSurfaceTransformFlagsKHR</type> <name>supportedTransforms</name><comment>1 or more bits representing the transforms supported</comment></member>
+ <member><type>VkSurfaceTransformFlagsKHR</type> <name>supportedTransforms</name><comment>1 or more bits representing the transforms supported</comment></member>
<member><type>VkSurfaceTransformFlagBitsKHR</type> <name>currentTransform</name><comment>The surface's current transform relative to the device's natural orientation</comment></member>
- <member optional="true"><type>VkCompositeAlphaFlagsKHR</type> <name>supportedCompositeAlpha</name><comment>1 or more bits representing the alpha compositing modes supported</comment></member>
- <member optional="true"><type>VkImageUsageFlags</type> <name>supportedUsageFlags</name><comment>Supported image usage flags for the surface</comment></member>
+ <member><type>VkCompositeAlphaFlagsKHR</type> <name>supportedCompositeAlpha</name><comment>1 or more bits representing the alpha compositing modes supported</comment></member>
+ <member><type>VkImageUsageFlags</type> <name>supportedUsageFlags</name><comment>Supported image usage flags for the surface</comment></member>
</type>
<type category="struct" name="VkAndroidSurfaceCreateInfoKHR">
<member values="VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
@@ -1772,6 +1997,13 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true"><type>VkStreamDescriptorSurfaceCreateFlagsGGP</type> <name>flags</name></member>
<member><type>GgpStreamDescriptor</type> <name>streamDescriptor</name></member>
</type>
+ <type category="struct" name="VkScreenSurfaceCreateInfoQNX">
+ <member values="VK_STRUCTURE_TYPE_SCREEN_SURFACE_CREATE_INFO_QNX"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkScreenSurfaceCreateFlagsQNX</type> <name>flags</name></member>
+ <member noautovalidity="true">struct <type>_screen_context</type>* <name>context</name></member>
+ <member noautovalidity="true">struct <type>_screen_window</type>* <name>window</name></member>
+ </type>
<type category="struct" name="VkSurfaceFormatKHR" returnedonly="true">
<member><type>VkFormat</type> <name>format</name><comment>Supported pair of rendering format</comment></member>
<member><type>VkColorSpaceKHR</type> <name>colorSpace</name><comment>and color space for the surface</comment></member>
@@ -1836,14 +2068,14 @@ typedef void <name>CAMetalLayer</name>;
<member values="VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>VkDebugReportObjectTypeEXT</type> <name>objectType</name><comment>The type of the object</comment></member>
- <member><type>uint64_t</type> <name>object</name><comment>The handle of the object, cast to uint64_t</comment></member>
+ <member objecttype="objectType"><type>uint64_t</type> <name>object</name><comment>The handle of the object, cast to uint64_t</comment></member>
<member len="null-terminated">const <type>char</type>* <name>pObjectName</name><comment>Name to apply to the object</comment></member>
</type>
<type category="struct" name="VkDebugMarkerObjectTagInfoEXT">
<member values="VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>VkDebugReportObjectTypeEXT</type> <name>objectType</name><comment>The type of the object</comment></member>
- <member><type>uint64_t</type> <name>object</name><comment>The handle of the object, cast to uint64_t</comment></member>
+ <member objecttype="objectType"><type>uint64_t</type> <name>object</name><comment>The handle of the object, cast to uint64_t</comment></member>
<member><type>uint64_t</type> <name>tagName</name><comment>The name of the tag to set on the object</comment></member>
<member><type>size_t</type> <name>tagSize</name><comment>The length in bytes of the tag data</comment></member>
<member len="tagSize">const <type>void</type>* <name>pTag</name><comment>Tag data to attach to the object</comment></member>
@@ -1852,7 +2084,7 @@ typedef void <name>CAMetalLayer</name>;
<member values="VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member len="null-terminated">const <type>char</type>* <name>pMarkerName</name><comment>Name of the debug marker</comment></member>
- <member optional="true"><type>float</type> <name>color</name>[4]<comment>Optional color for debug marker</comment></member>
+ <member><type>float</type> <name>color</name>[4]<comment>Optional color for debug marker</comment></member>
</type>
<type category="struct" name="VkDedicatedAllocationImageCreateInfoNV" structextends="VkImageCreateInfo">
<member values="VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV"><type>VkStructureType</type> <name>sType</name></member>
@@ -1898,7 +2130,7 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true">const <type>SECURITY_ATTRIBUTES</type>* <name>pAttributes</name></member>
<member optional="true"><type>DWORD</type> <name>dwAccess</name></member>
</type>
- <type category="struct" name="VkWin32KeyedMutexAcquireReleaseInfoNV" structextends="VkSubmitInfo">
+ <type category="struct" name="VkWin32KeyedMutexAcquireReleaseInfoNV" structextends="VkSubmitInfo,VkSubmitInfo2">
<member values="VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member optional="true"><type>uint32_t</type> <name>acquireCount</name></member>
@@ -1910,37 +2142,45 @@ typedef void <name>CAMetalLayer</name>;
<member len="releaseCount">const <type>uint64_t</type>* <name>pReleaseKeys</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceDeviceGeneratedCommandsFeaturesNV" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_FEATURES_NV"><type>VkStructureType</type><name>sType</name></member>
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_FEATURES_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>deviceGeneratedCommands</name></member>
</type>
- <type category="struct" name="VkDevicePrivateDataCreateInfoEXT" allowduplicate="true" structextends="VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <type category="struct" name="VkDevicePrivateDataCreateInfo" allowduplicate="true" structextends="VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>uint32_t</type> <name>privateDataSlotRequestCount</name></member>
</type>
- <type category="struct" name="VkPrivateDataSlotCreateInfoEXT">
- <member values="VK_STRUCTURE_TYPE_PRIVATE_DATA_SLOT_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <type category="struct" name="VkDevicePrivateDataCreateInfoEXT" alias="VkDevicePrivateDataCreateInfo"/>
+ <type category="struct" name="VkPrivateDataSlotCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PRIVATE_DATA_SLOT_CREATE_INFO"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkPrivateDataSlotCreateFlagsEXT</type> <name>flags</name></member>
+ <member><type>VkPrivateDataSlotCreateFlags</type> <name>flags</name></member>
</type>
- <type category="struct" name="VkPhysicalDevicePrivateDataFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <type category="struct" name="VkPrivateDataSlotCreateInfoEXT" alias="VkPrivateDataSlotCreateInfo"/>
+ <type category="struct" name="VkPhysicalDevicePrivateDataFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>privateData</name></member>
</type>
+ <type category="struct" name="VkPhysicalDevicePrivateDataFeaturesEXT" alias="VkPhysicalDevicePrivateDataFeatures"/>
<type category="struct" name="VkPhysicalDeviceDeviceGeneratedCommandsPropertiesNV" structextends="VkPhysicalDeviceProperties2" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_PROPERTIES_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>maxGraphicsShaderGroupCount</name></member>
- <member><type>uint32_t</type> <name>maxIndirectSequenceCount</name></member>
- <member><type>uint32_t</type> <name>maxIndirectCommandsTokenCount</name></member>
- <member><type>uint32_t</type> <name>maxIndirectCommandsStreamCount</name></member>
- <member><type>uint32_t</type> <name>maxIndirectCommandsTokenOffset</name></member>
- <member><type>uint32_t</type> <name>maxIndirectCommandsStreamStride</name></member>
- <member><type>uint32_t</type> <name>minSequencesCountBufferOffsetAlignment</name></member>
- <member><type>uint32_t</type> <name>minSequencesIndexBufferOffsetAlignment</name></member>
- <member><type>uint32_t</type> <name>minIndirectCommandsBufferOffsetAlignment</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxGraphicsShaderGroupCount</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxIndirectSequenceCount</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxIndirectCommandsTokenCount</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxIndirectCommandsStreamCount</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxIndirectCommandsTokenOffset</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxIndirectCommandsStreamStride</name></member>
+ <member limittype="min"><type>uint32_t</type> <name>minSequencesCountBufferOffsetAlignment</name></member>
+ <member limittype="min"><type>uint32_t</type> <name>minSequencesIndexBufferOffsetAlignment</name></member>
+ <member limittype="min"><type>uint32_t</type> <name>minIndirectCommandsBufferOffsetAlignment</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceMultiDrawPropertiesEXT" structextends="VkPhysicalDeviceProperties2" returnedonly="true">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxMultiDrawCount</name></member>
</type>
<type category="struct" name="VkGraphicsShaderGroupCreateInfoNV">
<member values="VK_STRUCTURE_TYPE_GRAPHICS_SHADER_GROUP_CREATE_INFO_NV"><type>VkStructureType</type> <name>sType</name></member>
@@ -1952,8 +2192,8 @@ typedef void <name>CAMetalLayer</name>;
</type>
<type category="struct" name="VkGraphicsPipelineShaderGroupsCreateInfoNV" structextends="VkGraphicsPipelineCreateInfo">
<member values="VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_SHADER_GROUPS_CREATE_INFO_NV"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>groupCount</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>uint32_t</type> <name>groupCount</name></member>
<member len="groupCount">const <type>VkGraphicsShaderGroupCreateInfoNV</type>* <name>pGroups</name></member>
<member optional="true"><type>uint32_t</type> <name>pipelineCount</name></member>
<member len="pipelineCount">const <type>VkPipeline</type>* <name>pPipelines</name></member>
@@ -1998,7 +2238,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkIndirectCommandsLayoutCreateInfoNV">
<member values="VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkIndirectCommandsLayoutUsageFlagsNV</type> <name>flags</name></member>
+ <member optional="true"><type>VkIndirectCommandsLayoutUsageFlagsNV</type> <name>flags</name></member>
<member><type>VkPipelineBindPoint</type> <name>pipelineBindPoint</name></member>
<member><type>uint32_t</type> <name>tokenCount</name></member>
<member len="tokenCount">const <type>VkIndirectCommandsLayoutTokenNV</type>* <name>pTokens</name></member>
@@ -2018,9 +2258,9 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkDeviceSize</type> <name>preprocessOffset</name></member>
<member><type>VkDeviceSize</type> <name>preprocessSize</name></member>
<member optional="true"><type>VkBuffer</type> <name>sequencesCountBuffer</name></member>
- <member optional="true"><type>VkDeviceSize</type> <name>sequencesCountOffset</name></member>
+ <member><type>VkDeviceSize</type> <name>sequencesCountOffset</name></member>
<member optional="true"><type>VkBuffer</type> <name>sequencesIndexBuffer</name></member>
- <member optional="true"><type>VkDeviceSize</type> <name>sequencesIndexOffset</name></member>
+ <member><type>VkDeviceSize</type> <name>sequencesIndexOffset</name></member>
</type>
<type category="struct" name="VkGeneratedCommandsMemoryRequirementsInfoNV">
<member values="VK_STRUCTURE_TYPE_GENERATED_COMMANDS_MEMORY_REQUIREMENTS_INFO_NV"><type>VkStructureType</type> <name>sType</name></member>
@@ -2039,7 +2279,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceProperties2" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkPhysicalDeviceProperties</type> <name>properties</name></member>
+ <member limittype="struct"><type>VkPhysicalDeviceProperties</type> <name>properties</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceProperties2KHR" alias="VkPhysicalDeviceProperties2"/>
<type category="struct" name="VkFormatProperties2" returnedonly="true">
@@ -2067,7 +2307,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkQueueFamilyProperties2" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkQueueFamilyProperties</type> <name>queueFamilyProperties</name></member>
+ <member limittype="struct"><type>VkQueueFamilyProperties</type> <name>queueFamilyProperties</name></member>
</type>
<type category="struct" name="VkQueueFamilyProperties2KHR" alias="VkQueueFamilyProperties2"/>
<type category="struct" name="VkPhysicalDeviceMemoryProperties2" returnedonly="true">
@@ -2079,7 +2319,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkSparseImageFormatProperties2" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkSparseImageFormatProperties</type> <name>properties</name></member>
+ <member limittype="struct"><type>VkSparseImageFormatProperties</type> <name>properties</name></member>
</type>
<type category="struct" name="VkSparseImageFormatProperties2KHR" alias="VkSparseImageFormatProperties2"/>
<type category="struct" name="VkPhysicalDeviceSparseImageFormatInfo2">
@@ -2095,7 +2335,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDevicePushDescriptorPropertiesKHR" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>maxPushDescriptors</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPushDescriptors</name></member>
</type>
<type category="struct" name="VkConformanceVersion">
<member><type>uint8_t</type> <name>major</name></member>
@@ -2107,10 +2347,10 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceDriverProperties" structextends="VkPhysicalDeviceProperties2" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkDriverId</type> <name>driverID</name></member>
- <member><type>char</type> <name>driverName</name>[<enum>VK_MAX_DRIVER_NAME_SIZE</enum>]</member>
- <member><type>char</type> <name>driverInfo</name>[<enum>VK_MAX_DRIVER_INFO_SIZE</enum>]</member>
- <member><type>VkConformanceVersion</type> <name>conformanceVersion</name></member>
+ <member limittype="exact"><type>VkDriverId</type> <name>driverID</name></member>
+ <member limittype="exact"><type>char</type> <name>driverName</name>[<enum>VK_MAX_DRIVER_NAME_SIZE</enum>]</member>
+ <member limittype="exact"><type>char</type> <name>driverInfo</name>[<enum>VK_MAX_DRIVER_INFO_SIZE</enum>]</member>
+ <member limittype="exact"><type>VkConformanceVersion</type> <name>conformanceVersion</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceDriverPropertiesKHR" alias="VkPhysicalDeviceDriverProperties"/>
<type category="struct" name="VkPresentRegionsKHR" structextends="VkPresentInfoKHR">
@@ -2172,11 +2412,11 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceIDProperties" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint8_t</type> <name>deviceUUID</name>[<enum>VK_UUID_SIZE</enum>]</member>
- <member><type>uint8_t</type> <name>driverUUID</name>[<enum>VK_UUID_SIZE</enum>]</member>
- <member><type>uint8_t</type> <name>deviceLUID</name>[<enum>VK_LUID_SIZE</enum>]</member>
- <member><type>uint32_t</type> <name>deviceNodeMask</name></member>
- <member><type>VkBool32</type> <name>deviceLUIDValid</name></member>
+ <member limittype="noauto"><type>uint8_t</type> <name>deviceUUID</name>[<enum>VK_UUID_SIZE</enum>]</member>
+ <member limittype="noauto"><type>uint8_t</type> <name>driverUUID</name>[<enum>VK_UUID_SIZE</enum>]</member>
+ <member limittype="noauto"><type>uint8_t</type> <name>deviceLUID</name>[<enum>VK_LUID_SIZE</enum>]</member>
+ <member limittype="noauto"><type>uint32_t</type> <name>deviceNodeMask</name></member>
+ <member limittype="noauto"><type>VkBool32</type> <name>deviceLUIDValid</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceIDPropertiesKHR" alias="VkPhysicalDeviceIDProperties"/>
<type category="struct" name="VkExternalMemoryImageCreateInfo" structextends="VkImageCreateInfo">
@@ -2211,6 +2451,23 @@ typedef void <name>CAMetalLayer</name>;
<member><type>DWORD</type> <name>dwAccess</name></member>
<member><type>LPCWSTR</type> <name>name</name></member>
</type>
+ <type category="struct" name="VkImportMemoryZirconHandleInfoFUCHSIA" structextends="VkMemoryAllocateInfo">
+ <member values="VK_STRUCTURE_TYPE_IMPORT_MEMORY_ZIRCON_HANDLE_INFO_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkExternalMemoryHandleTypeFlagBits</type> <name>handleType</name></member>
+ <member optional="true"><type>zx_handle_t</type> <name>handle</name></member>
+ </type>
+ <type category="struct" name="VkMemoryZirconHandlePropertiesFUCHSIA" returnedonly="true">
+ <member values="VK_STRUCTURE_TYPE_MEMORY_ZIRCON_HANDLE_PROPERTIES_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>memoryTypeBits</name></member>
+ </type>
+ <type category="struct" name="VkMemoryGetZirconHandleInfoFUCHSIA">
+ <member values="VK_STRUCTURE_TYPE_MEMORY_GET_ZIRCON_HANDLE_INFO_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkDeviceMemory</type> <name>memory</name></member>
+ <member><type>VkExternalMemoryHandleTypeFlagBits</type> <name>handleType</name></member>
+ </type>
<type category="struct" name="VkMemoryWin32HandlePropertiesKHR" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
@@ -2239,7 +2496,7 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkDeviceMemory</type> <name>memory</name></member>
<member><type>VkExternalMemoryHandleTypeFlagBits</type> <name>handleType</name></member>
</type>
- <type category="struct" name="VkWin32KeyedMutexAcquireReleaseInfoKHR" structextends="VkSubmitInfo">
+ <type category="struct" name="VkWin32KeyedMutexAcquireReleaseInfoKHR" structextends="VkSubmitInfo,VkSubmitInfo2">
<member values="VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member optional="true"><type>uint32_t</type> <name>acquireCount</name></member>
@@ -2275,7 +2532,7 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member externsync="true"><type>VkSemaphore</type> <name>semaphore</name></member>
<member optional="true"><type>VkSemaphoreImportFlags</type> <name>flags</name></member>
- <member optional="true"><type>VkExternalSemaphoreHandleTypeFlagBits</type> <name>handleType</name></member>
+ <member noautovalidity="true"><type>VkExternalSemaphoreHandleTypeFlagBits</type> <name>handleType</name></member>
<member optional="true"><type>HANDLE</type> <name>handle</name></member>
<member optional="true"><type>LPCWSTR</type> <name>name</name></member>
</type>
@@ -2314,6 +2571,20 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkSemaphore</type> <name>semaphore</name></member>
<member><type>VkExternalSemaphoreHandleTypeFlagBits</type> <name>handleType</name></member>
</type>
+ <type category="struct" name="VkImportSemaphoreZirconHandleInfoFUCHSIA">
+ <member values="VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member externsync="true"><type>VkSemaphore</type> <name>semaphore</name></member>
+ <member optional="true"><type>VkSemaphoreImportFlags</type> <name>flags</name></member>
+ <member><type>VkExternalSemaphoreHandleTypeFlagBits</type> <name>handleType</name></member>
+ <member><type>zx_handle_t</type> <name>zirconHandle</name></member>
+ </type>
+ <type category="struct" name="VkSemaphoreGetZirconHandleInfoFUCHSIA">
+ <member values="VK_STRUCTURE_TYPE_SEMAPHORE_GET_ZIRCON_HANDLE_INFO_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkSemaphore</type> <name>semaphore</name></member>
+ <member><type>VkExternalSemaphoreHandleTypeFlagBits</type> <name>handleType</name></member>
+ </type>
<type category="struct" name="VkPhysicalDeviceExternalFenceInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
@@ -2339,7 +2610,7 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member externsync="true"><type>VkFence</type> <name>fence</name></member>
<member optional="true"><type>VkFenceImportFlags</type> <name>flags</name></member>
- <member optional="true"><type>VkExternalFenceHandleTypeFlagBits</type> <name>handleType</name></member>
+ <member noautovalidity="true"><type>VkExternalFenceHandleTypeFlagBits</type> <name>handleType</name></member>
<member optional="true"><type>HANDLE</type> <name>handle</name></member>
<member optional="true"><type>LPCWSTR</type> <name>name</name></member>
</type>
@@ -2381,8 +2652,8 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceMultiviewProperties" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>maxMultiviewViewCount</name><comment>max number of views in a subpass</comment></member>
- <member><type>uint32_t</type> <name>maxMultiviewInstanceIndex</name><comment>max instance index for a draw in a multiview subpass</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxMultiviewViewCount</name><comment>max number of views in a subpass</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxMultiviewInstanceIndex</name><comment>max instance index for a draw in a multiview subpass</comment></member>
</type>
<type category="struct" name="VkPhysicalDeviceMultiviewPropertiesKHR" alias="VkPhysicalDeviceMultiviewProperties"/>
<type category="struct" name="VkRenderPassMultiviewCreateInfo" structextends="VkRenderPassCreateInfo">
@@ -2405,10 +2676,10 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkExtent2D</type> <name>minImageExtent</name><comment>Supported minimum image width and height for the surface</comment></member>
<member><type>VkExtent2D</type> <name>maxImageExtent</name><comment>Supported maximum image width and height for the surface</comment></member>
<member><type>uint32_t</type> <name>maxImageArrayLayers</name><comment>Supported maximum number of image layers for the surface</comment></member>
- <member optional="true"><type>VkSurfaceTransformFlagsKHR</type> <name>supportedTransforms</name><comment>1 or more bits representing the transforms supported</comment></member>
+ <member><type>VkSurfaceTransformFlagsKHR</type> <name>supportedTransforms</name><comment>1 or more bits representing the transforms supported</comment></member>
<member><type>VkSurfaceTransformFlagBitsKHR</type> <name>currentTransform</name><comment>The surface's current transform relative to the device's natural orientation</comment></member>
- <member optional="true"><type>VkCompositeAlphaFlagsKHR</type> <name>supportedCompositeAlpha</name><comment>1 or more bits representing the alpha compositing modes supported</comment></member>
- <member optional="true"><type>VkImageUsageFlags</type> <name>supportedUsageFlags</name><comment>Supported image usage flags for the surface</comment></member>
+ <member><type>VkCompositeAlphaFlagsKHR</type> <name>supportedCompositeAlpha</name><comment>1 or more bits representing the alpha compositing modes supported</comment></member>
+ <member><type>VkImageUsageFlags</type> <name>supportedUsageFlags</name><comment>Supported image usage flags for the surface</comment></member>
<member optional="true"><type>VkSurfaceCounterFlagsEXT</type> <name>supportedSurfaceCounters</name></member>
</type>
<type category="struct" name="VkDisplayPowerInfoEXT">
@@ -2478,7 +2749,7 @@ typedef void <name>CAMetalLayer</name>;
<member len="splitInstanceBindRegionCount">const <type>VkRect2D</type>* <name>pSplitInstanceBindRegions</name></member>
</type>
<type category="struct" name="VkBindImageMemoryDeviceGroupInfoKHR" alias="VkBindImageMemoryDeviceGroupInfo"/>
- <type category="struct" name="VkDeviceGroupRenderPassBeginInfo" structextends="VkRenderPassBeginInfo">
+ <type category="struct" name="VkDeviceGroupRenderPassBeginInfo" structextends="VkRenderPassBeginInfo,VkRenderingInfo">
<member values="VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>uint32_t</type> <name>deviceMask</name></member>
@@ -2512,7 +2783,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkDeviceGroupBindSparseInfoKHR" alias="VkDeviceGroupBindSparseInfo"/>
<type category="struct" name="VkDeviceGroupPresentCapabilitiesKHR" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>uint32_t</type> <name>presentMask</name>[<enum>VK_MAX_DEVICE_GROUP_SIZE</enum>]</member>
<member><type>VkDeviceGroupPresentModeFlagsKHR</type> <name>modes</name></member>
</type>
@@ -2573,7 +2844,7 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkDescriptorUpdateTemplateType</type> <name>templateType</name></member>
<member noautovalidity="true"><type>VkDescriptorSetLayout</type> <name>descriptorSetLayout</name></member>
<member noautovalidity="true"><type>VkPipelineBindPoint</type> <name>pipelineBindPoint</name></member>
- <member noautovalidity="true"><type>VkPipelineLayout</type><name>pipelineLayout</name><comment>If used for push descriptors, this is the only allowed layout</comment></member>
+ <member noautovalidity="true"><type>VkPipelineLayout</type> <name>pipelineLayout</name><comment>If used for push descriptors, this is the only allowed layout</comment></member>
<member noautovalidity="true"><type>uint32_t</type> <name>set</name></member>
</type>
<type category="struct" name="VkDescriptorUpdateTemplateCreateInfoKHR" alias="VkDescriptorUpdateTemplateCreateInfo"/>
@@ -2581,6 +2852,22 @@ typedef void <name>CAMetalLayer</name>;
<member><type>float</type> <name>x</name></member>
<member><type>float</type> <name>y</name></member>
</type>
+ <type category="struct" name="VkPhysicalDevicePresentIdFeaturesKHR" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>presentId</name><comment>Present ID in VkPresentInfoKHR</comment></member>
+ </type>
+ <type category="struct" name="VkPresentIdKHR" structextends="VkPresentInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_PRESENT_ID_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>swapchainCount</name><comment>Copy of VkPresentInfoKHR::swapchainCount</comment></member>
+ <member len="swapchainCount" optional="true">const <type>uint64_t</type>* <name>pPresentIds</name><comment>Present ID values for each swapchain</comment></member>
+ </type>
+ <type category="struct" name="VkPhysicalDevicePresentWaitFeaturesKHR" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>presentWait</name><comment>vkWaitForPresentKHR is supported</comment></member>
+ </type>
<type category="struct" name="VkHdrMetadataEXT">
<comment>Display primary in chromaticity coordinates</comment>
<member values="VK_STRUCTURE_TYPE_HDR_METADATA_EXT"><type>VkStructureType</type> <name>sType</name></member>
@@ -2671,7 +2958,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceDiscardRectanglePropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>maxDiscardRectangles</name><comment>max number of active discard rectangles</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDiscardRectangles</name><comment>max number of active discard rectangles</comment></member>
</type>
<type category="struct" name="VkPipelineDiscardRectangleStateCreateInfoEXT" structextends="VkGraphicsPipelineCreateInfo">
<member values="VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
@@ -2684,7 +2971,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkBool32</type> <name>perViewPositionAllComponents</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>perViewPositionAllComponents</name></member>
</type>
<type category="struct" name="VkInputAttachmentAspectReference">
<member><type>uint32_t</type> <name>subpass</name></member>
@@ -2702,7 +2989,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceSurfaceInfo2KHR">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkSurfaceKHR</type> <name>surface</name></member>
+ <member optional="true"><type>VkSurfaceKHR</type> <name>surface</name></member>
</type>
<type category="struct" name="VkSurfaceCapabilities2KHR" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR"><type>VkStructureType</type> <name>sType</name></member>
@@ -2757,10 +3044,10 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceSubgroupProperties" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member noautovalidity="true"><type>uint32_t</type> <name>subgroupSize</name><comment>The size of a subgroup for this queue.</comment></member>
- <member noautovalidity="true"><type>VkShaderStageFlags</type> <name>supportedStages</name><comment>Bitfield of what shader stages support subgroup operations</comment></member>
- <member noautovalidity="true"><type>VkSubgroupFeatureFlags</type> <name>supportedOperations</name><comment>Bitfield of what subgroup operations are supported.</comment></member>
- <member noautovalidity="true"><type>VkBool32</type> <name>quadOperationsInAllStages</name><comment>Flag to specify whether quad operations are available in all stages.</comment></member>
+ <member limittype="min,pot" noautovalidity="true"><type>uint32_t</type> <name>subgroupSize</name><comment>The size of a subgroup for this queue.</comment></member>
+ <member limittype="bitmask" noautovalidity="true"><type>VkShaderStageFlags</type> <name>supportedStages</name><comment>Bitfield of what shader stages support subgroup operations</comment></member>
+ <member limittype="bitmask" noautovalidity="true"><type>VkSubgroupFeatureFlags</type> <name>supportedOperations</name><comment>Bitfield of what subgroup operations are supported.</comment></member>
+ <member limittype="bitmask" noautovalidity="true"><type>VkBool32</type> <name>quadOperationsInAllStages</name><comment>Flag to specify whether quad operations are available in all stages.</comment></member>
</type>
<type category="struct" name="VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
@@ -2774,6 +3061,12 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkBuffer</type> <name>buffer</name></member>
</type>
<type category="struct" name="VkBufferMemoryRequirementsInfo2KHR" alias="VkBufferMemoryRequirementsInfo2"/>
+ <type category="struct" name="VkDeviceBufferMemoryRequirements">
+ <member values="VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member>const <type>VkBufferCreateInfo</type>* <name>pCreateInfo</name></member>
+ </type>
+ <type category="struct" name="VkDeviceBufferMemoryRequirementsKHR" alias="VkDeviceBufferMemoryRequirements"/>
<type category="struct" name="VkImageMemoryRequirementsInfo2">
<member values="VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
@@ -2786,6 +3079,13 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkImage</type> <name>image</name></member>
</type>
<type category="struct" name="VkImageSparseMemoryRequirementsInfo2KHR" alias="VkImageSparseMemoryRequirementsInfo2"/>
+ <type category="struct" name="VkDeviceImageMemoryRequirements">
+ <member values="VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member>const <type>VkImageCreateInfo</type>* <name>pCreateInfo</name></member>
+ <member optional="true"><type>VkImageAspectFlagBits</type> <name>planeAspect</name></member>
+ </type>
+ <type category="struct" name="VkDeviceImageMemoryRequirementsKHR" alias="VkDeviceImageMemoryRequirements"/>
<type category="struct" name="VkMemoryRequirements2" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
@@ -2801,7 +3101,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDevicePointClippingProperties" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkPointClippingBehavior</type> <name>pointClippingBehavior</name></member>
+ <member limittype="exact"><type>VkPointClippingBehavior</type> <name>pointClippingBehavior</name></member>
</type>
<type category="struct" name="VkPhysicalDevicePointClippingPropertiesKHR" alias="VkPhysicalDevicePointClippingProperties"/>
<type category="struct" name="VkMemoryDedicatedRequirements" returnedonly="true" structextends="VkMemoryRequirements2">
@@ -2898,7 +3198,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceProtectedMemoryProperties" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkBool32</type> <name>protectedNoFault</name></member>
+ <member limittype="exact"><type>VkBool32</type> <name>protectedNoFault</name></member>
</type>
<type category="struct" name="VkDeviceQueueInfo2">
<member values="VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2"><type>VkStructureType</type> <name>sType</name></member>
@@ -2917,18 +3217,18 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceSamplerFilterMinmaxProperties" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkBool32</type> <name>filterMinmaxSingleComponentFormats</name></member>
- <member><type>VkBool32</type> <name>filterMinmaxImageComponentMapping</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>filterMinmaxSingleComponentFormats</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>filterMinmaxImageComponentMapping</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT" alias="VkPhysicalDeviceSamplerFilterMinmaxProperties"/>
<type category="struct" name="VkSampleLocationEXT">
<member><type>float</type> <name>x</name></member>
<member><type>float</type> <name>y</name></member>
</type>
- <type category="struct" name="VkSampleLocationsInfoEXT" structextends="VkImageMemoryBarrier">
+ <type category="struct" name="VkSampleLocationsInfoEXT" structextends="VkImageMemoryBarrier,VkImageMemoryBarrier2">
<member values="VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member optional="true"><type>VkSampleCountFlagBits</type> <name>sampleLocationsPerPixel</name></member>
+ <member noautovalidity="true"><type>VkSampleCountFlagBits</type> <name>sampleLocationsPerPixel</name></member>
<member><type>VkExtent2D</type> <name>sampleLocationGridSize</name></member>
<member optional="true"><type>uint32_t</type> <name>sampleLocationsCount</name></member>
<member len="sampleLocationsCount">const <type>VkSampleLocationEXT</type>* <name>pSampleLocations</name></member>
@@ -2958,11 +3258,11 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceSampleLocationsPropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkSampleCountFlags</type> <name>sampleLocationSampleCounts</name></member>
- <member><type>VkExtent2D</type> <name>maxSampleLocationGridSize</name></member>
- <member><type>float</type> <name>sampleLocationCoordinateRange</name>[2]</member>
- <member><type>uint32_t</type> <name>sampleLocationSubPixelBits</name></member>
- <member><type>VkBool32</type> <name>variableSampleLocations</name></member>
+ <member limittype="bitmask"><type>VkSampleCountFlags</type> <name>sampleLocationSampleCounts</name></member>
+ <member limittype="max"><type>VkExtent2D</type> <name>maxSampleLocationGridSize</name></member>
+ <member limittype="range"><type>float</type> <name>sampleLocationCoordinateRange</name>[2]</member>
+ <member limittype="bits"><type>uint32_t</type> <name>sampleLocationSubPixelBits</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>variableSampleLocations</name></member>
</type>
<type category="struct" name="VkMultisamplePropertiesEXT" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
@@ -2980,15 +3280,20 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>advancedBlendCoherentOperations</name></member>
</type>
+ <type category="struct" name="VkPhysicalDeviceMultiDrawFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>multiDraw</name></member>
+ </type>
<type category="struct" name="VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>advancedBlendMaxColorAttachments</name></member>
- <member><type>VkBool32</type> <name>advancedBlendIndependentBlend</name></member>
- <member><type>VkBool32</type> <name>advancedBlendNonPremultipliedSrcColor</name></member>
- <member><type>VkBool32</type> <name>advancedBlendNonPremultipliedDstColor</name></member>
- <member><type>VkBool32</type> <name>advancedBlendCorrelatedOverlap</name></member>
- <member><type>VkBool32</type> <name>advancedBlendAllOperations</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>advancedBlendMaxColorAttachments</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>advancedBlendIndependentBlend</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>advancedBlendNonPremultipliedSrcColor</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>advancedBlendNonPremultipliedDstColor</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>advancedBlendCorrelatedOverlap</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>advancedBlendAllOperations</name></member>
</type>
<type category="struct" name="VkPipelineColorBlendAdvancedStateCreateInfoEXT" structextends="VkPipelineColorBlendStateCreateInfo">
<member values="VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
@@ -2997,32 +3302,36 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkBool32</type> <name>dstPremultiplied</name></member>
<member><type>VkBlendOverlapEXT</type> <name>blendOverlap</name></member>
</type>
- <type category="struct" name="VkPhysicalDeviceInlineUniformBlockFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <type category="struct" name="VkPhysicalDeviceInlineUniformBlockFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>inlineUniformBlock</name></member>
<member><type>VkBool32</type> <name>descriptorBindingInlineUniformBlockUpdateAfterBind</name></member>
</type>
- <type category="struct" name="VkPhysicalDeviceInlineUniformBlockPropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <type category="struct" name="VkPhysicalDeviceInlineUniformBlockFeaturesEXT" alias="VkPhysicalDeviceInlineUniformBlockFeatures"/>
+ <type category="struct" name="VkPhysicalDeviceInlineUniformBlockProperties" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>maxInlineUniformBlockSize</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorInlineUniformBlocks</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetInlineUniformBlocks</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindInlineUniformBlocks</name></member>
- </type>
- <type category="struct" name="VkWriteDescriptorSetInlineUniformBlockEXT" structextends="VkWriteDescriptorSet">
- <member values="VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>dataSize</name></member>
- <member len="dataSize">const <type>void</type>* <name>pData</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxInlineUniformBlockSize</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorInlineUniformBlocks</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetInlineUniformBlocks</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindInlineUniformBlocks</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceInlineUniformBlockPropertiesEXT" alias="VkPhysicalDeviceInlineUniformBlockProperties"/>
+ <type category="struct" name="VkWriteDescriptorSetInlineUniformBlock" structextends="VkWriteDescriptorSet">
+ <member values="VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>dataSize</name></member>
+ <member len="dataSize">const <type>void</type>* <name>pData</name></member>
</type>
- <type category="struct" name="VkDescriptorPoolInlineUniformBlockCreateInfoEXT" structextends="VkDescriptorPoolCreateInfo">
- <member values="VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>maxInlineUniformBlockBindings</name></member>
+ <type category="struct" name="VkWriteDescriptorSetInlineUniformBlockEXT" alias="VkWriteDescriptorSetInlineUniformBlock"/>
+ <type category="struct" name="VkDescriptorPoolInlineUniformBlockCreateInfo" structextends="VkDescriptorPoolCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>maxInlineUniformBlockBindings</name></member>
</type>
+ <type category="struct" name="VkDescriptorPoolInlineUniformBlockCreateInfoEXT" alias="VkDescriptorPoolInlineUniformBlockCreateInfo"/>
<type category="struct" name="VkPipelineCoverageModulationStateCreateInfoNV" structextends="VkPipelineMultisampleStateCreateInfo">
<member values="VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_MODULATION_STATE_CREATE_INFO_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
@@ -3046,7 +3355,7 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true"><type>size_t</type> <name>initialDataSize</name></member>
<member len="initialDataSize">const <type>void</type>* <name>pInitialData</name></member>
</type>
- <type category="struct" name="VkShaderModuleValidationCacheCreateInfoEXT" structextends="VkShaderModuleCreateInfo">
+ <type category="struct" name="VkShaderModuleValidationCacheCreateInfoEXT" structextends="VkShaderModuleCreateInfo,VkPipelineShaderStageCreateInfo">
<member values="VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>VkValidationCacheEXT</type> <name>validationCache</name></member>
@@ -3054,10 +3363,22 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceMaintenance3Properties" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>maxPerSetDescriptors</name></member>
- <member><type>VkDeviceSize</type> <name>maxMemoryAllocationSize</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerSetDescriptors</name></member>
+ <member limittype="max"><type>VkDeviceSize</type> <name>maxMemoryAllocationSize</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceMaintenance3PropertiesKHR" alias="VkPhysicalDeviceMaintenance3Properties"/>
+ <type category="struct" name="VkPhysicalDeviceMaintenance4Features" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>maintenance4</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceMaintenance4FeaturesKHR" alias="VkPhysicalDeviceMaintenance4Features"/>
+ <type category="struct" name="VkPhysicalDeviceMaintenance4Properties" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="max"><type>VkDeviceSize</type> <name>maxBufferSize</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceMaintenance4PropertiesKHR" alias="VkPhysicalDeviceMaintenance4Properties"/>
<type category="struct" name="VkDescriptorSetLayoutSupport" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
@@ -3081,23 +3402,23 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceFloatControlsProperties" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkShaderFloatControlsIndependence</type> <name>denormBehaviorIndependence</name></member>
- <member><type>VkShaderFloatControlsIndependence</type> <name>roundingModeIndependence</name></member>
- <member><type>VkBool32</type> <name>shaderSignedZeroInfNanPreserveFloat16</name><comment>An implementation can preserve signed zero, nan, inf</comment></member>
- <member><type>VkBool32</type> <name>shaderSignedZeroInfNanPreserveFloat32</name><comment>An implementation can preserve signed zero, nan, inf</comment></member>
- <member><type>VkBool32</type> <name>shaderSignedZeroInfNanPreserveFloat64</name><comment>An implementation can preserve signed zero, nan, inf</comment></member>
- <member><type>VkBool32</type> <name>shaderDenormPreserveFloat16</name><comment>An implementation can preserve denormals</comment></member>
- <member><type>VkBool32</type> <name>shaderDenormPreserveFloat32</name><comment>An implementation can preserve denormals</comment></member>
- <member><type>VkBool32</type> <name>shaderDenormPreserveFloat64</name><comment>An implementation can preserve denormals</comment></member>
- <member><type>VkBool32</type> <name>shaderDenormFlushToZeroFloat16</name><comment>An implementation can flush to zero denormals</comment></member>
- <member><type>VkBool32</type> <name>shaderDenormFlushToZeroFloat32</name><comment>An implementation can flush to zero denormals</comment></member>
- <member><type>VkBool32</type> <name>shaderDenormFlushToZeroFloat64</name><comment>An implementation can flush to zero denormals</comment></member>
- <member><type>VkBool32</type> <name>shaderRoundingModeRTEFloat16</name><comment>An implementation can support RTE</comment></member>
- <member><type>VkBool32</type> <name>shaderRoundingModeRTEFloat32</name><comment>An implementation can support RTE</comment></member>
- <member><type>VkBool32</type> <name>shaderRoundingModeRTEFloat64</name><comment>An implementation can support RTE</comment></member>
- <member><type>VkBool32</type> <name>shaderRoundingModeRTZFloat16</name><comment>An implementation can support RTZ</comment></member>
- <member><type>VkBool32</type> <name>shaderRoundingModeRTZFloat32</name><comment>An implementation can support RTZ</comment></member>
- <member><type>VkBool32</type> <name>shaderRoundingModeRTZFloat64</name><comment>An implementation can support RTZ</comment></member>
+ <member limittype="exact"><type>VkShaderFloatControlsIndependence</type> <name>denormBehaviorIndependence</name></member>
+ <member limittype="exact"><type>VkShaderFloatControlsIndependence</type> <name>roundingModeIndependence</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderSignedZeroInfNanPreserveFloat16</name><comment>An implementation can preserve signed zero, nan, inf</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderSignedZeroInfNanPreserveFloat32</name><comment>An implementation can preserve signed zero, nan, inf</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderSignedZeroInfNanPreserveFloat64</name><comment>An implementation can preserve signed zero, nan, inf</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderDenormPreserveFloat16</name><comment>An implementation can preserve denormals</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderDenormPreserveFloat32</name><comment>An implementation can preserve denormals</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderDenormPreserveFloat64</name><comment>An implementation can preserve denormals</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderDenormFlushToZeroFloat16</name><comment>An implementation can flush to zero denormals</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderDenormFlushToZeroFloat32</name><comment>An implementation can flush to zero denormals</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderDenormFlushToZeroFloat64</name><comment>An implementation can flush to zero denormals</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderRoundingModeRTEFloat16</name><comment>An implementation can support RTE</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderRoundingModeRTEFloat32</name><comment>An implementation can support RTE</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderRoundingModeRTEFloat64</name><comment>An implementation can support RTE</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderRoundingModeRTZFloat16</name><comment>An implementation can support RTZ</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderRoundingModeRTZFloat32</name><comment>An implementation can support RTZ</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderRoundingModeRTZFloat64</name><comment>An implementation can support RTZ</comment></member>
</type>
<type category="struct" name="VkPhysicalDeviceFloatControlsPropertiesKHR" alias="VkPhysicalDeviceFloatControlsProperties"/>
<type category="struct" name="VkPhysicalDeviceHostQueryResetFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
@@ -3145,23 +3466,37 @@ typedef void <name>CAMetalLayer</name>;
<member><type>uint32_t</type> <name>numAvailableSgprs</name></member>
<member><type>uint32_t</type> <name>computeWorkGroupSize</name>[3]</member>
</type>
- <type category="struct" name="VkDeviceQueueGlobalPriorityCreateInfoEXT" structextends="VkDeviceQueueCreateInfo">
- <member values="VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <type category="struct" name="VkDeviceQueueGlobalPriorityCreateInfoKHR" structextends="VkDeviceQueueCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkQueueGlobalPriorityEXT</type> <name>globalPriority</name></member>
+ <member><type>VkQueueGlobalPriorityKHR</type> <name>globalPriority</name></member>
</type>
- <type category="struct" name="VkDebugUtilsObjectNameInfoEXT">
+ <type category="struct" name="VkDeviceQueueGlobalPriorityCreateInfoEXT" alias="VkDeviceQueueGlobalPriorityCreateInfoKHR"/>
+ <type category="struct" name="VkPhysicalDeviceGlobalPriorityQueryFeaturesKHR" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>globalPriorityQuery</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceGlobalPriorityQueryFeaturesEXT" alias="VkPhysicalDeviceGlobalPriorityQueryFeaturesKHR"/>
+ <type category="struct" name="VkQueueFamilyGlobalPriorityPropertiesKHR" structextends="VkQueueFamilyProperties2">
+ <member values="VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>priorityCount</name></member>
+ <member limittype="bitmask"><type>VkQueueGlobalPriorityKHR</type> <name>priorities</name>[<enum>VK_MAX_GLOBAL_PRIORITY_SIZE_KHR</enum>]</member>
+ </type>
+ <type category="struct" name="VkQueueFamilyGlobalPriorityPropertiesEXT" alias="VkQueueFamilyGlobalPriorityPropertiesKHR"/>
+ <type category="struct" name="VkDebugUtilsObjectNameInfoEXT" structextends="VkPipelineShaderStageCreateInfo">
<member values="VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>VkObjectType</type> <name>objectType</name></member>
- <member><type>uint64_t</type> <name>objectHandle</name></member>
+ <member objecttype="objectType"><type>uint64_t</type> <name>objectHandle</name></member>
<member optional="true" len="null-terminated">const <type>char</type>* <name>pObjectName</name></member>
</type>
<type category="struct" name="VkDebugUtilsObjectTagInfoEXT">
<member values="VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_TAG_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>VkObjectType</type> <name>objectType</name></member>
- <member><type>uint64_t</type> <name>objectHandle</name></member>
+ <member objecttype="objectType"><type>uint64_t</type> <name>objectHandle</name></member>
<member><type>uint64_t</type> <name>tagName</name></member>
<member><type>size_t</type> <name>tagSize</name></member>
<member len="tagSize">const <type>void</type>* <name>pTag</name></member>
@@ -3170,7 +3505,7 @@ typedef void <name>CAMetalLayer</name>;
<member values="VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member len="null-terminated">const <type>char</type>* <name>pLabelName</name></member>
- <member optional="true"><type>float</type> <name>color</name>[4]</member>
+ <member><type>float</type> <name>color</name>[4]</member>
</type>
<type category="struct" name="VkDebugUtilsMessengerCreateInfoEXT" allowduplicate="true" structextends="VkInstanceCreateInfo">
<member values="VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
@@ -3186,7 +3521,7 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member optional="true"><type>VkDebugUtilsMessengerCallbackDataFlagsEXT</type> <name>flags</name></member>
<member optional="true" len="null-terminated">const <type>char</type>* <name>pMessageIdName</name></member>
- <member optional="true"><type>int32_t</type> <name>messageIdNumber</name></member>
+ <member><type>int32_t</type> <name>messageIdNumber</name></member>
<member len="null-terminated">const <type>char</type>* <name>pMessage</name></member>
<member optional="true"><type>uint32_t</type> <name>queueLabelCount</name></member>
<member len="queueLabelCount">const <type>VkDebugUtilsLabelEXT</type>* <name>pQueueLabels</name></member>
@@ -3209,14 +3544,14 @@ typedef void <name>CAMetalLayer</name>;
</type>
<type category="struct" name="VkDeviceMemoryReportCallbackDataEXT" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_DEVICE_MEMORY_REPORT_CALLBACK_DATA_EXT"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkDeviceMemoryReportFlagsEXT</type> <name>flags</name></member>
<member><type>VkDeviceMemoryReportEventTypeEXT</type> <name>type</name></member>
<member><type>uint64_t</type> <name>memoryObjectId</name></member>
- <member optional="true"><type>VkDeviceSize</type> <name>size</name></member>
- <member optional="true"><type>VkObjectType</type> <name>objectType</name></member>
- <member optional="true"><type>uint64_t</type> <name>objectHandle</name></member>
- <member optional="true"><type>uint32_t</type> <name>heapIndex</name></member>
+ <member><type>VkDeviceSize</type> <name>size</name></member>
+ <member><type>VkObjectType</type> <name>objectType</name></member>
+ <member objecttype="objectType"><type>uint64_t</type> <name>objectHandle</name></member>
+ <member><type>uint32_t</type> <name>heapIndex</name></member>
</type>
<type category="struct" name="VkImportMemoryHostPointerInfoEXT" structextends="VkMemoryAllocateInfo">
<member values="VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
@@ -3232,20 +3567,20 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceExternalMemoryHostPropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkDeviceSize</type> <name>minImportedHostPointerAlignment</name></member>
+ <member limittype="min,pot"><type>VkDeviceSize</type> <name>minImportedHostPointerAlignment</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceConservativeRasterizationPropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONSERVATIVE_RASTERIZATION_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>float</type> <name>primitiveOverestimationSize</name><comment>The size in pixels the primitive is enlarged at each edge during conservative rasterization</comment></member>
- <member><type>float</type> <name>maxExtraPrimitiveOverestimationSize</name><comment>The maximum additional overestimation the client can specify in the pipeline state</comment></member>
- <member><type>float</type> <name>extraPrimitiveOverestimationSizeGranularity</name><comment>The granularity of extra overestimation sizes the implementations supports between 0 and maxExtraOverestimationSize</comment></member>
- <member><type>VkBool32</type> <name>primitiveUnderestimation</name><comment>true if the implementation supports conservative rasterization underestimation mode</comment></member>
- <member><type>VkBool32</type> <name>conservativePointAndLineRasterization</name><comment>true if conservative rasterization also applies to points and lines</comment></member>
- <member><type>VkBool32</type> <name>degenerateTrianglesRasterized</name><comment>true if degenerate triangles (those with zero area after snap) are rasterized</comment></member>
- <member><type>VkBool32</type> <name>degenerateLinesRasterized</name><comment>true if degenerate lines (those with zero length after snap) are rasterized</comment></member>
- <member><type>VkBool32</type> <name>fullyCoveredFragmentShaderInputVariable</name><comment>true if the implementation supports the FullyCoveredEXT SPIR-V builtin fragment shader input variable</comment></member>
- <member><type>VkBool32</type> <name>conservativeRasterizationPostDepthCoverage</name><comment>true if the implementation supports both conservative rasterization and post depth coverage sample coverage mask</comment></member>
+ <member limittype="exact"><type>float</type> <name>primitiveOverestimationSize</name><comment>The size in pixels the primitive is enlarged at each edge during conservative rasterization</comment></member>
+ <member limittype="max"><type>float</type> <name>maxExtraPrimitiveOverestimationSize</name><comment>The maximum additional overestimation the client can specify in the pipeline state</comment></member>
+ <member limittype="min,mul"><type>float</type> <name>extraPrimitiveOverestimationSizeGranularity</name><comment>The granularity of extra overestimation sizes the implementations supports between 0 and maxExtraOverestimationSize</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>primitiveUnderestimation</name><comment>true if the implementation supports conservative rasterization underestimation mode</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>conservativePointAndLineRasterization</name><comment>true if conservative rasterization also applies to points and lines</comment></member>
+ <member limittype="exact"><type>VkBool32</type> <name>degenerateTrianglesRasterized</name><comment>true if degenerate triangles (those with zero area after snap) are rasterized</comment></member>
+ <member limittype="exact"><type>VkBool32</type> <name>degenerateLinesRasterized</name><comment>true if degenerate lines (those with zero length after snap) are rasterized</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>fullyCoveredFragmentShaderInputVariable</name><comment>true if the implementation supports the FullyCoveredEXT SPIR-V builtin fragment shader input variable</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>conservativeRasterizationPostDepthCoverage</name><comment>true if the implementation supports both conservative rasterization and post depth coverage sample coverage mask</comment></member>
</type>
<type category="struct" name="VkCalibratedTimestampInfoEXT">
<member values="VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
@@ -3255,26 +3590,26 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceShaderCorePropertiesAMD" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_AMD"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>shaderEngineCount</name><comment>number of shader engines</comment></member>
- <member><type>uint32_t</type> <name>shaderArraysPerEngineCount</name><comment>number of shader arrays</comment></member>
- <member><type>uint32_t</type> <name>computeUnitsPerShaderArray</name><comment>number of physical CUs per shader array</comment></member>
- <member><type>uint32_t</type> <name>simdPerComputeUnit</name><comment>number of SIMDs per compute unit</comment></member>
- <member><type>uint32_t</type> <name>wavefrontsPerSimd</name><comment>number of wavefront slots in each SIMD</comment></member>
- <member><type>uint32_t</type> <name>wavefrontSize</name><comment>maximum number of threads per wavefront</comment></member>
- <member><type>uint32_t</type> <name>sgprsPerSimd</name><comment>number of physical SGPRs per SIMD</comment></member>
- <member><type>uint32_t</type> <name>minSgprAllocation</name><comment>minimum number of SGPRs that can be allocated by a wave</comment></member>
- <member><type>uint32_t</type> <name>maxSgprAllocation</name><comment>number of available SGPRs</comment></member>
- <member><type>uint32_t</type> <name>sgprAllocationGranularity</name><comment>SGPRs are allocated in groups of this size</comment></member>
- <member><type>uint32_t</type> <name>vgprsPerSimd</name><comment>number of physical VGPRs per SIMD</comment></member>
- <member><type>uint32_t</type> <name>minVgprAllocation</name><comment>minimum number of VGPRs that can be allocated by a wave</comment></member>
- <member><type>uint32_t</type> <name>maxVgprAllocation</name><comment>number of available VGPRs</comment></member>
- <member><type>uint32_t</type> <name>vgprAllocationGranularity</name><comment>VGPRs are allocated in groups of this size</comment></member>
+ <member limittype="exact"><type>uint32_t</type> <name>shaderEngineCount</name><comment>number of shader engines</comment></member>
+ <member limittype="exact"><type>uint32_t</type> <name>shaderArraysPerEngineCount</name><comment>number of shader arrays</comment></member>
+ <member limittype="exact"><type>uint32_t</type> <name>computeUnitsPerShaderArray</name><comment>number of physical CUs per shader array</comment></member>
+ <member limittype="exact"><type>uint32_t</type> <name>simdPerComputeUnit</name><comment>number of SIMDs per compute unit</comment></member>
+ <member limittype="exact"><type>uint32_t</type> <name>wavefrontsPerSimd</name><comment>number of wavefront slots in each SIMD</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>wavefrontSize</name><comment>maximum number of threads per wavefront</comment></member>
+ <member limittype="exact"><type>uint32_t</type> <name>sgprsPerSimd</name><comment>number of physical SGPRs per SIMD</comment></member>
+ <member limittype="min"><type>uint32_t</type> <name>minSgprAllocation</name><comment>minimum number of SGPRs that can be allocated by a wave</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxSgprAllocation</name><comment>number of available SGPRs</comment></member>
+ <member limittype="min,mul"><type>uint32_t</type> <name>sgprAllocationGranularity</name><comment>SGPRs are allocated in groups of this size</comment></member>
+ <member limittype="exact"><type>uint32_t</type> <name>vgprsPerSimd</name><comment>number of physical VGPRs per SIMD</comment></member>
+ <member limittype="min"><type>uint32_t</type> <name>minVgprAllocation</name><comment>minimum number of VGPRs that can be allocated by a wave</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxVgprAllocation</name><comment>number of available VGPRs</comment></member>
+ <member limittype="min,mul"><type>uint32_t</type> <name>vgprAllocationGranularity</name><comment>VGPRs are allocated in groups of this size</comment></member>
</type>
<type category="struct" name="VkPhysicalDeviceShaderCoreProperties2AMD" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_2_AMD"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name><comment>Pointer to next structure</comment></member>
- <member><type>VkShaderCorePropertiesFlagsAMD</type> <name>shaderCoreFeatures</name><comment>features supported by the shader core</comment></member>
- <member><type>uint32_t</type> <name>activeComputeUnitCount</name><comment>number of active compute units across all shader engines/arrays</comment></member>
+ <member limittype="bitmask"><type>VkShaderCorePropertiesFlagsAMD</type> <name>shaderCoreFeatures</name><comment>features supported by the shader core</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>activeComputeUnitCount</name><comment>number of active compute units across all shader engines/arrays</comment></member>
</type>
<type category="struct" name="VkPipelineRasterizationConservativeStateCreateInfoEXT" structextends="VkPipelineRasterizationStateCreateInfo">
<member values="VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
@@ -3311,29 +3646,29 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceDescriptorIndexingProperties" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>maxUpdateAfterBindDescriptorsInAllPools</name></member>
- <member><type>VkBool32</type> <name>shaderUniformBufferArrayNonUniformIndexingNative</name></member>
- <member><type>VkBool32</type> <name>shaderSampledImageArrayNonUniformIndexingNative</name></member>
- <member><type>VkBool32</type> <name>shaderStorageBufferArrayNonUniformIndexingNative</name></member>
- <member><type>VkBool32</type> <name>shaderStorageImageArrayNonUniformIndexingNative</name></member>
- <member><type>VkBool32</type> <name>shaderInputAttachmentArrayNonUniformIndexingNative</name></member>
- <member><type>VkBool32</type> <name>robustBufferAccessUpdateAfterBind</name></member>
- <member><type>VkBool32</type> <name>quadDivergentImplicitLod</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindSamplers</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindUniformBuffers</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindStorageBuffers</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindSampledImages</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindStorageImages</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindInputAttachments</name></member>
- <member><type>uint32_t</type> <name>maxPerStageUpdateAfterBindResources</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindSamplers</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindUniformBuffers</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindUniformBuffersDynamic</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindStorageBuffers</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindStorageBuffersDynamic</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindSampledImages</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindStorageImages</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindInputAttachments</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxUpdateAfterBindDescriptorsInAllPools</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderUniformBufferArrayNonUniformIndexingNative</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderSampledImageArrayNonUniformIndexingNative</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderStorageBufferArrayNonUniformIndexingNative</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderStorageImageArrayNonUniformIndexingNative</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderInputAttachmentArrayNonUniformIndexingNative</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>robustBufferAccessUpdateAfterBind</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>quadDivergentImplicitLod</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindSamplers</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindUniformBuffers</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindStorageBuffers</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindSampledImages</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindStorageImages</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindInputAttachments</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageUpdateAfterBindResources</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindSamplers</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindUniformBuffers</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindUniformBuffersDynamic</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindStorageBuffers</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindStorageBuffersDynamic</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindSampledImages</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindStorageImages</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindInputAttachments</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceDescriptorIndexingPropertiesEXT" alias="VkPhysicalDeviceDescriptorIndexingProperties"/>
<type category="struct" name="VkDescriptorSetLayoutBindingFlagsCreateInfo" structextends="VkDescriptorSetLayoutCreateInfo">
@@ -3399,12 +3734,12 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>uint32_t</type> <name>srcSubpass</name></member>
<member><type>uint32_t</type> <name>dstSubpass</name></member>
- <member><type>VkPipelineStageFlags</type> <name>srcStageMask</name></member>
- <member><type>VkPipelineStageFlags</type> <name>dstStageMask</name></member>
+ <member optional="true"><type>VkPipelineStageFlags</type> <name>srcStageMask</name></member>
+ <member optional="true"><type>VkPipelineStageFlags</type> <name>dstStageMask</name></member>
<member optional="true"><type>VkAccessFlags</type> <name>srcAccessMask</name></member>
<member optional="true"><type>VkAccessFlags</type> <name>dstAccessMask</name></member>
<member optional="true"><type>VkDependencyFlags</type> <name>dependencyFlags</name></member>
- <member optional="true"><type>int32_t</type> <name>viewOffset</name></member>
+ <member><type>int32_t</type> <name>viewOffset</name></member>
</type>
<type category="struct" name="VkSubpassDependency2KHR" alias="VkSubpassDependency2"/>
<type category="struct" name="VkRenderPassCreateInfo2">
@@ -3441,7 +3776,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceTimelineSemaphoreProperties" structextends="VkPhysicalDeviceProperties2" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint64_t</type> <name>maxTimelineSemaphoreValueDifference</name></member>
+ <member limittype="max"><type>uint64_t</type> <name>maxTimelineSemaphoreValueDifference</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceTimelineSemaphorePropertiesKHR" alias="VkPhysicalDeviceTimelineSemaphoreProperties"/>
<type category="struct" name="VkSemaphoreTypeCreateInfo" structextends="VkSemaphoreCreateInfo,VkPhysicalDeviceExternalSemaphoreInfo">
@@ -3489,15 +3824,15 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>maxVertexAttribDivisor</name><comment>max value of vertex attribute divisor</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxVertexAttribDivisor</name><comment>max value of vertex attribute divisor</comment></member>
</type>
<type category="struct" name="VkPhysicalDevicePCIBusInfoPropertiesEXT" structextends="VkPhysicalDeviceProperties2" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>pciDomain</name></member>
- <member><type>uint32_t</type> <name>pciBus</name></member>
- <member><type>uint32_t</type> <name>pciDevice</name></member>
- <member><type>uint32_t</type> <name>pciFunction</name></member>
+ <member limittype="noauto"><type>uint32_t</type> <name>pciDomain</name></member>
+ <member limittype="noauto"><type>uint32_t</type> <name>pciBus</name></member>
+ <member limittype="noauto"><type>uint32_t</type> <name>pciDevice</name></member>
+ <member limittype="noauto"><type>uint32_t</type> <name>pciFunction</name></member>
</type>
<type category="struct" name="VkImportAndroidHardwareBufferInfoANDROID" structextends="VkMemoryAllocateInfo">
<member values="VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID"><type>VkStructureType</type> <name>sType</name></member>
@@ -3587,6 +3922,22 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkBool32</type> <name>sparseImageFloat32Atomics</name></member>
<member><type>VkBool32</type> <name>sparseImageFloat32AtomicAdd</name></member>
</type>
+ <type category="struct" name="VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_2_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>shaderBufferFloat16Atomics</name></member>
+ <member><type>VkBool32</type> <name>shaderBufferFloat16AtomicAdd</name></member>
+ <member><type>VkBool32</type> <name>shaderBufferFloat16AtomicMinMax</name></member>
+ <member><type>VkBool32</type> <name>shaderBufferFloat32AtomicMinMax</name></member>
+ <member><type>VkBool32</type> <name>shaderBufferFloat64AtomicMinMax</name></member>
+ <member><type>VkBool32</type> <name>shaderSharedFloat16Atomics</name></member>
+ <member><type>VkBool32</type> <name>shaderSharedFloat16AtomicAdd</name></member>
+ <member><type>VkBool32</type> <name>shaderSharedFloat16AtomicMinMax</name></member>
+ <member><type>VkBool32</type> <name>shaderSharedFloat32AtomicMinMax</name></member>
+ <member><type>VkBool32</type> <name>shaderSharedFloat64AtomicMinMax</name></member>
+ <member><type>VkBool32</type> <name>shaderImageFloat32AtomicMinMax</name></member>
+ <member><type>VkBool32</type> <name>sparseImageFloat32AtomicMinMax</name></member>
+ </type>
<type category="struct" name="VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
@@ -3596,7 +3947,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkQueueFamilyCheckpointPropertiesNV" structextends="VkQueueFamilyProperties2" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_QUEUE_FAMILY_CHECKPOINT_PROPERTIES_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkPipelineStageFlags</type> <name>checkpointExecutionStageMask</name></member>
+ <member limittype="bitmask"><type>VkPipelineStageFlags</type> <name>checkpointExecutionStageMask</name></member>
</type>
<type category="struct" name="VkCheckpointDataNV" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_CHECKPOINT_DATA_NV"><type>VkStructureType</type> <name>sType</name></member>
@@ -3607,10 +3958,10 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceDepthStencilResolveProperties" structextends="VkPhysicalDeviceProperties2" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkResolveModeFlags</type> <name>supportedDepthResolveModes</name><comment>supported depth resolve modes</comment></member>
- <member><type>VkResolveModeFlags</type> <name>supportedStencilResolveModes</name><comment>supported stencil resolve modes</comment></member>
- <member><type>VkBool32</type> <name>independentResolveNone</name><comment>depth and stencil resolve modes can be set independently if one of them is none</comment></member>
- <member><type>VkBool32</type> <name>independentResolve</name><comment>depth and stencil resolve modes can be set independently</comment></member>
+ <member limittype="bitmask"><type>VkResolveModeFlags</type> <name>supportedDepthResolveModes</name><comment>supported depth resolve modes</comment></member>
+ <member limittype="bitmask"><type>VkResolveModeFlags</type> <name>supportedStencilResolveModes</name><comment>supported stencil resolve modes</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>independentResolveNone</name><comment>depth and stencil resolve modes can be set independently if one of them is none</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>independentResolve</name><comment>depth and stencil resolve modes can be set independently</comment></member>
</type>
<type category="struct" name="VkPhysicalDeviceDepthStencilResolvePropertiesKHR" alias="VkPhysicalDeviceDepthStencilResolveProperties"/>
<type category="struct" name="VkSubpassDescriptionDepthStencilResolve" structextends="VkSubpassDescription2">
@@ -3640,16 +3991,16 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceTransformFeedbackPropertiesEXT" structextends="VkPhysicalDeviceProperties2" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>maxTransformFeedbackStreams</name></member>
- <member><type>uint32_t</type> <name>maxTransformFeedbackBuffers</name></member>
- <member><type>VkDeviceSize</type> <name>maxTransformFeedbackBufferSize</name></member>
- <member><type>uint32_t</type> <name>maxTransformFeedbackStreamDataSize</name></member>
- <member><type>uint32_t</type> <name>maxTransformFeedbackBufferDataSize</name></member>
- <member><type>uint32_t</type> <name>maxTransformFeedbackBufferDataStride</name></member>
- <member><type>VkBool32</type> <name>transformFeedbackQueries</name></member>
- <member><type>VkBool32</type> <name>transformFeedbackStreamsLinesTriangles</name></member>
- <member><type>VkBool32</type> <name>transformFeedbackRasterizationStreamSelect</name></member>
- <member><type>VkBool32</type> <name>transformFeedbackDraw</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTransformFeedbackStreams</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTransformFeedbackBuffers</name></member>
+ <member limittype="max"><type>VkDeviceSize</type> <name>maxTransformFeedbackBufferSize</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTransformFeedbackStreamDataSize</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTransformFeedbackBufferDataSize</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTransformFeedbackBufferDataStride</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>transformFeedbackQueries</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>transformFeedbackStreamsLinesTriangles</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>transformFeedbackRasterizationStreamSelect</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>transformFeedbackDraw</name></member>
</type>
<type category="struct" name="VkPipelineRasterizationStateStreamCreateInfoEXT" structextends="VkPipelineRasterizationStateCreateInfo">
<member values="VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
@@ -3658,7 +4009,7 @@ typedef void <name>CAMetalLayer</name>;
<member><type>uint32_t</type> <name>rasterizationStream</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceRepresentativeFragmentTestFeaturesNV" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_REPRESENTATIVE_FRAGMENT_TEST_FEATURES_NV"><type>VkStructureType</type><name>sType</name></member>
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_REPRESENTATIVE_FRAGMENT_TEST_FEATURES_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>representativeFragmentTest</name></member>
</type>
@@ -3689,11 +4040,7 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkBool32</type> <name>computeDerivativeGroupQuads</name></member>
<member><type>VkBool32</type> <name>computeDerivativeGroupLinear</name></member>
</type>
- <type category="struct" name="VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_NV"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkBool32</type> <name>fragmentShaderBarycentric</name></member>
- </type>
+ <type category="struct" name="VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV" alias="VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR"/>
<type category="struct" name="VkPhysicalDeviceShaderImageFootprintFeaturesNV" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_IMAGE_FOOTPRINT_FEATURES_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
@@ -3724,9 +4071,14 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceShadingRateImagePropertiesNV" structextends="VkPhysicalDeviceProperties2" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_PROPERTIES_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkExtent2D</type> <name>shadingRateTexelSize</name></member>
- <member><type>uint32_t</type> <name>shadingRatePaletteSize</name></member>
- <member><type>uint32_t</type> <name>shadingRateMaxCoarseSamples</name></member>
+ <member limittype="exact"><type>VkExtent2D</type> <name>shadingRateTexelSize</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>shadingRatePaletteSize</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>shadingRateMaxCoarseSamples</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceInvocationMaskFeaturesHUAWEI" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INVOCATION_MASK_FEATURES_HUAWEI"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>invocationMask</name></member>
</type>
<type category="struct" name="VkCoarseSampleLocationNV">
<member><type>uint32_t</type> <name>pixelX</name></member>
@@ -3755,19 +4107,19 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceMeshShaderPropertiesNV" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>maxDrawMeshTasksCount</name></member>
- <member><type>uint32_t</type> <name>maxTaskWorkGroupInvocations</name></member>
- <member><type>uint32_t</type> <name>maxTaskWorkGroupSize</name>[3]</member>
- <member><type>uint32_t</type> <name>maxTaskTotalMemorySize</name></member>
- <member><type>uint32_t</type> <name>maxTaskOutputCount</name></member>
- <member><type>uint32_t</type> <name>maxMeshWorkGroupInvocations</name></member>
- <member><type>uint32_t</type> <name>maxMeshWorkGroupSize</name>[3]</member>
- <member><type>uint32_t</type> <name>maxMeshTotalMemorySize</name></member>
- <member><type>uint32_t</type> <name>maxMeshOutputVertices</name></member>
- <member><type>uint32_t</type> <name>maxMeshOutputPrimitives</name></member>
- <member><type>uint32_t</type> <name>maxMeshMultiviewViewCount</name></member>
- <member><type>uint32_t</type> <name>meshOutputPerVertexGranularity</name></member>
- <member><type>uint32_t</type> <name>meshOutputPerPrimitiveGranularity</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDrawMeshTasksCount</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTaskWorkGroupInvocations</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTaskWorkGroupSize</name>[3]</member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTaskTotalMemorySize</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxTaskOutputCount</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxMeshWorkGroupInvocations</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxMeshWorkGroupSize</name>[3]</member>
+ <member limittype="max"><type>uint32_t</type> <name>maxMeshTotalMemorySize</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxMeshOutputVertices</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxMeshOutputPrimitives</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxMeshMultiviewViewCount</name></member>
+ <member limittype="min,mul"><type>uint32_t</type> <name>meshOutputPerVertexGranularity</name></member>
+ <member limittype="min,mul"><type>uint32_t</type> <name>meshOutputPerPrimitiveGranularity</name></member>
</type>
<type category="struct" name="VkDrawMeshTasksIndirectCommandNV">
<member><type>uint32_t</type> <name>taskCount</name></member>
@@ -3859,7 +4211,7 @@ typedef void <name>CAMetalLayer</name>;
<member values="VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_INFO_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>VkAccelerationStructureTypeNV</type> <name>type</name></member>
- <member optional="true"><type>VkBuildAccelerationStructureFlagsNV</type><name>flags</name></member>
+ <member optional="true"><type>VkBuildAccelerationStructureFlagsNV</type> <name>flags</name></member>
<member optional="true"><type>uint32_t</type> <name>instanceCount</name></member>
<member optional="true"><type>uint32_t</type> <name>geometryCount</name></member>
<member len="geometryCount">const <type>VkGeometryNV</type>* <name>pGeometries</name></member>
@@ -3923,38 +4275,38 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceAccelerationStructurePropertiesKHR" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_PROPERTIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint64_t</type> <name>maxGeometryCount</name></member>
- <member><type>uint64_t</type> <name>maxInstanceCount</name></member>
- <member><type>uint64_t</type> <name>maxPrimitiveCount</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorAccelerationStructures</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindAccelerationStructures</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetAccelerationStructures</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindAccelerationStructures</name></member>
- <member><type>uint32_t</type> <name>minAccelerationStructureScratchOffsetAlignment</name></member>
+ <member limittype="max"><type>uint64_t</type> <name>maxGeometryCount</name></member>
+ <member limittype="max"><type>uint64_t</type> <name>maxInstanceCount</name></member>
+ <member limittype="max"><type>uint64_t</type> <name>maxPrimitiveCount</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorAccelerationStructures</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindAccelerationStructures</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetAccelerationStructures</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindAccelerationStructures</name></member>
+ <member limittype="min"><type>uint32_t</type> <name>minAccelerationStructureScratchOffsetAlignment</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceRayTracingPipelinePropertiesKHR" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>shaderGroupHandleSize</name></member>
- <member><type>uint32_t</type> <name>maxRayRecursionDepth</name></member>
- <member><type>uint32_t</type> <name>maxShaderGroupStride</name></member>
- <member><type>uint32_t</type> <name>shaderGroupBaseAlignment</name></member>
- <member><type>uint32_t</type> <name>shaderGroupHandleCaptureReplaySize</name></member>
- <member><type>uint32_t</type> <name>maxRayDispatchInvocationCount</name></member>
- <member><type>uint32_t</type> <name>shaderGroupHandleAlignment</name></member>
- <member><type>uint32_t</type> <name>maxRayHitAttributeSize</name></member>
+ <member limittype="exact"><type>uint32_t</type> <name>shaderGroupHandleSize</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxRayRecursionDepth</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxShaderGroupStride</name></member>
+ <member limittype="exact"><type>uint32_t</type> <name>shaderGroupBaseAlignment</name></member>
+ <member limittype="exact"><type>uint32_t</type> <name>shaderGroupHandleCaptureReplaySize</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxRayDispatchInvocationCount</name></member>
+ <member limittype="min,pot"><type>uint32_t</type> <name>shaderGroupHandleAlignment</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxRayHitAttributeSize</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceRayTracingPropertiesNV" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PROPERTIES_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>shaderGroupHandleSize</name></member>
- <member><type>uint32_t</type> <name>maxRecursionDepth</name></member>
- <member><type>uint32_t</type> <name>maxShaderGroupStride</name></member>
- <member><type>uint32_t</type> <name>shaderGroupBaseAlignment</name></member>
- <member><type>uint64_t</type> <name>maxGeometryCount</name></member>
- <member><type>uint64_t</type> <name>maxInstanceCount</name></member>
- <member><type>uint64_t</type> <name>maxTriangleCount</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetAccelerationStructures</name></member>
+ <member limittype="exact"><type>uint32_t</type> <name>shaderGroupHandleSize</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxRecursionDepth</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxShaderGroupStride</name></member>
+ <member limittype="exact"><type>uint32_t</type> <name>shaderGroupBaseAlignment</name></member>
+ <member limittype="max"><type>uint64_t</type> <name>maxGeometryCount</name></member>
+ <member limittype="max"><type>uint64_t</type> <name>maxInstanceCount</name></member>
+ <member limittype="max"><type>uint64_t</type> <name>maxTriangleCount</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetAccelerationStructures</name></member>
</type>
<type category="struct" name="VkStridedDeviceAddressRegionKHR">
<member optional="true"><type>VkDeviceAddress</type> <name>deviceAddress</name></member>
@@ -3966,11 +4318,33 @@ typedef void <name>CAMetalLayer</name>;
<member><type>uint32_t</type> <name>height</name></member>
<member><type>uint32_t</type> <name>depth</name></member>
</type>
+ <type category="struct" name="VkTraceRaysIndirectCommand2KHR">
+ <member><type>VkDeviceAddress</type> <name>raygenShaderRecordAddress</name></member>
+ <member><type>VkDeviceSize</type> <name>raygenShaderRecordSize</name></member>
+ <member><type>VkDeviceAddress</type> <name>missShaderBindingTableAddress</name></member>
+ <member><type>VkDeviceSize</type> <name>missShaderBindingTableSize</name></member>
+ <member><type>VkDeviceSize</type> <name>missShaderBindingTableStride</name></member>
+ <member><type>VkDeviceAddress</type> <name>hitShaderBindingTableAddress</name></member>
+ <member><type>VkDeviceSize</type> <name>hitShaderBindingTableSize</name></member>
+ <member><type>VkDeviceSize</type> <name>hitShaderBindingTableStride</name></member>
+ <member><type>VkDeviceAddress</type> <name>callableShaderBindingTableAddress</name></member>
+ <member><type>VkDeviceSize</type> <name>callableShaderBindingTableSize</name></member>
+ <member><type>VkDeviceSize</type> <name>callableShaderBindingTableStride</name></member>
+ <member><type>uint32_t</type> <name>width</name></member>
+ <member><type>uint32_t</type> <name>height</name></member>
+ <member><type>uint32_t</type> <name>depth</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceRayTracingMaintenance1FeaturesKHR" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_MAINTENANCE_1_FEATURES_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>rayTracingMaintenance1</name></member>
+ <member><type>VkBool32</type> <name>rayTracingPipelineTraceRaysIndirect2</name></member>
+ </type>
<type category="struct" name="VkDrmFormatModifierPropertiesListEXT" returnedonly="true" structextends="VkFormatProperties2">
<member values="VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member optional="true"><type>uint32_t</type> <name>drmFormatModifierCount</name></member>
- <member optional="true,false" len="drmFormatModifierCount"><type>VkDrmFormatModifierPropertiesEXT</type>* <name>pDrmFormatModifierProperties</name></member>
+ <member optional="true" len="drmFormatModifierCount"><type>VkDrmFormatModifierPropertiesEXT</type>* <name>pDrmFormatModifierProperties</name></member>
</type>
<type category="struct" name="VkDrmFormatModifierPropertiesEXT" returnedonly="true">
<member><type>uint64_t</type> <name>drmFormatModifier</name></member>
@@ -4026,26 +4400,42 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>fragmentDensityMapDeferred</name></member>
</type>
+ <type category="struct" name="VkPhysicalDeviceFragmentDensityMapOffsetFeaturesQCOM" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_OFFSET_FEATURES_QCOM"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>fragmentDensityMapOffset</name></member>
+ </type>
<type category="struct" name="VkPhysicalDeviceFragmentDensityMapPropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkExtent2D</type> <name>minFragmentDensityTexelSize</name></member>
- <member><type>VkExtent2D</type> <name>maxFragmentDensityTexelSize</name></member>
- <member><type>VkBool32</type> <name>fragmentDensityInvocations</name></member>
+ <member limittype="min"><type>VkExtent2D</type> <name>minFragmentDensityTexelSize</name></member>
+ <member limittype="max"><type>VkExtent2D</type> <name>maxFragmentDensityTexelSize</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>fragmentDensityInvocations</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceFragmentDensityMap2PropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_2_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkBool32</type> <name>subsampledLoads</name></member>
- <member><type>VkBool32</type> <name>subsampledCoarseReconstructionEarlyAccess</name></member>
- <member><type>uint32_t</type> <name>maxSubsampledArrayLayers</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetSubsampledSamplers</name></member>
+ <member limittype="exact"><type>VkBool32</type> <name>subsampledLoads</name></member>
+ <member limittype="exact"><type>VkBool32</type> <name>subsampledCoarseReconstructionEarlyAccess</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxSubsampledArrayLayers</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetSubsampledSamplers</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceFragmentDensityMapOffsetPropertiesQCOM" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_OFFSET_PROPERTIES_QCOM"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="min,mul"><type>VkExtent2D</type> <name>fragmentDensityOffsetGranularity</name></member>
</type>
<type category="struct" name="VkRenderPassFragmentDensityMapCreateInfoEXT" structextends="VkRenderPassCreateInfo,VkRenderPassCreateInfo2">
<member values="VK_STRUCTURE_TYPE_RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>VkAttachmentReference</type> <name>fragmentDensityMapAttachment</name></member>
</type>
+ <type category="struct" name="VkSubpassFragmentDensityMapOffsetEndInfoQCOM" structextends="VkSubpassEndInfo">
+ <member values="VK_STRUCTURE_TYPE_SUBPASS_FRAGMENT_DENSITY_MAP_OFFSET_END_INFO_QCOM"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>uint32_t</type> <name>fragmentDensityOffsetCount</name></member>
+ <member len="fragmentDensityOffsetCount">const <type>VkOffset2D</type>* <name>pFragmentDensityOffsets</name></member>
+ </type>
<type category="struct" name="VkPhysicalDeviceScalarBlockLayoutFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
@@ -4090,6 +4480,11 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>float</type> <name>priority</name></member>
</type>
+ <type category="struct" name="VkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PAGEABLE_DEVICE_LOCAL_MEMORY_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>pageableDeviceLocalMemory</name></member>
+ </type>
<type category="struct" name="VkPhysicalDeviceBufferDeviceAddressFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
@@ -4162,16 +4557,17 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkFramebufferAttachmentImageInfoKHR" alias="VkFramebufferAttachmentImageInfo"/>
<type category="struct" name="VkRenderPassAttachmentBeginInfo" structextends="VkRenderPassBeginInfo">
<member values="VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member> <!-- Pointer to next structure -->
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
<member optional="true"><type>uint32_t</type> <name>attachmentCount</name></member>
<member len="attachmentCount">const <type>VkImageView</type>* <name>pAttachments</name></member>
</type>
<type category="struct" name="VkRenderPassAttachmentBeginInfoKHR" alias="VkRenderPassAttachmentBeginInfo"/>
- <type category="struct" name="VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <type category="struct" name="VkPhysicalDeviceTextureCompressionASTCHDRFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>textureCompressionASTC_HDR</name></member>
</type>
+ <type category="struct" name="VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT" alias="VkPhysicalDeviceTextureCompressionASTCHDRFeatures"/>
<type category="struct" name="VkPhysicalDeviceCooperativeMatrixFeaturesNV" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
@@ -4181,7 +4577,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceCooperativeMatrixPropertiesNV" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_PROPERTIES_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkShaderStageFlags</type> <name>cooperativeMatrixSupportedStages</name></member>
+ <member limittype="bitmask"><type>VkShaderStageFlags</type> <name>cooperativeMatrixSupportedStages</name></member>
</type>
<type category="struct" name="VkCooperativeMatrixPropertiesNV">
<member values="VK_STRUCTURE_TYPE_COOPERATIVE_MATRIX_PROPERTIES_NV"><type>VkStructureType</type> <name>sType</name></member>
@@ -4218,17 +4614,19 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>GgpFrameToken</type> <name>frameToken</name></member>
</type>
- <type category="struct" name="VkPipelineCreationFeedbackEXT" returnedonly="true">
- <member><type>VkPipelineCreationFeedbackFlagsEXT</type> <name>flags</name></member>
+ <type category="struct" name="VkPipelineCreationFeedback" returnedonly="true">
+ <member><type>VkPipelineCreationFeedbackFlags</type> <name>flags</name></member>
<member><type>uint64_t</type> <name>duration</name></member>
</type>
- <type category="struct" name="VkPipelineCreationFeedbackCreateInfoEXT" structextends="VkGraphicsPipelineCreateInfo,VkComputePipelineCreateInfo,VkRayTracingPipelineCreateInfoNV,VkRayTracingPipelineCreateInfoKHR">
- <member values="VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkPipelineCreationFeedbackEXT</type>* <name>pPipelineCreationFeedback</name><comment>Output pipeline creation feedback.</comment></member>
- <member><type>uint32_t</type> <name>pipelineStageCreationFeedbackCount</name></member>
- <member len="pipelineStageCreationFeedbackCount"><type>VkPipelineCreationFeedbackEXT</type>* <name>pPipelineStageCreationFeedbacks</name><comment>One entry for each shader stage specified in the parent Vk*PipelineCreateInfo struct</comment></member>
+ <type category="struct" name="VkPipelineCreationFeedbackEXT" alias="VkPipelineCreationFeedback"/>
+ <type category="struct" name="VkPipelineCreationFeedbackCreateInfo" structextends="VkGraphicsPipelineCreateInfo,VkComputePipelineCreateInfo,VkRayTracingPipelineCreateInfoNV,VkRayTracingPipelineCreateInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkPipelineCreationFeedback</type>* <name>pPipelineCreationFeedback</name><comment>Output pipeline creation feedback.</comment></member>
+ <member optional="true"><type>uint32_t</type> <name>pipelineStageCreationFeedbackCount</name></member>
+ <member len="pipelineStageCreationFeedbackCount"><type>VkPipelineCreationFeedback</type>* <name>pPipelineStageCreationFeedbacks</name><comment>One entry for each shader stage specified in the parent Vk*PipelineCreateInfo struct</comment></member>
</type>
+ <type category="struct" name="VkPipelineCreationFeedbackCreateInfoEXT" alias="VkPipelineCreationFeedbackCreateInfo"/>
<type category="struct" name="VkSurfaceFullScreenExclusiveInfoEXT" structextends="VkPhysicalDeviceSurfaceInfo2KHR,VkSwapchainCreateInfoKHR">
<member values="VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
@@ -4253,11 +4651,11 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDevicePerformanceQueryPropertiesKHR" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PERFORMANCE_QUERY_PROPERTIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member noautovalidity="true"><type>VkBool32</type> <name>allowCommandBufferQueryCopies</name><comment>Flag to specify whether performance queries are allowed to be used in vkCmdCopyQueryPoolResults</comment></member>
+ <member limittype="bitmask" noautovalidity="true"><type>VkBool32</type> <name>allowCommandBufferQueryCopies</name><comment>Flag to specify whether performance queries are allowed to be used in vkCmdCopyQueryPoolResults</comment></member>
</type>
<type category="struct" name="VkPerformanceCounterKHR" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_PERFORMANCE_COUNTER_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member> <!-- Pointer to next structure -->
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkPerformanceCounterUnitKHR</type> <name>unit</name></member>
<member><type>VkPerformanceCounterScopeKHR</type> <name>scope</name></member>
<member><type>VkPerformanceCounterStorageKHR</type> <name>storage</name></member>
@@ -4265,7 +4663,7 @@ typedef void <name>CAMetalLayer</name>;
</type>
<type category="struct" name="VkPerformanceCounterDescriptionKHR" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_PERFORMANCE_COUNTER_DESCRIPTION_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member> <!-- Pointer to next structure -->
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
<member optional="true"><type>VkPerformanceCounterDescriptionFlagsKHR</type> <name>flags</name></member>
<member><type>char</type> <name>name</name>[<enum>VK_MAX_DESCRIPTION_SIZE</enum>]</member>
<member><type>char</type> <name>category</name>[<enum>VK_MAX_DESCRIPTION_SIZE</enum>]</member>
@@ -4273,7 +4671,7 @@ typedef void <name>CAMetalLayer</name>;
</type>
<type category="struct" name="VkQueryPoolPerformanceCreateInfoKHR" structextends="VkQueryPoolCreateInfo">
<member values="VK_STRUCTURE_TYPE_QUERY_POOL_PERFORMANCE_CREATE_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member> <!-- Pointer to next structure -->
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>uint32_t</type> <name>queueFamilyIndex</name></member>
<member><type>uint32_t</type> <name>counterIndexCount</name></member>
<member len="counterIndexCount">const <type>uint32_t</type>* <name>pCounterIndices</name></member>
@@ -4292,7 +4690,7 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true"><type>VkAcquireProfilingLockFlagsKHR</type> <name>flags</name><comment>Acquire profiling lock flags</comment></member>
<member><type>uint64_t</type> <name>timeout</name></member>
</type>
- <type category="struct" name="VkPerformanceQuerySubmitInfoKHR" structextends="VkSubmitInfo">
+ <type category="struct" name="VkPerformanceQuerySubmitInfoKHR" structextends="VkSubmitInfo,VkSubmitInfo2">
<member values="VK_STRUCTURE_TYPE_PERFORMANCE_QUERY_SUBMIT_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>uint32_t</type> <name>counterPassIndex</name><comment>Index for which counter pass to submit</comment></member>
@@ -4303,7 +4701,7 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true"><type>VkHeadlessSurfaceCreateFlagsEXT</type> <name>flags</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceCoverageReductionModeFeaturesNV" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COVERAGE_REDUCTION_MODE_FEATURES_NV"><type>VkStructureType</type><name>sType</name></member>
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COVERAGE_REDUCTION_MODE_FEATURES_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>coverageReductionMode</name></member>
</type>
@@ -4335,7 +4733,7 @@ typedef void <name>CAMetalLayer</name>;
</type>
<type category="struct" name="VkPerformanceValueINTEL">
<member><type>VkPerformanceValueTypeINTEL</type> <name>type</name></member>
- <member selector="type"><type>VkPerformanceValueDataINTEL</type> <name>data</name></member>
+ <member selector="type" noautovalidity="true"><type>VkPerformanceValueDataINTEL</type> <name>data</name></member>
</type>
<type category="struct" name="VkInitializePerformanceApiInfoINTEL" >
<member values="VK_STRUCTURE_TYPE_INITIALIZE_PERFORMANCE_API_INFO_INTEL"><type>VkStructureType</type> <name>sType</name></member>
@@ -4384,11 +4782,11 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceShaderSMBuiltinsPropertiesNV" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SM_BUILTINS_PROPERTIES_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>shaderSMCount</name></member>
- <member><type>uint32_t</type> <name>shaderWarpsPerSM</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>shaderSMCount</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>shaderWarpsPerSM</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceShaderSMBuiltinsFeaturesNV" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SM_BUILTINS_FEATURES_NV"><type>VkStructureType</type><name>sType</name></member>
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SM_BUILTINS_FEATURES_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>shaderSMBuiltins</name></member>
</type>
@@ -4400,19 +4798,25 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkBool32</type> <name>fragmentShaderShadingRateInterlock</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES"><type>VkStructureType</type><name>sType</name></member>
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>separateDepthStencilLayouts</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR" alias="VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures"/>
<type category="struct" name="VkAttachmentReferenceStencilLayout" structextends="VkAttachmentReference2">
- <member values="VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_STENCIL_LAYOUT"><type>VkStructureType</type><name>sType</name></member>
+ <member values="VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_STENCIL_LAYOUT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkImageLayout</type> <name>stencilLayout</name></member>
</type>
+ <type category="struct" name="VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>primitiveTopologyListRestart</name></member>
+ <member><type>VkBool32</type> <name>primitiveTopologyPatchListRestart</name></member>
+ </type>
<type category="struct" name="VkAttachmentReferenceStencilLayoutKHR" alias="VkAttachmentReferenceStencilLayout"/>
<type category="struct" name="VkAttachmentDescriptionStencilLayout" structextends="VkAttachmentDescription2">
- <member values="VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT"><type>VkStructureType</type><name>sType</name></member>
+ <member values="VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkImageLayout</type> <name>stencilInitialLayout</name></member>
<member><type>VkImageLayout</type> <name>stencilFinalLayout</name></member>
@@ -4428,6 +4832,7 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>VkPipeline</type> <name>pipeline</name></member>
</type>
+ <type category="struct" name="VkPipelineInfoEXT" alias="VkPipelineInfoKHR"/>
<type category="struct" name="VkPipelineExecutablePropertiesKHR" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_PROPERTIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
@@ -4454,7 +4859,7 @@ typedef void <name>CAMetalLayer</name>;
<member><type>char</type> <name>name</name>[<enum>VK_MAX_DESCRIPTION_SIZE</enum>]</member>
<member><type>char</type> <name>description</name>[<enum>VK_MAX_DESCRIPTION_SIZE</enum>]</member>
<member><type>VkPipelineExecutableStatisticFormatKHR</type> <name>format</name></member>
- <member selector="format"><type>VkPipelineExecutableStatisticValueKHR</type> <name>value</name></member>
+ <member selector="format" noautovalidity="true"><type>VkPipelineExecutableStatisticValueKHR</type> <name>value</name></member>
</type>
<type category="struct" name="VkPipelineExecutableInternalRepresentationKHR" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INTERNAL_REPRESENTATION_KHR"><type>VkStructureType</type> <name>sType</name></member>
@@ -4462,45 +4867,61 @@ typedef void <name>CAMetalLayer</name>;
<member><type>char</type> <name>name</name>[<enum>VK_MAX_DESCRIPTION_SIZE</enum>]</member>
<member><type>char</type> <name>description</name>[<enum>VK_MAX_DESCRIPTION_SIZE</enum>]</member>
<member><type>VkBool32</type> <name>isText</name></member>
- <member optional="true"><type>size_t</type> <name>dataSize</name></member>
+ <member><type>size_t</type> <name>dataSize</name></member>
<member optional="true" len="dataSize"><type>void</type>* <name>pData</name></member>
</type>
- <type category="struct" name="VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkBool32</type> <name>shaderDemoteToHelperInvocation</name></member>
+ <type category="struct" name="VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>shaderDemoteToHelperInvocation</name></member>
</type>
+ <type category="struct" name="VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT" alias="VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures"/>
<type category="struct" name="VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>texelBufferAlignment</name></member>
</type>
- <type category="struct" name="VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT" structextends="VkPhysicalDeviceProperties2" returnedonly="true">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <type category="struct" name="VkPhysicalDeviceTexelBufferAlignmentProperties" structextends="VkPhysicalDeviceProperties2" returnedonly="true">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkDeviceSize</type> <name>storageTexelBufferOffsetAlignmentBytes</name></member>
- <member><type>VkBool32</type> <name>storageTexelBufferOffsetSingleTexelAlignment</name></member>
- <member><type>VkDeviceSize</type> <name>uniformTexelBufferOffsetAlignmentBytes</name></member>
- <member><type>VkBool32</type> <name>uniformTexelBufferOffsetSingleTexelAlignment</name></member>
- </type>
- <type category="struct" name="VkPhysicalDeviceSubgroupSizeControlFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member limittype="min,pot"><type>VkDeviceSize</type> <name>storageTexelBufferOffsetAlignmentBytes</name></member>
+ <member limittype="exact"><type>VkBool32</type> <name>storageTexelBufferOffsetSingleTexelAlignment</name></member>
+ <member limittype="min,pot"><type>VkDeviceSize</type> <name>uniformTexelBufferOffsetAlignmentBytes</name></member>
+ <member limittype="exact"><type>VkBool32</type> <name>uniformTexelBufferOffsetSingleTexelAlignment</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT" alias="VkPhysicalDeviceTexelBufferAlignmentProperties"/>
+ <type category="struct" name="VkPhysicalDeviceSubgroupSizeControlFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>subgroupSizeControl</name></member>
+ <member><type>VkBool32</type> <name>computeFullSubgroups</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceSubgroupSizeControlFeaturesEXT" alias="VkPhysicalDeviceSubgroupSizeControlFeatures"/>
+ <type category="struct" name="VkPhysicalDeviceSubgroupSizeControlProperties" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkBool32</type> <name>subgroupSizeControl</name></member>
- <member><type>VkBool32</type> <name>computeFullSubgroups</name></member>
+ <member limittype="min,pot" noautovalidity="true"><type>uint32_t</type> <name>minSubgroupSize</name><comment>The minimum subgroup size supported by this device</comment></member>
+ <member limittype="max,pot" noautovalidity="true"><type>uint32_t</type> <name>maxSubgroupSize</name><comment>The maximum subgroup size supported by this device</comment></member>
+ <member limittype="max" noautovalidity="true"><type>uint32_t</type> <name>maxComputeWorkgroupSubgroups</name><comment>The maximum number of subgroups supported in a workgroup</comment></member>
+ <member limittype="bitmask"><type>VkShaderStageFlags</type> <name>requiredSubgroupSizeStages</name><comment>The shader stages that support specifying a subgroup size</comment></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceSubgroupSizeControlPropertiesEXT" alias="VkPhysicalDeviceSubgroupSizeControlProperties"/>
+ <type category="struct" name="VkPipelineShaderStageRequiredSubgroupSizeCreateInfo" returnedonly="true" structextends="VkPipelineShaderStageCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>requiredSubgroupSize</name></member>
</type>
- <type category="struct" name="VkPhysicalDeviceSubgroupSizeControlPropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true"><type>void</type>* <name>pNext</name></member>
- <member noautovalidity="true"><type>uint32_t</type> <name>minSubgroupSize</name><comment>The minimum subgroup size supported by this device</comment></member>
- <member noautovalidity="true"><type>uint32_t</type> <name>maxSubgroupSize</name><comment>The maximum subgroup size supported by this device</comment></member>
- <member noautovalidity="true"><type>uint32_t</type> <name>maxComputeWorkgroupSubgroups</name><comment>The maximum number of subgroups supported in a workgroup</comment></member>
- <member><type>VkShaderStageFlags</type> <name>requiredSubgroupSizeStages</name><comment>The shader stages that support specifying a subgroup size</comment></member>
+ <type category="struct" name="VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT" alias="VkPipelineShaderStageRequiredSubgroupSizeCreateInfo"/>
+ <type category="struct" name="VkSubpassShadingPipelineCreateInfoHUAWEI" returnedonly="true" structextends="VkComputePipelineCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_SUBPASS_SHADING_PIPELINE_CREATE_INFO_HUAWEI"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkRenderPass</type> <name>renderPass</name></member>
+ <member><type>uint32_t</type> <name>subpass</name></member>
</type>
- <type category="struct" name="VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT" returnedonly="true" structextends="VkPipelineShaderStageCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <type category="struct" name="VkPhysicalDeviceSubpassShadingPropertiesHUAWEI" structextends="VkPhysicalDeviceProperties2" returnedonly="true">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_SHADING_PROPERTIES_HUAWEI"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>requiredSubgroupSize</name></member>
+ <member limittype="max,pot"><type>uint32_t</type> <name>maxSubpassShadingWorkgroupSizeAspectRatio</name></member>
</type>
<type category="struct" name="VkMemoryOpaqueCaptureAddressAllocateInfo" structextends="VkMemoryAllocateInfo">
<member values="VK_STRUCTURE_TYPE_MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO"><type>VkStructureType</type> <name>sType</name></member>
@@ -4527,23 +4948,24 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceLineRasterizationPropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>lineSubPixelPrecisionBits</name></member>
+ <member limittype="bits"><type>uint32_t</type> <name>lineSubPixelPrecisionBits</name></member>
</type>
<type category="struct" name="VkPipelineRasterizationLineStateCreateInfoEXT" structextends="VkPipelineRasterizationStateCreateInfo">
<member values="VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>VkLineRasterizationModeEXT</type> <name>lineRasterizationMode</name></member>
<member><type>VkBool32</type> <name>stippledLineEnable</name></member>
- <member optional="true"><type>uint32_t</type> <name>lineStippleFactor</name></member>
- <member optional="true"><type>uint16_t</type> <name>lineStipplePattern</name></member>
+ <member><type>uint32_t</type> <name>lineStippleFactor</name></member>
+ <member><type>uint16_t</type> <name>lineStipplePattern</name></member>
</type>
- <type category="struct" name="VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <type category="struct" name="VkPhysicalDevicePipelineCreationCacheControlFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkBool32</type> <name>pipelineCreationCacheControl</name></member>
+ <member><type>VkBool32</type> <name>pipelineCreationCacheControl</name></member>
</type>
+ <type category="struct" name="VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT" alias="VkPhysicalDevicePipelineCreationCacheControlFeatures"/>
<type category="struct" name="VkPhysicalDeviceVulkan11Features" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES"><type>VkStructureType</type><name>sType</name></member>
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>storageBuffer16BitAccess</name><comment>16-bit integer/floating-point variables supported in BufferBlock</comment></member>
<member><type>VkBool32</type> <name>uniformAndStorageBuffer16BitAccess</name><comment>16-bit integer/floating-point variables supported in BufferBlock and Block</comment></member>
@@ -4559,26 +4981,26 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkBool32</type> <name>shaderDrawParameters</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceVulkan11Properties" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES"><type>VkStructureType</type><name>sType</name></member>
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint8_t</type> <name>deviceUUID</name>[<enum>VK_UUID_SIZE</enum>]</member>
- <member><type>uint8_t</type> <name>driverUUID</name>[<enum>VK_UUID_SIZE</enum>]</member>
- <member><type>uint8_t</type> <name>deviceLUID</name>[<enum>VK_LUID_SIZE</enum>]</member>
- <member><type>uint32_t</type> <name>deviceNodeMask</name></member>
- <member><type>VkBool32</type> <name>deviceLUIDValid</name></member>
- <member noautovalidity="true"><type>uint32_t</type> <name>subgroupSize</name><comment>The size of a subgroup for this queue.</comment></member>
- <member noautovalidity="true"><type>VkShaderStageFlags</type> <name>subgroupSupportedStages</name><comment>Bitfield of what shader stages support subgroup operations</comment></member>
- <member noautovalidity="true"><type>VkSubgroupFeatureFlags</type> <name>subgroupSupportedOperations</name><comment>Bitfield of what subgroup operations are supported.</comment></member>
- <member noautovalidity="true"><type>VkBool32</type> <name>subgroupQuadOperationsInAllStages</name><comment>Flag to specify whether quad operations are available in all stages.</comment></member>
- <member><type>VkPointClippingBehavior</type> <name>pointClippingBehavior</name></member>
- <member><type>uint32_t</type> <name>maxMultiviewViewCount</name><comment>max number of views in a subpass</comment></member>
- <member><type>uint32_t</type> <name>maxMultiviewInstanceIndex</name><comment>max instance index for a draw in a multiview subpass</comment></member>
- <member><type>VkBool32</type> <name>protectedNoFault</name></member>
- <member><type>uint32_t</type> <name>maxPerSetDescriptors</name></member>
- <member><type>VkDeviceSize</type> <name>maxMemoryAllocationSize</name></member>
+ <member limittype="exact"><type>uint8_t</type> <name>deviceUUID</name>[<enum>VK_UUID_SIZE</enum>]</member>
+ <member limittype="exact"><type>uint8_t</type> <name>driverUUID</name>[<enum>VK_UUID_SIZE</enum>]</member>
+ <member limittype="exact"><type>uint8_t</type> <name>deviceLUID</name>[<enum>VK_LUID_SIZE</enum>]</member>
+ <member limittype="exact"><type>uint32_t</type> <name>deviceNodeMask</name></member>
+ <member limittype="exact"><type>VkBool32</type> <name>deviceLUIDValid</name></member>
+ <member limittype="min,pot" noautovalidity="true"><type>uint32_t</type> <name>subgroupSize</name><comment>The size of a subgroup for this queue.</comment></member>
+ <member limittype="bitmask" noautovalidity="true"><type>VkShaderStageFlags</type> <name>subgroupSupportedStages</name><comment>Bitfield of what shader stages support subgroup operations</comment></member>
+ <member limittype="bitmask" noautovalidity="true"><type>VkSubgroupFeatureFlags</type> <name>subgroupSupportedOperations</name><comment>Bitfield of what subgroup operations are supported.</comment></member>
+ <member limittype="bitmask" noautovalidity="true"><type>VkBool32</type> <name>subgroupQuadOperationsInAllStages</name><comment>Flag to specify whether quad operations are available in all stages.</comment></member>
+ <member limittype="exact"><type>VkPointClippingBehavior</type> <name>pointClippingBehavior</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxMultiviewViewCount</name><comment>max number of views in a subpass</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxMultiviewInstanceIndex</name><comment>max instance index for a draw in a multiview subpass</comment></member>
+ <member limittype="exact"><type>VkBool32</type> <name>protectedNoFault</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerSetDescriptors</name></member>
+ <member limittype="max"><type>VkDeviceSize</type> <name>maxMemoryAllocationSize</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceVulkan12Features" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES"><type>VkStructureType</type><name>sType</name></member>
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>samplerMirrorClampToEdge</name></member>
<member><type>VkBool32</type> <name>drawIndirectCount</name></member>
@@ -4629,60 +5051,128 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkBool32</type> <name>subgroupBroadcastDynamicId</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceVulkan12Properties" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES"><type>VkStructureType</type><name>sType</name></member>
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkDriverId</type> <name>driverID</name></member>
- <member><type>char</type> <name>driverName</name>[<enum>VK_MAX_DRIVER_NAME_SIZE</enum>]</member>
- <member><type>char</type> <name>driverInfo</name>[<enum>VK_MAX_DRIVER_INFO_SIZE</enum>]</member>
- <member><type>VkConformanceVersion</type> <name>conformanceVersion</name></member>
- <member><type>VkShaderFloatControlsIndependence</type><name>denormBehaviorIndependence</name></member>
- <member><type>VkShaderFloatControlsIndependence</type><name>roundingModeIndependence</name></member>
- <member><type>VkBool32</type> <name>shaderSignedZeroInfNanPreserveFloat16</name><comment>An implementation can preserve signed zero, nan, inf</comment></member>
- <member><type>VkBool32</type> <name>shaderSignedZeroInfNanPreserveFloat32</name><comment>An implementation can preserve signed zero, nan, inf</comment></member>
- <member><type>VkBool32</type> <name>shaderSignedZeroInfNanPreserveFloat64</name><comment>An implementation can preserve signed zero, nan, inf</comment></member>
- <member><type>VkBool32</type> <name>shaderDenormPreserveFloat16</name><comment>An implementation can preserve denormals</comment></member>
- <member><type>VkBool32</type> <name>shaderDenormPreserveFloat32</name><comment>An implementation can preserve denormals</comment></member>
- <member><type>VkBool32</type> <name>shaderDenormPreserveFloat64</name><comment>An implementation can preserve denormals</comment></member>
- <member><type>VkBool32</type> <name>shaderDenormFlushToZeroFloat16</name><comment>An implementation can flush to zero denormals</comment></member>
- <member><type>VkBool32</type> <name>shaderDenormFlushToZeroFloat32</name><comment>An implementation can flush to zero denormals</comment></member>
- <member><type>VkBool32</type> <name>shaderDenormFlushToZeroFloat64</name><comment>An implementation can flush to zero denormals</comment></member>
- <member><type>VkBool32</type> <name>shaderRoundingModeRTEFloat16</name><comment>An implementation can support RTE</comment></member>
- <member><type>VkBool32</type> <name>shaderRoundingModeRTEFloat32</name><comment>An implementation can support RTE</comment></member>
- <member><type>VkBool32</type> <name>shaderRoundingModeRTEFloat64</name><comment>An implementation can support RTE</comment></member>
- <member><type>VkBool32</type> <name>shaderRoundingModeRTZFloat16</name><comment>An implementation can support RTZ</comment></member>
- <member><type>VkBool32</type> <name>shaderRoundingModeRTZFloat32</name><comment>An implementation can support RTZ</comment></member>
- <member><type>VkBool32</type> <name>shaderRoundingModeRTZFloat64</name><comment>An implementation can support RTZ</comment></member>
- <member><type>uint32_t</type> <name>maxUpdateAfterBindDescriptorsInAllPools</name></member>
- <member><type>VkBool32</type> <name>shaderUniformBufferArrayNonUniformIndexingNative</name></member>
- <member><type>VkBool32</type> <name>shaderSampledImageArrayNonUniformIndexingNative</name></member>
- <member><type>VkBool32</type> <name>shaderStorageBufferArrayNonUniformIndexingNative</name></member>
- <member><type>VkBool32</type> <name>shaderStorageImageArrayNonUniformIndexingNative</name></member>
- <member><type>VkBool32</type> <name>shaderInputAttachmentArrayNonUniformIndexingNative</name></member>
- <member><type>VkBool32</type> <name>robustBufferAccessUpdateAfterBind</name></member>
- <member><type>VkBool32</type> <name>quadDivergentImplicitLod</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindSamplers</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindUniformBuffers</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindStorageBuffers</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindSampledImages</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindStorageImages</name></member>
- <member><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindInputAttachments</name></member>
- <member><type>uint32_t</type> <name>maxPerStageUpdateAfterBindResources</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindSamplers</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindUniformBuffers</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindUniformBuffersDynamic</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindStorageBuffers</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindStorageBuffersDynamic</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindSampledImages</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindStorageImages</name></member>
- <member><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindInputAttachments</name></member>
- <member><type>VkResolveModeFlags</type> <name>supportedDepthResolveModes</name><comment>supported depth resolve modes</comment></member>
- <member><type>VkResolveModeFlags</type> <name>supportedStencilResolveModes</name><comment>supported stencil resolve modes</comment></member>
- <member><type>VkBool32</type> <name>independentResolveNone</name><comment>depth and stencil resolve modes can be set independently if one of them is none</comment></member>
- <member><type>VkBool32</type> <name>independentResolve</name><comment>depth and stencil resolve modes can be set independently</comment></member>
- <member><type>VkBool32</type> <name>filterMinmaxSingleComponentFormats</name></member>
- <member><type>VkBool32</type> <name>filterMinmaxImageComponentMapping</name></member>
- <member><type>uint64_t</type> <name>maxTimelineSemaphoreValueDifference</name></member>
- <member optional="true"><type>VkSampleCountFlags</type> <name>framebufferIntegerColorSampleCounts</name></member>
+ <member limittype="noauto"><type>VkDriverId</type> <name>driverID</name></member>
+ <member limittype="noauto"><type>char</type> <name>driverName</name>[<enum>VK_MAX_DRIVER_NAME_SIZE</enum>]</member>
+ <member limittype="noauto"><type>char</type> <name>driverInfo</name>[<enum>VK_MAX_DRIVER_INFO_SIZE</enum>]</member>
+ <member limittype="noauto"><type>VkConformanceVersion</type> <name>conformanceVersion</name></member>
+ <member limittype="exact"><type>VkShaderFloatControlsIndependence</type> <name>denormBehaviorIndependence</name></member>
+ <member limittype="exact"><type>VkShaderFloatControlsIndependence</type> <name>roundingModeIndependence</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderSignedZeroInfNanPreserveFloat16</name><comment>An implementation can preserve signed zero, nan, inf</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderSignedZeroInfNanPreserveFloat32</name><comment>An implementation can preserve signed zero, nan, inf</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderSignedZeroInfNanPreserveFloat64</name><comment>An implementation can preserve signed zero, nan, inf</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderDenormPreserveFloat16</name><comment>An implementation can preserve denormals</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderDenormPreserveFloat32</name><comment>An implementation can preserve denormals</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderDenormPreserveFloat64</name><comment>An implementation can preserve denormals</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderDenormFlushToZeroFloat16</name><comment>An implementation can flush to zero denormals</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderDenormFlushToZeroFloat32</name><comment>An implementation can flush to zero denormals</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderDenormFlushToZeroFloat64</name><comment>An implementation can flush to zero denormals</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderRoundingModeRTEFloat16</name><comment>An implementation can support RTE</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderRoundingModeRTEFloat32</name><comment>An implementation can support RTE</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderRoundingModeRTEFloat64</name><comment>An implementation can support RTE</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderRoundingModeRTZFloat16</name><comment>An implementation can support RTZ</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderRoundingModeRTZFloat32</name><comment>An implementation can support RTZ</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderRoundingModeRTZFloat64</name><comment>An implementation can support RTZ</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxUpdateAfterBindDescriptorsInAllPools</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderUniformBufferArrayNonUniformIndexingNative</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderSampledImageArrayNonUniformIndexingNative</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderStorageBufferArrayNonUniformIndexingNative</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderStorageImageArrayNonUniformIndexingNative</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>shaderInputAttachmentArrayNonUniformIndexingNative</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>robustBufferAccessUpdateAfterBind</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>quadDivergentImplicitLod</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindSamplers</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindUniformBuffers</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindStorageBuffers</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindSampledImages</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindStorageImages</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindInputAttachments</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageUpdateAfterBindResources</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindSamplers</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindUniformBuffers</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindUniformBuffersDynamic</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindStorageBuffers</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindStorageBuffersDynamic</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindSampledImages</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindStorageImages</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindInputAttachments</name></member>
+ <member limittype="bitmask"><type>VkResolveModeFlags</type> <name>supportedDepthResolveModes</name><comment>supported depth resolve modes</comment></member>
+ <member limittype="bitmask"><type>VkResolveModeFlags</type> <name>supportedStencilResolveModes</name><comment>supported stencil resolve modes</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>independentResolveNone</name><comment>depth and stencil resolve modes can be set independently if one of them is none</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>independentResolve</name><comment>depth and stencil resolve modes can be set independently</comment></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>filterMinmaxSingleComponentFormats</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>filterMinmaxImageComponentMapping</name></member>
+ <member limittype="max"><type>uint64_t</type> <name>maxTimelineSemaphoreValueDifference</name></member>
+ <member limittype="bitmask" optional="true"><type>VkSampleCountFlags</type> <name>framebufferIntegerColorSampleCounts</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceVulkan13Features" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>robustImageAccess</name></member>
+ <member><type>VkBool32</type> <name>inlineUniformBlock</name></member>
+ <member><type>VkBool32</type> <name>descriptorBindingInlineUniformBlockUpdateAfterBind</name></member>
+ <member><type>VkBool32</type> <name>pipelineCreationCacheControl</name></member>
+ <member><type>VkBool32</type> <name>privateData</name></member>
+ <member><type>VkBool32</type> <name>shaderDemoteToHelperInvocation</name></member>
+ <member><type>VkBool32</type> <name>shaderTerminateInvocation</name></member>
+ <member><type>VkBool32</type> <name>subgroupSizeControl</name></member>
+ <member><type>VkBool32</type> <name>computeFullSubgroups</name></member>
+ <member><type>VkBool32</type> <name>synchronization2</name></member>
+ <member><type>VkBool32</type> <name>textureCompressionASTC_HDR</name></member>
+ <member><type>VkBool32</type> <name>shaderZeroInitializeWorkgroupMemory</name></member>
+ <member><type>VkBool32</type> <name>dynamicRendering</name></member>
+ <member><type>VkBool32</type> <name>shaderIntegerDotProduct</name></member>
+ <member><type>VkBool32</type> <name>maintenance4</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceVulkan13Properties" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="min,pot" noautovalidity="true"><type>uint32_t</type> <name>minSubgroupSize</name><comment>The minimum subgroup size supported by this device</comment></member>
+ <member limittype="max,pot" noautovalidity="true"><type>uint32_t</type> <name>maxSubgroupSize</name><comment>The maximum subgroup size supported by this device</comment></member>
+ <member limittype="max" noautovalidity="true"><type>uint32_t</type> <name>maxComputeWorkgroupSubgroups</name><comment>The maximum number of subgroups supported in a workgroup</comment></member>
+ <member limittype="bitmask"><type>VkShaderStageFlags</type> <name>requiredSubgroupSizeStages</name><comment>The shader stages that support specifying a subgroup size</comment></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxInlineUniformBlockSize</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorInlineUniformBlocks</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetInlineUniformBlocks</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxDescriptorSetUpdateAfterBindInlineUniformBlocks</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxInlineUniformTotalSize</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct8BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct8BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct8BitMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct4x8BitPackedUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct4x8BitPackedSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct4x8BitPackedMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct16BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct16BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct16BitMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct32BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct32BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct32BitMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct64BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct64BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct64BitMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating8BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating8BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating16BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating16BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating32BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating32BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating64BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating64BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated</name></member>
+ <member limittype="min,pot"><type>VkDeviceSize</type> <name>storageTexelBufferOffsetAlignmentBytes</name></member>
+ <member limittype="exact"><type>VkBool32</type> <name>storageTexelBufferOffsetSingleTexelAlignment</name></member>
+ <member limittype="min,pot"><type>VkDeviceSize</type> <name>uniformTexelBufferOffsetAlignmentBytes</name></member>
+ <member limittype="exact"><type>VkBool32</type> <name>uniformTexelBufferOffsetSingleTexelAlignment</name></member>
+ <member limittype="max"><type>VkDeviceSize</type> <name>maxBufferSize</name></member>
</type>
<type category="struct" name="VkPipelineCompilerControlCreateInfoAMD" structextends="VkGraphicsPipelineCreateInfo,VkComputePipelineCreateInfo">
<member values="VK_STRUCTURE_TYPE_PIPELINE_COMPILER_CONTROL_CREATE_INFO_AMD"><type>VkStructureType</type> <name>sType</name></member>
@@ -4694,25 +5184,26 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>deviceCoherentMemory</name></member>
</type>
- <type category="struct" name="VkPhysicalDeviceToolPropertiesEXT" returnedonly="true">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TOOL_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <type category="struct" name="VkPhysicalDeviceToolProperties" returnedonly="true">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TOOL_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>char</type> <name>name</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
- <member><type>char</type> <name>version</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
- <member><type>VkToolPurposeFlagsEXT</type> <name>purposes</name></member>
- <member><type>char</type> <name>description</name>[<enum>VK_MAX_DESCRIPTION_SIZE</enum>]</member>
- <member><type>char</type> <name>layer</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
+ <member><type>char</type> <name>name</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
+ <member><type>char</type> <name>version</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
+ <member><type>VkToolPurposeFlags</type> <name>purposes</name></member>
+ <member><type>char</type> <name>description</name>[<enum>VK_MAX_DESCRIPTION_SIZE</enum>]</member>
+ <member><type>char</type> <name>layer</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
</type>
+ <type category="struct" name="VkPhysicalDeviceToolPropertiesEXT" alias="VkPhysicalDeviceToolProperties"/>
<type category="struct" name="VkSamplerCustomBorderColorCreateInfoEXT" structextends="VkSamplerCreateInfo">
<member values="VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkClearColorValue</type> <name>customBorderColor</name></member>
+ <member noautovalidity="true"><type>VkClearColorValue</type> <name>customBorderColor</name></member>
<member><type>VkFormat</type> <name>format</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceCustomBorderColorPropertiesEXT" structextends="VkPhysicalDeviceProperties2" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>maxCustomBorderColorSamplers</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxCustomBorderColorSamplers</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceCustomBorderColorFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
@@ -4720,6 +5211,18 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkBool32</type> <name>customBorderColors</name></member>
<member><type>VkBool32</type> <name>customBorderColorWithoutFormat</name></member>
</type>
+ <type category="struct" name="VkSamplerBorderColorComponentMappingCreateInfoEXT" structextends="VkSamplerCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_SAMPLER_BORDER_COLOR_COMPONENT_MAPPING_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkComponentMapping</type> <name>components</name></member>
+ <member><type>VkBool32</type> <name>srgb</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceBorderColorSwizzleFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BORDER_COLOR_SWIZZLE_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>borderColorSwizzle</name></member>
+ <member><type>VkBool32</type> <name>borderColorSwizzleFromImage</name></member>
+ </type>
<type category="union" name="VkDeviceOrHostAddressKHR">
<member noautovalidity="true"><type>VkDeviceAddress</type> <name>deviceAddress</name></member>
<member noautovalidity="true"><type>void</type>* <name>hostAddress</name></member>
@@ -4730,26 +5233,26 @@ typedef void <name>CAMetalLayer</name>;
</type>
<type category="struct" name="VkAccelerationStructureGeometryTrianglesDataKHR">
<member values="VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>VkFormat</type> <name>vertexFormat</name></member>
- <member><type>VkDeviceOrHostAddressConstKHR</type> <name>vertexData</name></member>
+ <member noautovalidity="true"><type>VkDeviceOrHostAddressConstKHR</type> <name>vertexData</name></member>
<member><type>VkDeviceSize</type> <name>vertexStride</name></member>
<member><type>uint32_t</type> <name>maxVertex</name></member>
<member><type>VkIndexType</type> <name>indexType</name></member>
- <member optional="true"><type>VkDeviceOrHostAddressConstKHR</type> <name>indexData</name></member>
- <member optional="true"><type>VkDeviceOrHostAddressConstKHR</type> <name>transformData</name></member>
+ <member noautovalidity="true"><type>VkDeviceOrHostAddressConstKHR</type> <name>indexData</name></member>
+ <member noautovalidity="true"><type>VkDeviceOrHostAddressConstKHR</type> <name>transformData</name></member>
</type>
<type category="struct" name="VkAccelerationStructureGeometryAabbsDataKHR">
<member values="VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_AABBS_DATA_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkDeviceOrHostAddressConstKHR</type> <name>data</name></member>
+ <member noautovalidity="true"><type>VkDeviceOrHostAddressConstKHR</type> <name>data</name></member>
<member><type>VkDeviceSize</type> <name>stride</name></member>
</type>
<type category="struct" name="VkAccelerationStructureGeometryInstancesDataKHR">
<member values="VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>arrayOfPointers</name></member>
- <member><type>VkDeviceOrHostAddressConstKHR</type> <name>data</name></member>
+ <member noautovalidity="true"><type>VkDeviceOrHostAddressConstKHR</type> <name>data</name></member>
</type>
<type category="union" name="VkAccelerationStructureGeometryDataKHR">
<member selection="VK_GEOMETRY_TYPE_TRIANGLES_KHR"><type>VkAccelerationStructureGeometryTrianglesDataKHR</type> <name>triangles</name></member>
@@ -4774,13 +5277,13 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true"><type>uint32_t</type> <name>geometryCount</name></member>
<member len="geometryCount" optional="true">const <type>VkAccelerationStructureGeometryKHR</type>* <name>pGeometries</name></member>
<member len="geometryCount,1" optional="true,false">const <type>VkAccelerationStructureGeometryKHR</type>* const* <name>ppGeometries</name></member>
- <member><type>VkDeviceOrHostAddressKHR</type> <name>scratchData</name></member>
+ <member noautovalidity="true"><type>VkDeviceOrHostAddressKHR</type> <name>scratchData</name></member>
</type>
<type category="struct" name="VkAccelerationStructureBuildRangeInfoKHR">
<member><type>uint32_t</type> <name>primitiveCount</name></member>
<member><type>uint32_t</type> <name>primitiveOffset</name></member>
- <member optional="true"><type>uint32_t</type> <name>firstVertex</name></member>
- <member optional="true"><type>uint32_t</type> <name>transformOffset</name></member>
+ <member><type>uint32_t</type> <name>firstVertex</name></member>
+ <member><type>uint32_t</type> <name>transformOffset</name></member>
</type>
<type category="struct" name="VkAccelerationStructureCreateInfoKHR">
<member values="VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
@@ -4823,7 +5326,7 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkAccelerationStructureVersionInfoKHR">
<member values="VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_VERSION_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member len="2*ename:VK_UUID_SIZE" altlen="2*VK_UUID_SIZE">const <type>uint8_t</type>* <name>pVersionData</name></member>
+ <member len="latexmath:[2 \times \mathtt{VK\_UUID\_SIZE}]" altlen="2*VK_UUID_SIZE">const <type>uint8_t</type>* <name>pVersionData</name></member>
</type>
<type category="struct" name="VkCopyAccelerationStructureInfoKHR">
<member values="VK_STRUCTURE_TYPE_COPY_ACCELERATION_STRUCTURE_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
@@ -4836,13 +5339,13 @@ typedef void <name>CAMetalLayer</name>;
<member values="VK_STRUCTURE_TYPE_COPY_ACCELERATION_STRUCTURE_TO_MEMORY_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>VkAccelerationStructureKHR</type> <name>src</name></member>
- <member><type>VkDeviceOrHostAddressKHR</type> <name>dst</name></member>
+ <member noautovalidity="true"><type>VkDeviceOrHostAddressKHR</type> <name>dst</name></member>
<member><type>VkCopyAccelerationStructureModeKHR</type> <name>mode</name></member>
</type>
<type category="struct" name="VkCopyMemoryToAccelerationStructureInfoKHR">
<member values="VK_STRUCTURE_TYPE_COPY_MEMORY_TO_ACCELERATION_STRUCTURE_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkDeviceOrHostAddressConstKHR</type> <name>src</name></member>
+ <member noautovalidity="true"><type>VkDeviceOrHostAddressConstKHR</type> <name>src</name></member>
<member><type>VkAccelerationStructureKHR</type> <name>dst</name></member>
<member><type>VkCopyAccelerationStructureModeKHR</type> <name>mode</name></member>
</type>
@@ -4852,7 +5355,7 @@ typedef void <name>CAMetalLayer</name>;
<member><type>uint32_t</type> <name>maxPipelineRayPayloadSize</name></member>
<member><type>uint32_t</type> <name>maxPipelineRayHitAttributeSize</name></member>
</type>
- <type category="struct" name="VkPipelineLibraryCreateInfoKHR">
+ <type category="struct" name="VkPipelineLibraryCreateInfoKHR" structextends="VkGraphicsPipelineCreateInfo">
<member values="VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member optional="true"><type>uint32_t</type> <name>libraryCount</name></member>
@@ -4863,12 +5366,19 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>extendedDynamicState</name></member>
</type>
+ <type category="struct" name="VkPhysicalDeviceExtendedDynamicState2FeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>extendedDynamicState2</name></member>
+ <member><type>VkBool32</type> <name>extendedDynamicState2LogicOp</name></member>
+ <member><type>VkBool32</type> <name>extendedDynamicState2PatchControlPoints</name></member>
+ </type>
<type category="struct" name="VkRenderPassTransformBeginInfoQCOM" structextends="VkRenderPassBeginInfo">
<member values="VK_STRUCTURE_TYPE_RENDER_PASS_TRANSFORM_BEGIN_INFO_QCOM"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name><comment>Pointer to next structure</comment></member>
<member noautovalidity="true"><type>VkSurfaceTransformFlagBitsKHR</type> <name>transform</name></member>
</type>
- <type category="struct" name="VkCopyCommandTransformInfoQCOM" structextends="VkBufferImageCopy2KHR,VkImageBlit2KHR">
+ <type category="struct" name="VkCopyCommandTransformInfoQCOM" structextends="VkBufferImageCopy2,VkImageBlit2">
<member values="VK_STRUCTURE_TYPE_COPY_COMMAND_TRANSFORM_INFO_QCOM"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true" noautovalidity="true">const <type>void</type>* <name>pNext</name></member>
<member noautovalidity="true"><type>VkSurfaceTransformFlagBitsKHR</type> <name>transform</name></member>
@@ -4880,7 +5390,7 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkRect2D</type> <name>renderArea</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceDiagnosticsConfigFeaturesNV" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DIAGNOSTICS_CONFIG_FEATURES_NV"><type>VkStructureType</type><name>sType</name></member>
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DIAGNOSTICS_CONFIG_FEATURES_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>diagnosticsConfig</name></member>
</type>
@@ -4889,24 +5399,44 @@ typedef void <name>CAMetalLayer</name>;
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member optional="true"><type>VkDeviceDiagnosticsConfigFlagsNV</type> <name>flags</name></member>
</type>
+ <type category="struct" name="VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>shaderZeroInitializeWorkgroupMemory</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeaturesKHR" alias="VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures"/>
+ <type category="struct" name="VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_FEATURES_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>shaderSubgroupUniformControlFlow</name></member>
+ </type>
<type category="struct" name="VkPhysicalDeviceRobustness2FeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>robustBufferAccess2</name></member>
<member><type>VkBool32</type> <name>robustImageAccess2</name></member>
<member><type>VkBool32</type> <name>nullDescriptor</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceRobustness2PropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkDeviceSize</type> <name>robustStorageBufferAccessSizeAlignment</name></member>
- <member><type>VkDeviceSize</type> <name>robustUniformBufferAccessSizeAlignment</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="min,pot"><type>VkDeviceSize</type> <name>robustStorageBufferAccessSizeAlignment</name></member>
+ <member limittype="min,pot"><type>VkDeviceSize</type> <name>robustUniformBufferAccessSizeAlignment</name></member>
</type>
- <type category="struct" name="VkPhysicalDeviceImageRobustnessFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <type category="struct" name="VkPhysicalDeviceImageRobustnessFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>robustImageAccess</name></member>
</type>
+ <type category="struct" name="VkPhysicalDeviceImageRobustnessFeaturesEXT" alias="VkPhysicalDeviceImageRobustnessFeatures"/>
+ <type category="struct" name="VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>workgroupMemoryExplicitLayout</name></member>
+ <member><type>VkBool32</type> <name>workgroupMemoryExplicitLayoutScalarBlockLayout</name></member>
+ <member><type>VkBool32</type> <name>workgroupMemoryExplicitLayout8BitAccess</name></member>
+ <member><type>VkBool32</type> <name>workgroupMemoryExplicitLayout16BitAccess</name></member>
+ </type>
<type category="struct" name="VkPhysicalDevicePortabilitySubsetFeaturesKHR" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
@@ -4929,114 +5459,130 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDevicePortabilitySubsetPropertiesKHR" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>uint32_t</type> <name>minVertexInputBindingStrideAlignment</name></member>
+ <member limittype="min,pot"><type>uint32_t</type> <name>minVertexInputBindingStrideAlignment</name></member>
</type>
<type category="struct" name="VkPhysicalDevice4444FormatsFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>formatA4R4G4B4</name></member>
<member><type>VkBool32</type> <name>formatA4B4G4R4</name></member>
</type>
- <type category="struct" name="VkBufferCopy2KHR">
- <member values="VK_STRUCTURE_TYPE_BUFFER_COPY_2_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkDeviceSize</type> <name>srcOffset</name><comment>Specified in bytes</comment></member>
- <member><type>VkDeviceSize</type> <name>dstOffset</name><comment>Specified in bytes</comment></member>
- <member noautovalidity="true"><type>VkDeviceSize</type> <name>size</name><comment>Specified in bytes</comment></member>
- </type>
- <type category="struct" name="VkImageCopy2KHR">
- <member values="VK_STRUCTURE_TYPE_IMAGE_COPY_2_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkImageSubresourceLayers</type> <name>srcSubresource</name></member>
- <member><type>VkOffset3D</type> <name>srcOffset</name><comment>Specified in pixels for both compressed and uncompressed images</comment></member>
- <member><type>VkImageSubresourceLayers</type> <name>dstSubresource</name></member>
- <member><type>VkOffset3D</type> <name>dstOffset</name><comment>Specified in pixels for both compressed and uncompressed images</comment></member>
- <member><type>VkExtent3D</type> <name>extent</name><comment>Specified in pixels for both compressed and uncompressed images</comment></member>
- </type>
- <type category="struct" name="VkImageBlit2KHR">
- <member values="VK_STRUCTURE_TYPE_IMAGE_BLIT_2_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkImageSubresourceLayers</type> <name>srcSubresource</name></member>
- <member><type>VkOffset3D</type> <name>srcOffsets</name>[2]<comment>Specified in pixels for both compressed and uncompressed images</comment></member>
- <member><type>VkImageSubresourceLayers</type> <name>dstSubresource</name></member>
- <member><type>VkOffset3D</type> <name>dstOffsets</name>[2]<comment>Specified in pixels for both compressed and uncompressed images</comment></member>
- </type>
- <type category="struct" name="VkBufferImageCopy2KHR">
- <member values="VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkDeviceSize</type> <name>bufferOffset</name><comment>Specified in bytes</comment></member>
- <member><type>uint32_t</type> <name>bufferRowLength</name><comment>Specified in texels</comment></member>
- <member><type>uint32_t</type> <name>bufferImageHeight</name></member>
- <member><type>VkImageSubresourceLayers</type> <name>imageSubresource</name></member>
- <member><type>VkOffset3D</type> <name>imageOffset</name><comment>Specified in pixels for both compressed and uncompressed images</comment></member>
- <member><type>VkExtent3D</type> <name>imageExtent</name><comment>Specified in pixels for both compressed and uncompressed images</comment></member>
- </type>
- <type category="struct" name="VkImageResolve2KHR">
- <member values="VK_STRUCTURE_TYPE_IMAGE_RESOLVE_2_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkImageSubresourceLayers</type> <name>srcSubresource</name></member>
- <member><type>VkOffset3D</type> <name>srcOffset</name></member>
- <member><type>VkImageSubresourceLayers</type> <name>dstSubresource</name></member>
- <member><type>VkOffset3D</type> <name>dstOffset</name></member>
- <member><type>VkExtent3D</type> <name>extent</name></member>
- </type>
- <type category="struct" name="VkCopyBufferInfo2KHR">
- <member values="VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkBuffer</type> <name>srcBuffer</name></member>
- <member><type>VkBuffer</type> <name>dstBuffer</name></member>
- <member><type>uint32_t</type> <name>regionCount</name></member>
- <member len="regionCount">const <type>VkBufferCopy2KHR</type>* <name>pRegions</name></member>
+ <type category="struct" name="VkPhysicalDeviceSubpassShadingFeaturesHUAWEI" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_SHADING_FEATURES_HUAWEI"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>subpassShading</name></member>
</type>
- <type category="struct" name="VkCopyImageInfo2KHR">
- <member values="VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkImage</type> <name>srcImage</name></member>
- <member><type>VkImageLayout</type> <name>srcImageLayout</name></member>
- <member><type>VkImage</type> <name>dstImage</name></member>
- <member><type>VkImageLayout</type> <name>dstImageLayout</name></member>
- <member><type>uint32_t</type> <name>regionCount</name></member>
- <member len="regionCount">const <type>VkImageCopy2KHR</type>* <name>pRegions</name></member>
- </type>
- <type category="struct" name="VkBlitImageInfo2KHR">
- <member values="VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkImage</type> <name>srcImage</name></member>
- <member><type>VkImageLayout</type> <name>srcImageLayout</name></member>
- <member><type>VkImage</type> <name>dstImage</name></member>
- <member><type>VkImageLayout</type> <name>dstImageLayout</name></member>
- <member><type>uint32_t</type> <name>regionCount</name></member>
- <member len="regionCount">const <type>VkImageBlit2KHR</type>* <name>pRegions</name></member>
- <member><type>VkFilter</type> <name>filter</name></member>
- </type>
- <type category="struct" name="VkCopyBufferToImageInfo2KHR">
- <member values="VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkBuffer</type> <name>srcBuffer</name></member>
- <member><type>VkImage</type> <name>dstImage</name></member>
- <member><type>VkImageLayout</type> <name>dstImageLayout</name></member>
- <member><type>uint32_t</type> <name>regionCount</name></member>
- <member len="regionCount">const <type>VkBufferImageCopy2KHR</type>* <name>pRegions</name></member>
- </type>
- <type category="struct" name="VkCopyImageToBufferInfo2KHR">
- <member values="VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkImage</type> <name>srcImage</name></member>
- <member><type>VkImageLayout</type> <name>srcImageLayout</name></member>
- <member><type>VkBuffer</type> <name>dstBuffer</name></member>
- <member><type>uint32_t</type> <name>regionCount</name></member>
- <member len="regionCount">const <type>VkBufferImageCopy2KHR</type>* <name>pRegions</name></member>
- </type>
- <type category="struct" name="VkResolveImageInfo2KHR">
- <member values="VK_STRUCTURE_TYPE_RESOLVE_IMAGE_INFO_2_KHR"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkImage</type> <name>srcImage</name></member>
- <member><type>VkImageLayout</type> <name>srcImageLayout</name></member>
- <member><type>VkImage</type> <name>dstImage</name></member>
- <member><type>VkImageLayout</type> <name>dstImageLayout</name></member>
- <member><type>uint32_t</type> <name>regionCount</name></member>
- <member len="regionCount">const <type>VkImageResolve2KHR</type>* <name>pRegions</name></member>
+ <type category="struct" name="VkBufferCopy2">
+ <member values="VK_STRUCTURE_TYPE_BUFFER_COPY_2"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkDeviceSize</type> <name>srcOffset</name><comment>Specified in bytes</comment></member>
+ <member><type>VkDeviceSize</type> <name>dstOffset</name><comment>Specified in bytes</comment></member>
+ <member noautovalidity="true"><type>VkDeviceSize</type> <name>size</name><comment>Specified in bytes</comment></member>
</type>
+ <type category="struct" name="VkBufferCopy2KHR" alias="VkBufferCopy2"/>
+ <type category="struct" name="VkImageCopy2">
+ <member values="VK_STRUCTURE_TYPE_IMAGE_COPY_2"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkImageSubresourceLayers</type> <name>srcSubresource</name></member>
+ <member><type>VkOffset3D</type> <name>srcOffset</name><comment>Specified in pixels for both compressed and uncompressed images</comment></member>
+ <member><type>VkImageSubresourceLayers</type> <name>dstSubresource</name></member>
+ <member><type>VkOffset3D</type> <name>dstOffset</name><comment>Specified in pixels for both compressed and uncompressed images</comment></member>
+ <member><type>VkExtent3D</type> <name>extent</name><comment>Specified in pixels for both compressed and uncompressed images</comment></member>
+ </type>
+ <type category="struct" name="VkImageCopy2KHR" alias="VkImageCopy2"/>
+ <type category="struct" name="VkImageBlit2">
+ <member values="VK_STRUCTURE_TYPE_IMAGE_BLIT_2"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkImageSubresourceLayers</type> <name>srcSubresource</name></member>
+ <member><type>VkOffset3D</type> <name>srcOffsets</name>[2]<comment>Specified in pixels for both compressed and uncompressed images</comment></member>
+ <member><type>VkImageSubresourceLayers</type> <name>dstSubresource</name></member>
+ <member><type>VkOffset3D</type> <name>dstOffsets</name>[2]<comment>Specified in pixels for both compressed and uncompressed images</comment></member>
+ </type>
+ <type category="struct" name="VkImageBlit2KHR" alias="VkImageBlit2"/>
+ <type category="struct" name="VkBufferImageCopy2">
+ <member values="VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkDeviceSize</type> <name>bufferOffset</name><comment>Specified in bytes</comment></member>
+ <member><type>uint32_t</type> <name>bufferRowLength</name><comment>Specified in texels</comment></member>
+ <member><type>uint32_t</type> <name>bufferImageHeight</name></member>
+ <member><type>VkImageSubresourceLayers</type> <name>imageSubresource</name></member>
+ <member><type>VkOffset3D</type> <name>imageOffset</name><comment>Specified in pixels for both compressed and uncompressed images</comment></member>
+ <member><type>VkExtent3D</type> <name>imageExtent</name><comment>Specified in pixels for both compressed and uncompressed images</comment></member>
+ </type>
+ <type category="struct" name="VkBufferImageCopy2KHR" alias="VkBufferImageCopy2"/>
+ <type category="struct" name="VkImageResolve2">
+ <member values="VK_STRUCTURE_TYPE_IMAGE_RESOLVE_2"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkImageSubresourceLayers</type> <name>srcSubresource</name></member>
+ <member><type>VkOffset3D</type> <name>srcOffset</name></member>
+ <member><type>VkImageSubresourceLayers</type> <name>dstSubresource</name></member>
+ <member><type>VkOffset3D</type> <name>dstOffset</name></member>
+ <member><type>VkExtent3D</type> <name>extent</name></member>
+ </type>
+ <type category="struct" name="VkImageResolve2KHR" alias="VkImageResolve2"/>
+ <type category="struct" name="VkCopyBufferInfo2">
+ <member values="VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkBuffer</type> <name>srcBuffer</name></member>
+ <member><type>VkBuffer</type> <name>dstBuffer</name></member>
+ <member><type>uint32_t</type> <name>regionCount</name></member>
+ <member len="regionCount">const <type>VkBufferCopy2</type>* <name>pRegions</name></member>
+ </type>
+ <type category="struct" name="VkCopyBufferInfo2KHR" alias="VkCopyBufferInfo2"/>
+ <type category="struct" name="VkCopyImageInfo2">
+ <member values="VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkImage</type> <name>srcImage</name></member>
+ <member><type>VkImageLayout</type> <name>srcImageLayout</name></member>
+ <member><type>VkImage</type> <name>dstImage</name></member>
+ <member><type>VkImageLayout</type> <name>dstImageLayout</name></member>
+ <member><type>uint32_t</type> <name>regionCount</name></member>
+ <member len="regionCount">const <type>VkImageCopy2</type>* <name>pRegions</name></member>
+ </type>
+ <type category="struct" name="VkCopyImageInfo2KHR" alias="VkCopyImageInfo2"/>
+ <type category="struct" name="VkBlitImageInfo2">
+ <member values="VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkImage</type> <name>srcImage</name></member>
+ <member><type>VkImageLayout</type> <name>srcImageLayout</name></member>
+ <member><type>VkImage</type> <name>dstImage</name></member>
+ <member><type>VkImageLayout</type> <name>dstImageLayout</name></member>
+ <member><type>uint32_t</type> <name>regionCount</name></member>
+ <member len="regionCount">const <type>VkImageBlit2</type>* <name>pRegions</name></member>
+ <member><type>VkFilter</type> <name>filter</name></member>
+ </type>
+ <type category="struct" name="VkBlitImageInfo2KHR" alias="VkBlitImageInfo2"/>
+ <type category="struct" name="VkCopyBufferToImageInfo2">
+ <member values="VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkBuffer</type> <name>srcBuffer</name></member>
+ <member><type>VkImage</type> <name>dstImage</name></member>
+ <member><type>VkImageLayout</type> <name>dstImageLayout</name></member>
+ <member><type>uint32_t</type> <name>regionCount</name></member>
+ <member len="regionCount">const <type>VkBufferImageCopy2</type>* <name>pRegions</name></member>
+ </type>
+ <type category="struct" name="VkCopyBufferToImageInfo2KHR" alias="VkCopyBufferToImageInfo2"/>
+ <type category="struct" name="VkCopyImageToBufferInfo2">
+ <member values="VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkImage</type> <name>srcImage</name></member>
+ <member><type>VkImageLayout</type> <name>srcImageLayout</name></member>
+ <member><type>VkBuffer</type> <name>dstBuffer</name></member>
+ <member><type>uint32_t</type> <name>regionCount</name></member>
+ <member len="regionCount">const <type>VkBufferImageCopy2</type>* <name>pRegions</name></member>
+ </type>
+ <type category="struct" name="VkCopyImageToBufferInfo2KHR" alias="VkCopyImageToBufferInfo2"/>
+ <type category="struct" name="VkResolveImageInfo2">
+ <member values="VK_STRUCTURE_TYPE_RESOLVE_IMAGE_INFO_2"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkImage</type> <name>srcImage</name></member>
+ <member><type>VkImageLayout</type> <name>srcImageLayout</name></member>
+ <member><type>VkImage</type> <name>dstImage</name></member>
+ <member><type>VkImageLayout</type> <name>dstImageLayout</name></member>
+ <member><type>uint32_t</type> <name>regionCount</name></member>
+ <member len="regionCount">const <type>VkImageResolve2</type>* <name>pRegions</name></member>
+ </type>
+ <type category="struct" name="VkResolveImageInfo2KHR" alias="VkResolveImageInfo2"/>
<type category="struct" name="VkPhysicalDeviceShaderImageAtomicInt64FeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_IMAGE_ATOMIC_INT64_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
@@ -5046,14 +5592,14 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkFragmentShadingRateAttachmentInfoKHR" structextends="VkSubpassDescription2">
<member values="VK_STRUCTURE_TYPE_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member>const <type>VkAttachmentReference2</type>* <name>pFragmentShadingRateAttachment</name></member>
+ <member optional="true">const <type>VkAttachmentReference2</type>* <name>pFragmentShadingRateAttachment</name></member>
<member><type>VkExtent2D</type> <name>shadingRateAttachmentTexelSize</name></member>
</type>
<type category="struct" name="VkPipelineFragmentShadingRateStateCreateInfoKHR" structextends="VkGraphicsPipelineCreateInfo">
<member values="VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_SHADING_RATE_STATE_CREATE_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>VkExtent2D</type> <name>fragmentSize</name></member>
- <member><type>VkFragmentShadingRateCombinerOpKHR</type> <name>combinerOps</name>[2]</member>
+ <member noautovalidity="true"><type>VkFragmentShadingRateCombinerOpKHR</type> <name>combinerOps</name>[2]</member>
</type>
<type category="struct" name="VkPhysicalDeviceFragmentShadingRateFeaturesKHR" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR"><type>VkStructureType</type> <name>sType</name></member>
@@ -5065,23 +5611,23 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceFragmentShadingRatePropertiesKHR" structextends="VkPhysicalDeviceProperties2" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkExtent2D</type> <name>minFragmentShadingRateAttachmentTexelSize</name></member>
- <member><type>VkExtent2D</type> <name>maxFragmentShadingRateAttachmentTexelSize</name></member>
- <member><type>uint32_t</type> <name>maxFragmentShadingRateAttachmentTexelSizeAspectRatio</name></member>
- <member><type>VkBool32</type> <name>primitiveFragmentShadingRateWithMultipleViewports</name></member>
- <member><type>VkBool32</type> <name>layeredShadingRateAttachments</name></member>
- <member><type>VkBool32</type> <name>fragmentShadingRateNonTrivialCombinerOps</name></member>
- <member><type>VkExtent2D</type> <name>maxFragmentSize</name></member>
- <member><type>uint32_t</type> <name>maxFragmentSizeAspectRatio</name></member>
- <member><type>uint32_t</type> <name>maxFragmentShadingRateCoverageSamples</name></member>
- <member><type>VkSampleCountFlagBits</type> <name>maxFragmentShadingRateRasterizationSamples</name></member>
- <member><type>VkBool32</type> <name>fragmentShadingRateWithShaderDepthStencilWrites</name></member>
- <member><type>VkBool32</type> <name>fragmentShadingRateWithSampleMask</name></member>
- <member><type>VkBool32</type> <name>fragmentShadingRateWithShaderSampleMask</name></member>
- <member><type>VkBool32</type> <name>fragmentShadingRateWithConservativeRasterization</name></member>
- <member><type>VkBool32</type> <name>fragmentShadingRateWithFragmentShaderInterlock</name></member>
- <member><type>VkBool32</type> <name>fragmentShadingRateWithCustomSampleLocations</name></member>
- <member><type>VkBool32</type> <name>fragmentShadingRateStrictMultiplyCombiner</name></member>
+ <member limittype="min"><type>VkExtent2D</type> <name>minFragmentShadingRateAttachmentTexelSize</name></member>
+ <member limittype="max"><type>VkExtent2D</type> <name>maxFragmentShadingRateAttachmentTexelSize</name></member>
+ <member limittype="max,pot"><type>uint32_t</type> <name>maxFragmentShadingRateAttachmentTexelSizeAspectRatio</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>primitiveFragmentShadingRateWithMultipleViewports</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>layeredShadingRateAttachments</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>fragmentShadingRateNonTrivialCombinerOps</name></member>
+ <member limittype="max"><type>VkExtent2D</type> <name>maxFragmentSize</name></member>
+ <member limittype="max,pot"><type>uint32_t</type> <name>maxFragmentSizeAspectRatio</name></member>
+ <member limittype="max"><type>uint32_t</type> <name>maxFragmentShadingRateCoverageSamples</name></member>
+ <member limittype="max"><type>VkSampleCountFlagBits</type> <name>maxFragmentShadingRateRasterizationSamples</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>fragmentShadingRateWithShaderDepthStencilWrites</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>fragmentShadingRateWithSampleMask</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>fragmentShadingRateWithShaderSampleMask</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>fragmentShadingRateWithConservativeRasterization</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>fragmentShadingRateWithFragmentShaderInterlock</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>fragmentShadingRateWithCustomSampleLocations</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>fragmentShadingRateStrictMultiplyCombiner</name></member>
</type>
<type category="struct" name="VkPhysicalDeviceFragmentShadingRateKHR" returnedonly="true">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_KHR"><type>VkStructureType</type> <name>sType</name></member>
@@ -5089,14 +5635,15 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkSampleCountFlags</type> <name>sampleCounts</name></member>
<member><type>VkExtent2D</type> <name>fragmentSize</name></member>
</type>
- <type category="struct" name="VkPhysicalDeviceShaderTerminateInvocationFeaturesKHR" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
- <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES_KHR"><type>VkStructureType</type><name>sType</name></member>
+ <type category="struct" name="VkPhysicalDeviceShaderTerminateInvocationFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkBool32</type> <name>shaderTerminateInvocation</name></member>
+ <member><type>VkBool32</type> <name>shaderTerminateInvocation</name></member>
</type>
+ <type category="struct" name="VkPhysicalDeviceShaderTerminateInvocationFeaturesKHR" alias="VkPhysicalDeviceShaderTerminateInvocationFeatures"/>
<type category="struct" name="VkPhysicalDeviceFragmentShadingRateEnumsFeaturesNV" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_ENUMS_FEATURES_NV"><type>VkStructureType</type> <name>sType</name></member>
- <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>fragmentShadingRateEnums</name></member>
<member><type>VkBool32</type> <name>supersampleFragmentShadingRates</name></member>
<member><type>VkBool32</type> <name>noInvocationFragmentShadingRates</name></member>
@@ -5104,14 +5651,14 @@ typedef void <name>CAMetalLayer</name>;
<type category="struct" name="VkPhysicalDeviceFragmentShadingRateEnumsPropertiesNV" structextends="VkPhysicalDeviceProperties2">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_ENUMS_PROPERTIES_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true"><type>void</type>* <name>pNext</name></member>
- <member><type>VkSampleCountFlagBits</type> <name>maxFragmentShadingRateInvocationCount</name></member>
+ <member limittype="max"><type>VkSampleCountFlagBits</type> <name>maxFragmentShadingRateInvocationCount</name></member>
</type>
<type category="struct" name="VkPipelineFragmentShadingRateEnumStateCreateInfoNV" structextends="VkGraphicsPipelineCreateInfo">
<member values="VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_SHADING_RATE_ENUM_STATE_CREATE_INFO_NV"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
- <member><type>VkFragmentShadingRateTypeNV</type> <name>shadingRateType</name></member>
- <member><type>VkFragmentShadingRateNV</type> <name>shadingRate</name></member>
- <member><type>VkFragmentShadingRateCombinerOpKHR</type> <name>combinerOps</name>[2]</member>
+ <member noautovalidity="true"><type>VkFragmentShadingRateTypeNV</type> <name>shadingRateType</name></member>
+ <member noautovalidity="true"><type>VkFragmentShadingRateNV</type> <name>shadingRate</name></member>
+ <member noautovalidity="true"><type>VkFragmentShadingRateCombinerOpKHR</type> <name>combinerOps</name>[2]</member>
</type>
<type category="struct" name="VkAccelerationStructureBuildSizesInfoKHR">
<member values="VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
@@ -5120,9 +5667,15 @@ typedef void <name>CAMetalLayer</name>;
<member><type>VkDeviceSize</type> <name>updateScratchSize</name></member>
<member><type>VkDeviceSize</type> <name>buildScratchSize</name></member>
</type>
+ <type category="struct" name="VkPhysicalDeviceImage2DViewOf3DFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_2D_VIEW_OF_3D_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>image2DViewOf3D</name></member>
+ <member><type>VkBool32</type> <name>sampler2DViewOf3D</name></member>
+ </type>
<type category="struct" name="VkPhysicalDeviceMutableDescriptorTypeFeaturesVALVE" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_VALVE"><type>VkStructureType</type> <name>sType</name></member>
- <member noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
<member><type>VkBool32</type> <name>mutableDescriptorType</name></member>
</type>
<type category="struct" name="VkMutableDescriptorTypeListVALVE">
@@ -5131,43 +5684,1446 @@ typedef void <name>CAMetalLayer</name>;
</type>
<type category="struct" name="VkMutableDescriptorTypeCreateInfoVALVE" structextends="VkDescriptorSetLayoutCreateInfo,VkDescriptorPoolCreateInfo">
<member values="VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_VALVE"><type>VkStructureType</type> <name>sType</name></member>
- <member>const <type>void</type>* <name>pNext</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
<member optional="true"><type>uint32_t</type> <name>mutableDescriptorTypeListCount</name></member>
<member len="mutableDescriptorTypeListCount">const <type>VkMutableDescriptorTypeListVALVE</type>* <name>pMutableDescriptorTypeLists</name></member>
</type>
+ <type category="struct" name="VkPhysicalDeviceDepthClipControlFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>depthClipControl</name></member>
+ </type>
+ <type category="struct" name="VkPipelineViewportDepthClipControlCreateInfoEXT" structextends="VkPipelineViewportStateCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>negativeOneToOne</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>vertexInputDynamicState</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceExternalMemoryRDMAFeaturesNV" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_RDMA_FEATURES_NV"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>externalMemoryRDMA</name></member>
+ </type>
+ <type category="struct" name="VkVertexInputBindingDescription2EXT">
+ <member values="VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>binding</name></member>
+ <member><type>uint32_t</type> <name>stride</name></member>
+ <member><type>VkVertexInputRate</type> <name>inputRate</name></member>
+ <member><type>uint32_t</type> <name>divisor</name></member>
+ </type>
+ <type category="struct" name="VkVertexInputAttributeDescription2EXT">
+ <member values="VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>location</name><comment>location of the shader vertex attrib</comment></member>
+ <member><type>uint32_t</type> <name>binding</name><comment>Vertex buffer binding id</comment></member>
+ <member><type>VkFormat</type> <name>format</name><comment>format of source data</comment></member>
+ <member><type>uint32_t</type> <name>offset</name><comment>Offset of first element in bytes from base of vertex</comment></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceColorWriteEnableFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COLOR_WRITE_ENABLE_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>colorWriteEnable</name></member>
+ </type>
+ <type category="struct" name="VkPipelineColorWriteCreateInfoEXT" structextends="VkPipelineColorBlendStateCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PIPELINE_COLOR_WRITE_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>uint32_t</type> <name>attachmentCount</name><comment># of pAttachments</comment></member>
+ <member len="attachmentCount">const <type>VkBool32</type>* <name>pColorWriteEnables</name></member>
+ </type>
+ <type category="struct" name="VkMemoryBarrier2" structextends="VkSubpassDependency2">
+ <member values="VK_STRUCTURE_TYPE_MEMORY_BARRIER_2"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkPipelineStageFlags2</type> <name>srcStageMask</name></member>
+ <member optional="true"><type>VkAccessFlags2</type> <name>srcAccessMask</name></member>
+ <member optional="true"><type>VkPipelineStageFlags2</type> <name>dstStageMask</name></member>
+ <member optional="true"><type>VkAccessFlags2</type> <name>dstAccessMask</name></member>
+ </type>
+ <type category="struct" name="VkMemoryBarrier2KHR" alias="VkMemoryBarrier2"/>
+ <type category="struct" name="VkImageMemoryBarrier2">
+ <member values="VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkPipelineStageFlags2</type> <name>srcStageMask</name></member>
+ <member optional="true"><type>VkAccessFlags2</type> <name>srcAccessMask</name></member>
+ <member optional="true"><type>VkPipelineStageFlags2</type> <name>dstStageMask</name></member>
+ <member optional="true"><type>VkAccessFlags2</type> <name>dstAccessMask</name></member>
+ <member><type>VkImageLayout</type> <name>oldLayout</name></member>
+ <member><type>VkImageLayout</type> <name>newLayout</name></member>
+ <member><type>uint32_t</type> <name>srcQueueFamilyIndex</name></member>
+ <member><type>uint32_t</type> <name>dstQueueFamilyIndex</name></member>
+ <member><type>VkImage</type> <name>image</name></member>
+ <member><type>VkImageSubresourceRange</type> <name>subresourceRange</name></member>
+ </type>
+ <type category="struct" name="VkImageMemoryBarrier2KHR" alias="VkImageMemoryBarrier2"/>
+ <type category="struct" name="VkBufferMemoryBarrier2">
+ <member values="VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkPipelineStageFlags2</type> <name>srcStageMask</name></member>
+ <member optional="true"><type>VkAccessFlags2</type> <name>srcAccessMask</name></member>
+ <member optional="true"><type>VkPipelineStageFlags2</type> <name>dstStageMask</name></member>
+ <member optional="true"><type>VkAccessFlags2</type> <name>dstAccessMask</name></member>
+ <member><type>uint32_t</type> <name>srcQueueFamilyIndex</name></member>
+ <member><type>uint32_t</type> <name>dstQueueFamilyIndex</name></member>
+ <member><type>VkBuffer</type> <name>buffer</name></member>
+ <member><type>VkDeviceSize</type> <name>offset</name></member>
+ <member><type>VkDeviceSize</type> <name>size</name></member>
+ </type>
+ <type category="struct" name="VkBufferMemoryBarrier2KHR" alias="VkBufferMemoryBarrier2"/>
+ <type category="struct" name="VkDependencyInfo">
+ <member values="VK_STRUCTURE_TYPE_DEPENDENCY_INFO"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkDependencyFlags</type> <name>dependencyFlags</name></member>
+ <member optional="true"><type>uint32_t</type> <name>memoryBarrierCount</name></member>
+ <member len="memoryBarrierCount">const <type>VkMemoryBarrier2</type>* <name>pMemoryBarriers</name></member>
+ <member optional="true"><type>uint32_t</type> <name>bufferMemoryBarrierCount</name></member>
+ <member len="bufferMemoryBarrierCount">const <type>VkBufferMemoryBarrier2</type>* <name>pBufferMemoryBarriers</name></member>
+ <member optional="true"><type>uint32_t</type> <name>imageMemoryBarrierCount</name></member>
+ <member len="imageMemoryBarrierCount">const <type>VkImageMemoryBarrier2</type>* <name>pImageMemoryBarriers</name></member>
+ </type>
+ <type category="struct" name="VkDependencyInfoKHR" alias="VkDependencyInfo"/>
+ <type category="struct" name="VkSemaphoreSubmitInfo">
+ <member values="VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkSemaphore</type> <name>semaphore</name></member>
+ <member><type>uint64_t</type> <name>value</name></member>
+ <member optional="true"><type>VkPipelineStageFlags2</type> <name>stageMask</name></member>
+ <member><type>uint32_t</type> <name>deviceIndex</name></member>
+ </type>
+ <type category="struct" name="VkSemaphoreSubmitInfoKHR" alias="VkSemaphoreSubmitInfo"/>
+ <type category="struct" name="VkCommandBufferSubmitInfo">
+ <member values="VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkCommandBuffer</type> <name>commandBuffer</name></member>
+ <member><type>uint32_t</type> <name>deviceMask</name></member>
+ </type>
+ <type category="struct" name="VkCommandBufferSubmitInfoKHR" alias="VkCommandBufferSubmitInfo"/>
+ <type category="struct" name="VkSubmitInfo2">
+ <member values="VK_STRUCTURE_TYPE_SUBMIT_INFO_2"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkSubmitFlags</type> <name>flags</name></member>
+ <member optional="true"><type>uint32_t</type> <name>waitSemaphoreInfoCount</name></member>
+ <member len="waitSemaphoreInfoCount">const <type>VkSemaphoreSubmitInfo</type>* <name>pWaitSemaphoreInfos</name></member>
+ <member optional="true"><type>uint32_t</type> <name>commandBufferInfoCount</name></member>
+ <member len="commandBufferInfoCount">const <type>VkCommandBufferSubmitInfo</type>* <name>pCommandBufferInfos</name></member>
+ <member optional="true"><type>uint32_t</type> <name>signalSemaphoreInfoCount</name></member>
+ <member len="signalSemaphoreInfoCount">const <type>VkSemaphoreSubmitInfo</type>* <name>pSignalSemaphoreInfos</name></member>
+ </type>
+ <type category="struct" name="VkSubmitInfo2KHR" alias="VkSubmitInfo2"/>
+ <type category="struct" name="VkQueueFamilyCheckpointProperties2NV" structextends="VkQueueFamilyProperties2" returnedonly="true">
+ <member values="VK_STRUCTURE_TYPE_QUEUE_FAMILY_CHECKPOINT_PROPERTIES_2_NV"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="bitmask"><type>VkPipelineStageFlags2</type> <name>checkpointExecutionStageMask</name></member>
+ </type>
+ <type category="struct" name="VkCheckpointData2NV" returnedonly="true">
+ <member values="VK_STRUCTURE_TYPE_CHECKPOINT_DATA_2_NV"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkPipelineStageFlags2</type> <name>stage</name></member>
+ <member noautovalidity="true"><type>void</type>* <name>pCheckpointMarker</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceSynchronization2Features" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>synchronization2</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceSynchronization2FeaturesKHR" alias="VkPhysicalDeviceSynchronization2Features"/>
+ <type category="struct" name="VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>primitivesGeneratedQuery</name></member>
+ <member><type>VkBool32</type> <name>primitivesGeneratedQueryWithRasterizerDiscard</name></member>
+ <member><type>VkBool32</type> <name>primitivesGeneratedQueryWithNonZeroStreams</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>multisampledRenderToSingleSampled</name></member>
+ </type>
+ <type category="struct" name="VkSubpassResolvePerformanceQueryEXT" returnedonly="true" structextends="VkFormatProperties2">
+ <member values="VK_STRUCTURE_TYPE_SUBPASS_RESOLVE_PERFORMANCE_QUERY_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>optimal</name></member>
+ </type>
+ <type category="struct" name="VkMultisampledRenderToSingleSampledInfoEXT" structextends="VkSubpassDescription2,VkRenderingInfo">
+ <member values="VK_STRUCTURE_TYPE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>multisampledRenderToSingleSampledEnable</name></member>
+ <member><type>VkSampleCountFlagBits</type> <name>rasterizationSamples</name></member>
+ </type>
+ <type category="struct" name="VkVideoQueueFamilyProperties2KHR" structextends="VkQueueFamilyProperties2">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_QUEUE_FAMILY_PROPERTIES_2_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="bitmask"><type>VkVideoCodecOperationFlagsKHR</type> <name>videoCodecOperations</name></member>
+ </type>
+ <type category="struct" name="VkQueueFamilyQueryResultStatusProperties2KHR" structextends="VkQueueFamilyProperties2">
+ <member values="VK_STRUCTURE_TYPE_QUEUE_FAMILY_QUERY_RESULT_STATUS_PROPERTIES_2_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>queryResultStatusSupport</name></member>
+ </type>
+ <type category="struct" name="VkVideoProfilesKHR" structextends="VkPhysicalDeviceImageFormatInfo2,VkPhysicalDeviceVideoFormatInfoKHR,VkImageCreateInfo,VkBufferCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_PROFILES_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>uint32_t</type> <name>profileCount</name></member>
+ <member len="profileCount">const <type>VkVideoProfileKHR</type>* <name>pProfiles</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceVideoFormatInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_FORMAT_INFO_KHR"> <type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkImageUsageFlags</type> <name>imageUsage</name></member>
+ </type>
+ <type category="struct" name="VkVideoFormatPropertiesKHR" returnedonly="true">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_FORMAT_PROPERTIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkFormat</type> <name>format</name></member>
+ <member><type>VkComponentMapping</type> <name>componentMapping</name></member>
+ <member><type>VkImageCreateFlags</type> <name>imageCreateFlags</name></member>
+ <member><type>VkImageType</type> <name>imageType</name></member>
+ <member><type>VkImageTiling</type> <name>imageTiling</name></member>
+ <member><type>VkImageUsageFlags</type> <name>imageUsageFlags</name></member>
+ </type>
+ <type category="struct" name="VkVideoProfileKHR" structextends="VkQueryPoolCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_PROFILE_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkVideoCodecOperationFlagBitsKHR</type> <name>videoCodecOperation</name></member>
+ <member><type>VkVideoChromaSubsamplingFlagsKHR</type> <name>chromaSubsampling</name></member>
+ <member><type>VkVideoComponentBitDepthFlagsKHR</type> <name>lumaBitDepth</name></member>
+ <member><type>VkVideoComponentBitDepthFlagsKHR</type> <name>chromaBitDepth</name></member>
+ </type>
+ <type category="struct" name="VkVideoCapabilitiesKHR" returnedonly="true">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_CAPABILITIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkVideoCapabilityFlagsKHR</type> <name>capabilityFlags</name></member>
+ <member><type>VkDeviceSize</type> <name>minBitstreamBufferOffsetAlignment</name></member>
+ <member><type>VkDeviceSize</type> <name>minBitstreamBufferSizeAlignment</name></member>
+ <member><type>VkExtent2D</type> <name>videoPictureExtentGranularity</name></member>
+ <member><type>VkExtent2D</type> <name>minExtent</name></member>
+ <member><type>VkExtent2D</type> <name>maxExtent</name></member>
+ <member><type>uint32_t</type> <name>maxReferencePicturesSlotsCount</name></member>
+ <member><type>uint32_t</type> <name>maxReferencePicturesActiveCount</name></member>
+ <member><type>VkExtensionProperties</type> <name>stdHeaderVersion</name></member>
+ </type>
+ <type category="struct" name="VkVideoGetMemoryPropertiesKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_GET_MEMORY_PROPERTIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>memoryBindIndex</name></member>
+ <member><type>VkMemoryRequirements2</type>* <name>pMemoryRequirements</name></member>
+ </type>
+ <type category="struct" name="VkVideoBindMemoryKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_BIND_MEMORY_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>memoryBindIndex</name></member>
+ <member><type>VkDeviceMemory</type> <name>memory</name></member>
+ <member><type>VkDeviceSize</type> <name>memoryOffset</name></member>
+ <member><type>VkDeviceSize</type> <name>memorySize</name></member>
+ </type>
+ <type category="struct" name="VkVideoPictureResourceKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkOffset2D</type> <name>codedOffset</name><comment>The offset to be used for the picture resource, currently only used in field mode</comment></member>
+ <member><type>VkExtent2D</type> <name>codedExtent</name><comment>The extent to be used for the picture resource</comment></member>
+ <member><type>uint32_t</type> <name>baseArrayLayer</name><comment>The first array layer to be accessed for the Decode or Encode Operations</comment></member>
+ <member><type>VkImageView</type> <name>imageViewBinding</name><comment>The ImageView binding of the resource</comment></member>
+ </type>
+ <type category="struct" name="VkVideoReferenceSlotKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>int8_t</type> <name>slotIndex</name><comment>The reference slot index</comment></member>
+ <member>const <type>VkVideoPictureResourceKHR</type>* <name>pPictureResource</name><comment>The reference picture resource</comment></member>
+ </type>
+ <type category="struct" name="VkVideoDecodeCapabilitiesKHR" returnedonly="true" structextends="VkVideoCapabilitiesKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_DECODE_CAPABILITIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member noautovalidity="true"><type>VkVideoDecodeCapabilityFlagsKHR</type> <name>flags</name></member>
+ </type>
+ <type category="struct" name="VkVideoDecodeInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_DECODE_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkVideoDecodeFlagsKHR</type> <name>flags</name></member>
+ <member><type>VkBuffer</type> <name>srcBuffer</name></member>
+ <member><type>VkDeviceSize</type> <name>srcBufferOffset</name></member>
+ <member><type>VkDeviceSize</type> <name>srcBufferRange</name></member>
+ <member><type>VkVideoPictureResourceKHR</type> <name>dstPictureResource</name></member>
+ <member>const <type>VkVideoReferenceSlotKHR</type>* <name>pSetupReferenceSlot</name></member>
+ <member optional="true"><type>uint32_t</type> <name>referenceSlotCount</name></member>
+ <member len="referenceSlotCount">const <type>VkVideoReferenceSlotKHR</type>* <name>pReferenceSlots</name></member>
+ </type>
+ <comment>Video Decode Codec Standard specific structures</comment>
+ <type category="include" name="vk_video/vulkan_video_codec_h264std.h">#include "vk_video/vulkan_video_codec_h264std.h"</type>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264ProfileIdc"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264Level"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264ChromaFormatIdc"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264PocType"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264SpsFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264ScalingLists"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264SequenceParameterSetVui"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264AspectRatioIdc"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264HrdParameters"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264SpsVuiFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264WeightedBipredIdc"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264PpsFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264SliceType"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264CabacInitIdc"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264DisableDeblockingFilterIdc"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264PictureType"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264ModificationOfPicNumsIdc"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264MemMgmtControlOp"/>
+ <type category="include" name="vk_video/vulkan_video_codec_h264std_decode.h">#include "vk_video/vulkan_video_codec_h264std_decode.h"</type>
+ <type requires="vk_video/vulkan_video_codec_h264std_decode.h" name="StdVideoDecodeH264PictureInfo"/>
+ <type requires="vk_video/vulkan_video_codec_h264std_decode.h" name="StdVideoDecodeH264ReferenceInfo"/>
+ <type requires="vk_video/vulkan_video_codec_h264std_decode.h" name="StdVideoDecodeH264Mvc"/>
+ <type requires="vk_video/vulkan_video_codec_h264std_decode.h" name="StdVideoDecodeH264PictureInfoFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h264std_decode.h" name="StdVideoDecodeH264ReferenceInfoFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h264std_decode.h" name="StdVideoDecodeH264MvcElement"/>
+ <type requires="vk_video/vulkan_video_codec_h264std_decode.h" name="StdVideoDecodeH264MvcElementFlags"/>
+ <type category="struct" name="VkVideoDecodeH264ProfileEXT" structextends="VkVideoProfileKHR,VkQueryPoolCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PROFILE_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>StdVideoH264ProfileIdc</type> <name>stdProfileIdc</name></member>
+ <member noautovalidity="true"><type>VkVideoDecodeH264PictureLayoutFlagsEXT</type> <name>pictureLayout</name></member>
+ </type>
+ <type category="struct" name="VkVideoDecodeH264CapabilitiesEXT" returnedonly="true" structextends="VkVideoDecodeCapabilitiesKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_CAPABILITIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>StdVideoH264Level</type> <name>maxLevel</name></member>
+ <member><type>VkOffset2D</type> <name>fieldOffsetGranularity</name></member>
+ </type>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264SequenceParameterSet"/>
+ <type requires="vk_video/vulkan_video_codec_h264std.h" name="StdVideoH264PictureParameterSet"/>
+ <type category="struct" name="VkVideoDecodeH264SessionParametersAddInfoEXT" structextends="VkVideoSessionParametersUpdateInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>spsStdCount</name></member>
+ <member len="spsStdCount" optional="true">const <type>StdVideoH264SequenceParameterSet</type>* <name>pSpsStd</name></member>
+ <member><type>uint32_t</type> <name>ppsStdCount</name></member>
+ <member len="ppsStdCount" optional="true">const <type>StdVideoH264PictureParameterSet</type>* <name>pPpsStd</name><comment>List of Picture Parameters associated with the spsStd, above</comment></member>
+ </type>
+ <type category="struct" name="VkVideoDecodeH264SessionParametersCreateInfoEXT" structextends="VkVideoSessionParametersCreateInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>maxSpsStdCount</name></member>
+ <member><type>uint32_t</type> <name>maxPpsStdCount</name></member>
+ <member optional="true">const <type>VkVideoDecodeH264SessionParametersAddInfoEXT</type>* <name>pParametersAddInfo</name></member>
+ </type>
+ <type category="struct" name="VkVideoDecodeH264PictureInfoEXT" structextends="VkVideoDecodeInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PICTURE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true">const <type>void</type>* <name>pNext</name></member>
+ <member>const <type>StdVideoDecodeH264PictureInfo</type>* <name>pStdPictureInfo</name></member>
+ <member><type>uint32_t</type> <name>slicesCount</name></member>
+ <member len="slicesCount">const <type>uint32_t</type>* <name>pSlicesDataOffsets</name></member>
+ </type>
+ <type category="struct" name="VkVideoDecodeH264DpbSlotInfoEXT" structextends="VkVideoReferenceSlotKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_DPB_SLOT_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member>const <type>StdVideoDecodeH264ReferenceInfo</type>* <name>pStdReferenceInfo</name></member>
+ </type>
+ <type category="struct" name="VkVideoDecodeH264MvcEXT" structextends="VkVideoDecodeH264PictureInfoEXT">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_MVC_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true">const <type>void</type>*<name>pNext</name></member>
+ <member>const <type>StdVideoDecodeH264Mvc</type>* <name>pStdMvc</name></member>
+ </type>
+ <type category="include" name="vk_video/vulkan_video_codec_h265std.h">#include "vk_video/vulkan_video_codec_h265std.h"</type>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265ProfileIdc"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265VideoParameterSet"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265SequenceParameterSet"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265PictureParameterSet"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265DecPicBufMgr"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265HrdParameters"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265VpsFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265Level"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265SpsFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265ScalingLists"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265SequenceParameterSetVui"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265PredictorPaletteEntries"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265PpsFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265SubLayerHrdParameters"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265HrdFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265SpsVuiFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265SliceType"/>
+ <type requires="vk_video/vulkan_video_codec_h265std.h" name="StdVideoH265PictureType"/>
+ <type category="include" name="vk_video/vulkan_video_codec_h265std_decode.h">#include "vk_video/vulkan_video_codec_h265std_decode.h"</type>
+ <type requires="vk_video/vulkan_video_codec_h265std_decode.h" name="StdVideoDecodeH265PictureInfo"/>
+ <type requires="vk_video/vulkan_video_codec_h265std_decode.h" name="StdVideoDecodeH265ReferenceInfo"/>
+ <type requires="vk_video/vulkan_video_codec_h265std_decode.h" name="StdVideoDecodeH265PictureInfoFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h265std_decode.h" name="StdVideoDecodeH265ReferenceInfoFlags"/>
+ <type category="struct" name="VkVideoDecodeH265ProfileEXT" structextends="VkVideoProfileKHR,VkQueryPoolCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PROFILE_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>StdVideoH265ProfileIdc</type> <name>stdProfileIdc</name></member>
+ </type>
+ <type category="struct" name="VkVideoDecodeH265CapabilitiesEXT" returnedonly="true" structextends="VkVideoDecodeCapabilitiesKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_CAPABILITIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>StdVideoH265Level</type> <name>maxLevel</name></member>
+ </type>
+ <type category="struct" name="VkVideoDecodeH265SessionParametersAddInfoEXT" structextends="VkVideoSessionParametersUpdateInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>vpsStdCount</name></member>
+ <member len="vpsStdCount" optional="true">const <type>StdVideoH265VideoParameterSet</type>* <name>pVpsStd</name></member>
+ <member><type>uint32_t</type> <name>spsStdCount</name></member>
+ <member len="spsStdCount" optional="true">const <type>StdVideoH265SequenceParameterSet</type>* <name>pSpsStd</name></member>
+ <member><type>uint32_t</type> <name>ppsStdCount</name></member>
+ <member len="ppsStdCount" optional="true">const <type>StdVideoH265PictureParameterSet</type>* <name>pPpsStd</name><comment>List of Picture Parameters associated with the spsStd, above</comment></member>
+ </type>
+ <type category="struct" name="VkVideoDecodeH265SessionParametersCreateInfoEXT" structextends="VkVideoSessionParametersCreateInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>maxVpsStdCount</name></member>
+ <member><type>uint32_t</type> <name>maxSpsStdCount</name></member>
+ <member><type>uint32_t</type> <name>maxPpsStdCount</name></member>
+ <member optional="true">const <type>VkVideoDecodeH265SessionParametersAddInfoEXT</type>* <name>pParametersAddInfo</name></member>
+ </type>
+ <type category="struct" name="VkVideoDecodeH265PictureInfoEXT" structextends="VkVideoDecodeInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PICTURE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>StdVideoDecodeH265PictureInfo</type>* <name>pStdPictureInfo</name></member>
+ <member><type>uint32_t</type> <name>slicesCount</name></member>
+ <member len="slicesCount">const <type>uint32_t</type>* <name>pSlicesDataOffsets</name></member>
+ </type>
+ <type category="struct" name="VkVideoDecodeH265DpbSlotInfoEXT" structextends="VkVideoReferenceSlotKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_DPB_SLOT_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member>const <type>StdVideoDecodeH265ReferenceInfo</type>* <name>pStdReferenceInfo</name></member>
+ </type>
+ <type category="struct" name="VkVideoSessionCreateInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_SESSION_CREATE_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>queueFamilyIndex</name></member>
+ <member optional="true"><type>VkVideoSessionCreateFlagsKHR</type> <name>flags</name></member>
+ <member>const <type>VkVideoProfileKHR</type>* <name>pVideoProfile</name></member>
+ <member><type>VkFormat</type> <name>pictureFormat</name></member>
+ <member><type>VkExtent2D</type> <name>maxCodedExtent</name></member>
+ <member><type>VkFormat</type> <name>referencePicturesFormat</name></member>
+ <member><type>uint32_t</type> <name>maxReferencePicturesSlotsCount</name></member>
+ <member><type>uint32_t</type> <name>maxReferencePicturesActiveCount</name></member>
+ <member>const <type>VkExtensionProperties</type>* <name>pStdHeaderVersion</name></member>
+ </type>
+ <type category="struct" name="VkVideoSessionParametersCreateInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkVideoSessionParametersKHR</type> <name>videoSessionParametersTemplate</name></member>
+ <member><type>VkVideoSessionKHR</type> <name>videoSession</name></member>
+ </type>
+ <type category="struct" name="VkVideoSessionParametersUpdateInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_UPDATE_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>updateSequenceCount</name></member>
+ </type>
+ <type category="struct" name="VkVideoBeginCodingInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_BEGIN_CODING_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkVideoBeginCodingFlagsKHR</type> <name>flags</name></member>
+ <member><type>VkVideoCodingQualityPresetFlagsKHR</type> <name>codecQualityPreset</name></member>
+ <member><type>VkVideoSessionKHR</type> <name>videoSession</name></member>
+ <member optional="true"><type>VkVideoSessionParametersKHR</type> <name>videoSessionParameters</name></member>
+ <member optional="true"><type>uint32_t</type> <name>referenceSlotCount</name></member>
+ <member len="referenceSlotCount">const <type>VkVideoReferenceSlotKHR</type>* <name>pReferenceSlots</name></member>
+ </type>
+ <type category="struct" name="VkVideoEndCodingInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_END_CODING_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkVideoEndCodingFlagsKHR</type> <name>flags</name></member>
+ </type>
+ <type category="struct" name="VkVideoCodingControlInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_CODING_CONTROL_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkVideoCodingControlFlagsKHR</type> <name>flags</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkVideoEncodeFlagsKHR</type> <name>flags</name></member>
+ <member><type>uint32_t</type> <name>qualityLevel</name></member>
+ <member><type>VkBuffer</type> <name>dstBitstreamBuffer</name></member>
+ <member><type>VkDeviceSize</type> <name>dstBitstreamBufferOffset</name></member>
+ <member><type>VkDeviceSize</type> <name>dstBitstreamBufferMaxRange</name></member>
+ <member><type>VkVideoPictureResourceKHR</type> <name>srcPictureResource</name></member>
+ <member>const <type>VkVideoReferenceSlotKHR</type>* <name>pSetupReferenceSlot</name></member>
+ <member optional="true"><type>uint32_t</type> <name>referenceSlotCount</name></member>
+ <member len="referenceSlotCount">const <type>VkVideoReferenceSlotKHR</type>* <name>pReferenceSlots</name></member>
+ <member><type>uint32_t</type> <name>precedingExternallyEncodedBytes</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeRateControlInfoKHR" structextends="VkVideoCodingControlInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_RATE_CONTROL_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member noautovalidity="true"><type>VkVideoEncodeRateControlFlagsKHR</type> <name>flags</name></member>
+ <member><type>VkVideoEncodeRateControlModeFlagBitsKHR</type> <name>rateControlMode</name></member>
+ <member><type>uint8_t</type> <name>layerCount</name></member>
+ <member len="layerCount">const <type>VkVideoEncodeRateControlLayerInfoKHR</type>* <name>pLayerConfigs</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeRateControlLayerInfoKHR" structextends="VkVideoCodingControlInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_RATE_CONTROL_LAYER_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member>const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>averageBitrate</name></member>
+ <member><type>uint32_t</type> <name>maxBitrate</name></member>
+ <member><type>uint32_t</type> <name>frameRateNumerator</name></member>
+ <member><type>uint32_t</type> <name>frameRateDenominator</name></member>
+ <member><type>uint32_t</type> <name>virtualBufferSizeInMs</name></member>
+ <member><type>uint32_t</type> <name>initialVirtualBufferSizeInMs</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeCapabilitiesKHR" returnedonly="true" structextends="VkVideoCapabilitiesKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_CAPABILITIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member noautovalidity="true"><type>VkVideoEncodeCapabilityFlagsKHR</type> <name>flags</name></member>
+ <member><type>VkVideoEncodeRateControlModeFlagsKHR</type> <name>rateControlModes</name></member>
+ <member><type>uint8_t</type> <name>rateControlLayerCount</name></member>
+ <member><type>uint8_t</type> <name>qualityLevelCount</name></member>
+ <member><type>VkExtent2D</type> <name>inputImageDataFillAlignment</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH264CapabilitiesEXT" returnedonly="true" structextends="VkVideoEncodeCapabilitiesKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_CAPABILITIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member noautovalidity="true"><type>VkVideoEncodeH264CapabilityFlagsEXT</type> <name>flags</name></member>
+ <member><type>VkVideoEncodeH264InputModeFlagsEXT</type> <name>inputModeFlags</name></member>
+ <member><type>VkVideoEncodeH264OutputModeFlagsEXT</type> <name>outputModeFlags</name></member>
+ <member><type>uint8_t</type> <name>maxPPictureL0ReferenceCount</name></member>
+ <member><type>uint8_t</type> <name>maxBPictureL0ReferenceCount</name></member>
+ <member><type>uint8_t</type> <name>maxL1ReferenceCount</name></member>
+ <member><type>VkBool32</type> <name>motionVectorsOverPicBoundariesFlag</name></member>
+ <member><type>uint32_t</type> <name>maxBytesPerPicDenom</name></member>
+ <member><type>uint32_t</type> <name>maxBitsPerMbDenom</name></member>
+ <member><type>uint32_t</type> <name>log2MaxMvLengthHorizontal</name></member>
+ <member><type>uint32_t</type> <name>log2MaxMvLengthVertical</name></member>
+ </type>
+ <type category="include" name="vk_video/vulkan_video_codec_h264std_encode.h">#include "vk_video/vulkan_video_codec_h264std_encode.h"</type>
+ <type requires="vk_video/vulkan_video_codec_h264std_encode.h" name="StdVideoEncodeH264SliceHeader"/>
+ <type requires="vk_video/vulkan_video_codec_h264std_encode.h" name="StdVideoEncodeH264PictureInfo"/>
+ <type requires="vk_video/vulkan_video_codec_h264std_encode.h" name="StdVideoEncodeH264ReferenceInfo"/>
+ <type requires="vk_video/vulkan_video_codec_h264std_encode.h" name="StdVideoEncodeH264SliceHeaderFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h264std_encode.h" name="StdVideoEncodeH264RefMemMgmtCtrlOperations"/>
+ <type requires="vk_video/vulkan_video_codec_h264std_encode.h" name="StdVideoEncodeH264PictureInfoFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h264std_encode.h" name="StdVideoEncodeH264ReferenceInfoFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h264std_encode.h" name="StdVideoEncodeH264RefMgmtFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h264std_encode.h" name="StdVideoEncodeH264RefListModEntry"/>
+ <type requires="vk_video/vulkan_video_codec_h264std_encode.h" name="StdVideoEncodeH264RefPicMarkingEntry"/>
+ <type category="struct" name="VkVideoEncodeH264SessionParametersAddInfoEXT" structextends="VkVideoSessionParametersUpdateInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>spsStdCount</name></member>
+ <member len="spsStdCount" optional="true">const <type>StdVideoH264SequenceParameterSet</type>* <name>pSpsStd</name></member>
+ <member><type>uint32_t</type> <name>ppsStdCount</name></member>
+ <member len="ppsStdCount" optional="true">const <type>StdVideoH264PictureParameterSet</type>* <name>pPpsStd</name><comment>List of Picture Parameters associated with the spsStd, above</comment></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH264SessionParametersCreateInfoEXT" structextends="VkVideoSessionParametersCreateInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>maxSpsStdCount</name></member>
+ <member><type>uint32_t</type> <name>maxPpsStdCount</name></member>
+ <member optional="true">const <type>VkVideoEncodeH264SessionParametersAddInfoEXT</type>* <name>pParametersAddInfo</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH264DpbSlotInfoEXT">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_DPB_SLOT_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>int8_t</type> <name>slotIndex</name></member>
+ <member>const <type>StdVideoEncodeH264ReferenceInfo</type>* <name>pStdReferenceInfo</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH264VclFrameInfoEXT" structextends="VkVideoEncodeInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_VCL_FRAME_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true">const <type>VkVideoEncodeH264ReferenceListsEXT</type>* <name>pReferenceFinalLists</name></member>
+ <member><type>uint32_t</type> <name>naluSliceEntryCount</name></member>
+ <member len="naluSliceEntryCount">const <type>VkVideoEncodeH264NaluSliceEXT</type>* <name>pNaluSliceEntries</name></member>
+ <member>const <type>StdVideoEncodeH264PictureInfo</type>* <name>pCurrentPictureInfo</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH264ReferenceListsEXT">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_REFERENCE_LISTS_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>uint8_t</type> <name>referenceList0EntryCount</name></member>
+ <member len="referenceList0EntryCount">const <type>VkVideoEncodeH264DpbSlotInfoEXT</type>* <name>pReferenceList0Entries</name></member>
+ <member optional="true"><type>uint8_t</type> <name>referenceList1EntryCount</name></member>
+ <member len="referenceList1EntryCount">const <type>VkVideoEncodeH264DpbSlotInfoEXT</type>* <name>pReferenceList1Entries</name></member>
+ <member>const <type>StdVideoEncodeH264RefMemMgmtCtrlOperations</type>* <name>pMemMgmtCtrlOperations</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH264EmitPictureParametersEXT" structextends="VkVideoEncodeInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_EMIT_PICTURE_PARAMETERS_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint8_t</type> <name>spsId</name></member>
+ <member><type>VkBool32</type> <name>emitSpsEnable</name></member>
+ <member><type>uint32_t</type> <name>ppsIdEntryCount</name></member>
+ <member len="ppsIdEntryCount">const <type>uint8_t</type>* <name>ppsIdEntries</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH264ProfileEXT" structextends="VkVideoProfileKHR,VkQueryPoolCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PROFILE_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>StdVideoH264ProfileIdc</type> <name>stdProfileIdc</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH264NaluSliceEXT">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_NALU_SLICE_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>mbCount</name></member>
+ <member optional="true">const <type>VkVideoEncodeH264ReferenceListsEXT</type>* <name>pReferenceFinalLists</name></member>
+ <member>const <type>StdVideoEncodeH264SliceHeader</type>* <name>pSliceHeaderStd</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH264RateControlInfoEXT" structextends="VkVideoEncodeRateControlInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_RATE_CONTROL_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>gopFrameCount</name></member>
+ <member><type>uint32_t</type> <name>idrPeriod</name></member>
+ <member><type>uint32_t</type> <name>consecutiveBFrameCount</name></member>
+ <member><type>VkVideoEncodeH264RateControlStructureFlagBitsEXT</type> <name>rateControlStructure</name></member>
+ <member><type>uint8_t</type> <name>temporalLayerCount</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH264QpEXT">
+ <member noautovalidity="true"><type>int32_t</type> <name>qpI</name></member>
+ <member noautovalidity="true"><type>int32_t</type> <name>qpP</name></member>
+ <member noautovalidity="true"><type>int32_t</type> <name>qpB</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH264FrameSizeEXT">
+ <member noautovalidity="true"><type>uint32_t</type> <name>frameISize</name></member>
+ <member noautovalidity="true"><type>uint32_t</type> <name>framePSize</name></member>
+ <member noautovalidity="true"><type>uint32_t</type> <name>frameBSize</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH264RateControlLayerInfoEXT" structextends="VkVideoEncodeRateControlLayerInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_RATE_CONTROL_LAYER_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint8_t</type> <name>temporalLayerId</name></member>
+ <member><type>VkBool32</type> <name>useInitialRcQp</name></member>
+ <member><type>VkVideoEncodeH264QpEXT</type> <name>initialRcQp</name></member>
+ <member><type>VkBool32</type> <name>useMinQp</name></member>
+ <member><type>VkVideoEncodeH264QpEXT</type> <name>minQp</name></member>
+ <member><type>VkBool32</type> <name>useMaxQp</name></member>
+ <member><type>VkVideoEncodeH264QpEXT</type> <name>maxQp</name></member>
+ <member><type>VkBool32</type> <name>useMaxFrameSize</name></member>
+ <member><type>VkVideoEncodeH264FrameSizeEXT</type> <name>maxFrameSize</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH265CapabilitiesEXT" returnedonly="true" structextends="VkVideoEncodeCapabilitiesKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_CAPABILITIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member noautovalidity="true"><type>VkVideoEncodeH265CapabilityFlagsEXT</type> <name>flags</name></member>
+ <member><type>VkVideoEncodeH265InputModeFlagsEXT</type> <name>inputModeFlags</name></member>
+ <member><type>VkVideoEncodeH265OutputModeFlagsEXT</type> <name>outputModeFlags</name></member>
+ <member><type>VkVideoEncodeH265CtbSizeFlagsEXT</type> <name>ctbSizes</name></member>
+ <member><type>VkVideoEncodeH265TransformBlockSizeFlagsEXT</type> <name>transformBlockSizes</name></member>
+ <member><type>uint8_t</type> <name>maxPPictureL0ReferenceCount</name></member>
+ <member><type>uint8_t</type> <name>maxBPictureL0ReferenceCount</name></member>
+ <member><type>uint8_t</type> <name>maxL1ReferenceCount</name></member>
+ <member><type>uint8_t</type> <name>maxSubLayersCount</name></member>
+ <member><type>uint8_t</type> <name>minLog2MinLumaCodingBlockSizeMinus3</name></member>
+ <member><type>uint8_t</type> <name>maxLog2MinLumaCodingBlockSizeMinus3</name></member>
+ <member><type>uint8_t</type> <name>minLog2MinLumaTransformBlockSizeMinus2</name></member>
+ <member><type>uint8_t</type> <name>maxLog2MinLumaTransformBlockSizeMinus2</name></member>
+ <member><type>uint8_t</type> <name>minMaxTransformHierarchyDepthInter</name></member>
+ <member><type>uint8_t</type> <name>maxMaxTransformHierarchyDepthInter</name></member>
+ <member><type>uint8_t</type> <name>minMaxTransformHierarchyDepthIntra</name></member>
+ <member><type>uint8_t</type> <name>maxMaxTransformHierarchyDepthIntra</name></member>
+ <member><type>uint8_t</type> <name>maxDiffCuQpDeltaDepth</name></member>
+ <member><type>uint8_t</type> <name>minMaxNumMergeCand</name></member>
+ <member><type>uint8_t</type> <name>maxMaxNumMergeCand</name></member>
+ </type>
+ <type category="include" name="vk_video/vulkan_video_codec_h265std_encode.h">#include "vk_video/vulkan_video_codec_h265std_encode.h"</type>
+ <type requires="vk_video/vulkan_video_codec_h265std_encode.h" name="StdVideoEncodeH265PictureInfoFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h265std_encode.h" name="StdVideoEncodeH265PictureInfo"/>
+ <type requires="vk_video/vulkan_video_codec_h265std_encode.h" name="StdVideoEncodeH265SliceSegmentHeader"/>
+ <type requires="vk_video/vulkan_video_codec_h265std_encode.h" name="StdVideoEncodeH265ReferenceInfo"/>
+ <type requires="vk_video/vulkan_video_codec_h265std_encode.h" name="StdVideoEncodeH265ReferenceModifications"/>
+ <type requires="vk_video/vulkan_video_codec_h265std_encode.h" name="StdVideoEncodeH265SliceSegmentHeaderFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h265std_encode.h" name="StdVideoEncodeH265ReferenceInfoFlags"/>
+ <type requires="vk_video/vulkan_video_codec_h265std_encode.h" name="StdVideoEncodeH265ReferenceModificationFlags"/>
+ <type category="struct" name="VkVideoEncodeH265SessionParametersAddInfoEXT" structextends="VkVideoSessionParametersUpdateInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_SESSION_PARAMETERS_ADD_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>vpsStdCount</name></member>
+ <member len="vpsStdCount" optional="true">const <type>StdVideoH265VideoParameterSet</type>* <name>pVpsStd</name></member>
+ <member><type>uint32_t</type> <name>spsStdCount</name></member>
+ <member len="spsStdCount" optional="true">const <type>StdVideoH265SequenceParameterSet</type>* <name>pSpsStd</name></member>
+ <member><type>uint32_t</type> <name>ppsStdCount</name></member>
+ <member len="ppsStdCount" optional="true">const <type>StdVideoH265PictureParameterSet</type>* <name>pPpsStd</name><comment>List of Picture Parameters associated with the spsStd, above</comment></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH265SessionParametersCreateInfoEXT" structextends="VkVideoSessionParametersCreateInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_SESSION_PARAMETERS_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>maxVpsStdCount</name></member>
+ <member><type>uint32_t</type> <name>maxSpsStdCount</name></member>
+ <member><type>uint32_t</type> <name>maxPpsStdCount</name></member>
+ <member optional="true">const <type>VkVideoEncodeH265SessionParametersAddInfoEXT</type>* <name>pParametersAddInfo</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH265VclFrameInfoEXT" structextends="VkVideoEncodeInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_VCL_FRAME_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true">const <type>VkVideoEncodeH265ReferenceListsEXT</type>* <name>pReferenceFinalLists</name></member>
+ <member><type>uint32_t</type> <name>naluSliceSegmentEntryCount</name></member>
+ <member len="naluSliceSegmentEntryCount">const <type>VkVideoEncodeH265NaluSliceSegmentEXT</type>* <name>pNaluSliceSegmentEntries</name></member>
+ <member>const <type>StdVideoEncodeH265PictureInfo</type>* <name>pCurrentPictureInfo</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH265EmitPictureParametersEXT" structextends="VkVideoEncodeInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_EMIT_PICTURE_PARAMETERS_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint8_t</type> <name>vpsId</name></member>
+ <member><type>uint8_t</type> <name>spsId</name></member>
+ <member><type>VkBool32</type> <name>emitVpsEnable</name></member>
+ <member><type>VkBool32</type> <name>emitSpsEnable</name></member>
+ <member optional="true"><type>uint32_t</type> <name>ppsIdEntryCount</name></member>
+ <member len="ppsIdEntryCount">const <type>uint8_t</type>* <name>ppsIdEntries</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH265NaluSliceSegmentEXT">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_NALU_SLICE_SEGMENT_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>ctbCount</name></member>
+ <member optional="true">const <type>VkVideoEncodeH265ReferenceListsEXT</type>* <name>pReferenceFinalLists</name></member>
+ <member>const <type>StdVideoEncodeH265SliceSegmentHeader</type>* <name>pSliceSegmentHeaderStd</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH265RateControlInfoEXT" structextends="VkVideoEncodeRateControlInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_RATE_CONTROL_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>gopFrameCount</name></member>
+ <member><type>uint32_t</type> <name>idrPeriod</name></member>
+ <member><type>uint32_t</type> <name>consecutiveBFrameCount</name></member>
+ <member><type>VkVideoEncodeH265RateControlStructureFlagBitsEXT</type> <name>rateControlStructure</name></member>
+ <member><type>uint8_t</type> <name>subLayerCount</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH265QpEXT">
+ <member noautovalidity="true"><type>int32_t</type> <name>qpI</name></member>
+ <member noautovalidity="true"><type>int32_t</type> <name>qpP</name></member>
+ <member noautovalidity="true"><type>int32_t</type> <name>qpB</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH265FrameSizeEXT">
+ <member noautovalidity="true"><type>uint32_t</type> <name>frameISize</name></member>
+ <member noautovalidity="true"><type>uint32_t</type> <name>framePSize</name></member>
+ <member noautovalidity="true"><type>uint32_t</type> <name>frameBSize</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH265RateControlLayerInfoEXT" structextends="VkVideoEncodeRateControlLayerInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_RATE_CONTROL_LAYER_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint8_t</type> <name>temporalId</name></member>
+ <member><type>VkBool32</type> <name>useInitialRcQp</name></member>
+ <member><type>VkVideoEncodeH265QpEXT</type> <name>initialRcQp</name></member>
+ <member><type>VkBool32</type> <name>useMinQp</name></member>
+ <member><type>VkVideoEncodeH265QpEXT</type> <name>minQp</name></member>
+ <member><type>VkBool32</type> <name>useMaxQp</name></member>
+ <member><type>VkVideoEncodeH265QpEXT</type> <name>maxQp</name></member>
+ <member><type>VkBool32</type> <name>useMaxFrameSize</name></member>
+ <member><type>VkVideoEncodeH265FrameSizeEXT</type> <name>maxFrameSize</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH265ProfileEXT" structextends="VkVideoProfileKHR,VkQueryPoolCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_PROFILE_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>StdVideoH265ProfileIdc</type> <name>stdProfileIdc</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH265DpbSlotInfoEXT">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_DPB_SLOT_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>int8_t</type> <name>slotIndex</name></member>
+ <member>const <type>StdVideoEncodeH265ReferenceInfo</type>* <name>pStdReferenceInfo</name></member>
+ </type>
+ <type category="struct" name="VkVideoEncodeH265ReferenceListsEXT">
+ <member values="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_REFERENCE_LISTS_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>uint8_t</type> <name>referenceList0EntryCount</name></member>
+ <member len="referenceList0EntryCount">const <type>VkVideoEncodeH265DpbSlotInfoEXT</type>* <name>pReferenceList0Entries</name></member>
+ <member optional="true"><type>uint8_t</type> <name>referenceList1EntryCount</name></member>
+ <member len="referenceList1EntryCount">const <type>VkVideoEncodeH265DpbSlotInfoEXT</type>* <name>pReferenceList1Entries</name></member>
+ <member>const <type>StdVideoEncodeH265ReferenceModifications</type>* <name>pReferenceModifications</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceInheritedViewportScissorFeaturesNV" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INHERITED_VIEWPORT_SCISSOR_FEATURES_NV"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>inheritedViewportScissor2D</name></member>
+ </type>
+ <type category="struct" name="VkCommandBufferInheritanceViewportScissorInfoNV" structextends="VkCommandBufferInheritanceInfo">
+ <member values="VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_VIEWPORT_SCISSOR_INFO_NV"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>viewportScissor2D</name></member>
+ <member><type>uint32_t</type> <name>viewportDepthCount</name></member>
+ <member noautovalidity="true">const <type>VkViewport</type>* <name>pViewportDepths</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceYcbcr2Plane444FormatsFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_YCBCR_2_PLANE_444_FORMATS_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>ycbcr2plane444Formats</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceProvokingVertexFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>provokingVertexLast</name></member>
+ <member><type>VkBool32</type> <name>transformFeedbackPreservesProvokingVertex</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceProvokingVertexPropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>provokingVertexModePerPipeline</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>transformFeedbackPreservesTriangleFanProvokingVertex</name></member>
+ </type>
+ <type category="struct" name="VkPipelineRasterizationProvokingVertexStateCreateInfoEXT" structextends="VkPipelineRasterizationStateCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkProvokingVertexModeEXT</type> <name>provokingVertexMode</name></member>
+ </type>
+ <type category="struct" name="VkCuModuleCreateInfoNVX">
+ <member values="VK_STRUCTURE_TYPE_CU_MODULE_CREATE_INFO_NVX"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>size_t</type> <name>dataSize</name></member>
+ <member len="dataSize">const <type>void</type>* <name>pData</name></member>
+ </type>
+ <type category="struct" name="VkCuFunctionCreateInfoNVX">
+ <member values="VK_STRUCTURE_TYPE_CU_FUNCTION_CREATE_INFO_NVX"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkCuModuleNVX</type> <name>module</name></member>
+ <member len="null-terminated">const <type>char</type>* <name>pName</name></member>
+ </type>
+ <type category="struct" name="VkCuLaunchInfoNVX">
+ <member values="VK_STRUCTURE_TYPE_CU_LAUNCH_INFO_NVX"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkCuFunctionNVX</type> <name>function</name></member>
+ <member><type>uint32_t</type> <name>gridDimX</name></member>
+ <member><type>uint32_t</type> <name>gridDimY</name></member>
+ <member><type>uint32_t</type> <name>gridDimZ</name></member>
+ <member><type>uint32_t</type> <name>blockDimX</name></member>
+ <member><type>uint32_t</type> <name>blockDimY</name></member>
+ <member><type>uint32_t</type> <name>blockDimZ</name></member>
+ <member><type>uint32_t</type> <name>sharedMemBytes</name></member>
+ <member optional="true"><type>size_t</type> <name>paramCount</name></member>
+ <member len="paramCount">const <type>void</type>* const * <name>pParams</name></member>
+ <member optional="true"><type>size_t</type> <name>extraCount</name></member>
+ <member len="extraCount">const <type>void</type>* const * <name>pExtras</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceShaderIntegerDotProductFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>shaderIntegerDotProduct</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR" alias="VkPhysicalDeviceShaderIntegerDotProductFeatures"/>
+ <type category="struct" name="VkPhysicalDeviceShaderIntegerDotProductProperties" structextends="VkPhysicalDeviceProperties2" returnedonly="true">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct8BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct8BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct8BitMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct4x8BitPackedUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct4x8BitPackedSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct4x8BitPackedMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct16BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct16BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct16BitMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct32BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct32BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct32BitMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct64BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct64BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProduct64BitMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating8BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating8BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating16BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating16BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating32BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating32BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating64BitUnsignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating64BitSignedAccelerated</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceShaderIntegerDotProductPropertiesKHR" alias="VkPhysicalDeviceShaderIntegerDotProductProperties"/>
+ <type category="struct" name="VkPhysicalDeviceDrmPropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>hasPrimary</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>hasRender</name></member>
+ <member limittype="noauto"><type>int64_t</type> <name>primaryMajor</name></member>
+ <member limittype="noauto"><type>int64_t</type> <name>primaryMinor</name></member>
+ <member limittype="noauto"><type>int64_t</type> <name>renderMajor</name></member>
+ <member limittype="noauto"><type>int64_t</type> <name>renderMinor</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>fragmentShaderBarycentric</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceFragmentShaderBarycentricPropertiesKHR" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_PROPERTIES_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>triStripVertexOrderIndependentOfProvokingVertex</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceRayTracingMotionBlurFeaturesNV" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_MOTION_BLUR_FEATURES_NV"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>rayTracingMotionBlur</name></member>
+ <member><type>VkBool32</type> <name>rayTracingMotionBlurPipelineTraceRaysIndirect</name></member>
+ </type>
+ <type name="VkAccelerationStructureMotionInstanceTypeNV" category="enum"/>
+ <type category="struct" name="VkAccelerationStructureGeometryMotionTrianglesDataNV" structextends="VkAccelerationStructureGeometryTrianglesDataKHR">
+ <member values="VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_MOTION_TRIANGLES_DATA_NV"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member noautovalidity="true"><type>VkDeviceOrHostAddressConstKHR</type> <name>vertexData</name></member>
+ </type>
+ <type category="struct" name="VkAccelerationStructureMotionInfoNV" structextends="VkAccelerationStructureCreateInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MOTION_INFO_NV"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>maxInstances</name></member>
+ <member optional="true"><type>VkAccelerationStructureMotionInfoFlagsNV</type> <name>flags</name></member>
+ </type>
+ <type category="struct" name="VkSRTDataNV">
+ <member><type>float</type> <name>sx</name></member>
+ <member><type>float</type> <name>a</name></member>
+ <member><type>float</type> <name>b</name></member>
+ <member><type>float</type> <name>pvx</name></member>
+ <member><type>float</type> <name>sy</name></member>
+ <member><type>float</type> <name>c</name></member>
+ <member><type>float</type> <name>pvy</name></member>
+ <member><type>float</type> <name>sz</name></member>
+ <member><type>float</type> <name>pvz</name></member>
+ <member><type>float</type> <name>qx</name></member>
+ <member><type>float</type> <name>qy</name></member>
+ <member><type>float</type> <name>qz</name></member>
+ <member><type>float</type> <name>qw</name></member>
+ <member><type>float</type> <name>tx</name></member>
+ <member><type>float</type> <name>ty</name></member>
+ <member><type>float</type> <name>tz</name></member>
+ </type>
+ <type category="struct" name="VkAccelerationStructureSRTMotionInstanceNV">
+ <comment>The bitfields in this structure are non-normative since bitfield ordering is implementation-defined in C. The specification defines the normative layout.</comment>
+ <member><type>VkSRTDataNV</type> <name>transformT0</name></member>
+ <member><type>VkSRTDataNV</type> <name>transformT1</name></member>
+ <member><type>uint32_t</type> <name>instanceCustomIndex</name>:24</member>
+ <member><type>uint32_t</type> <name>mask</name>:8</member>
+ <member><type>uint32_t</type> <name>instanceShaderBindingTableRecordOffset</name>:24</member>
+ <member optional="true"><type>VkGeometryInstanceFlagsKHR</type> <name>flags</name>:8</member>
+ <member><type>uint64_t</type> <name>accelerationStructureReference</name></member>
+ </type>
+ <type category="struct" name="VkAccelerationStructureMatrixMotionInstanceNV">
+ <comment>The bitfields in this structure are non-normative since bitfield ordering is implementation-defined in C. The specification defines the normative layout.</comment>
+ <member><type>VkTransformMatrixKHR</type> <name>transformT0</name></member>
+ <member><type>VkTransformMatrixKHR</type> <name>transformT1</name></member>
+ <member><type>uint32_t</type> <name>instanceCustomIndex</name>:24</member>
+ <member><type>uint32_t</type> <name>mask</name>:8</member>
+ <member><type>uint32_t</type> <name>instanceShaderBindingTableRecordOffset</name>:24</member>
+ <member optional="true"><type>VkGeometryInstanceFlagsKHR</type> <name>flags</name>:8</member>
+ <member><type>uint64_t</type> <name>accelerationStructureReference</name></member>
+ </type>
+ <type category="union" name="VkAccelerationStructureMotionInstanceDataNV">
+ <member selection="VK_ACCELERATION_STRUCTURE_MOTION_INSTANCE_TYPE_STATIC_NV"><type>VkAccelerationStructureInstanceKHR</type> <name>staticInstance</name></member>
+ <member selection="VK_ACCELERATION_STRUCTURE_MOTION_INSTANCE_TYPE_MATRIX_MOTION_NV"><type>VkAccelerationStructureMatrixMotionInstanceNV</type> <name>matrixMotionInstance</name></member>
+ <member selection="VK_ACCELERATION_STRUCTURE_MOTION_INSTANCE_TYPE_SRT_MOTION_NV"><type>VkAccelerationStructureSRTMotionInstanceNV</type> <name>srtMotionInstance</name></member>
+ </type>
+ <type category="struct" name="VkAccelerationStructureMotionInstanceNV">
+ <member><type>VkAccelerationStructureMotionInstanceTypeNV</type> <name>type</name></member>
+ <member optional="true"><type>VkAccelerationStructureMotionInstanceFlagsNV</type> <name>flags</name></member>
+ <member selector="type"><type>VkAccelerationStructureMotionInstanceDataNV</type> <name>data</name></member>
+ </type>
+ <type category="basetype">typedef <type>void</type>* <name>VkRemoteAddressNV</name>;</type>
+ <type category="struct" name="VkMemoryGetRemoteAddressInfoNV">
+ <member values="VK_STRUCTURE_TYPE_MEMORY_GET_REMOTE_ADDRESS_INFO_NV"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkDeviceMemory</type> <name>memory</name></member>
+ <member><type>VkExternalMemoryHandleTypeFlagBits</type> <name>handleType</name></member>
+ </type>
+ <type category="struct" name="VkImportMemoryBufferCollectionFUCHSIA" structextends="VkMemoryAllocateInfo">
+ <member values="VK_STRUCTURE_TYPE_IMPORT_MEMORY_BUFFER_COLLECTION_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkBufferCollectionFUCHSIA</type> <name>collection</name></member>
+ <member><type>uint32_t</type> <name>index</name></member>
+ </type>
+ <type category="struct" name="VkBufferCollectionImageCreateInfoFUCHSIA" structextends="VkImageCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_BUFFER_COLLECTION_IMAGE_CREATE_INFO_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkBufferCollectionFUCHSIA</type> <name>collection</name></member>
+ <member><type>uint32_t</type> <name>index</name></member>
+ </type>
+ <type category="struct" name="VkBufferCollectionBufferCreateInfoFUCHSIA" structextends="VkBufferCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_BUFFER_COLLECTION_BUFFER_CREATE_INFO_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkBufferCollectionFUCHSIA</type> <name>collection</name></member>
+ <member><type>uint32_t</type> <name>index</name></member>
+ </type>
+ <type category="struct" name="VkBufferCollectionCreateInfoFUCHSIA">
+ <member values="VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CREATE_INFO_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>zx_handle_t</type> <name>collectionToken</name></member>
+ </type>
+ <type category="struct" name="VkBufferCollectionPropertiesFUCHSIA">
+ <member values="VK_STRUCTURE_TYPE_BUFFER_COLLECTION_PROPERTIES_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>memoryTypeBits</name></member>
+ <member><type>uint32_t</type> <name>bufferCount</name></member>
+ <member><type>uint32_t</type> <name>createInfoIndex</name></member>
+ <member><type>uint64_t</type> <name>sysmemPixelFormat</name></member>
+ <member><type>VkFormatFeatureFlags</type> <name>formatFeatures</name></member>
+ <member><type>VkSysmemColorSpaceFUCHSIA</type> <name>sysmemColorSpaceIndex</name></member>
+ <member><type>VkComponentMapping</type> <name>samplerYcbcrConversionComponents</name></member>
+ <member><type>VkSamplerYcbcrModelConversion</type> <name>suggestedYcbcrModel</name></member>
+ <member><type>VkSamplerYcbcrRange</type> <name>suggestedYcbcrRange</name></member>
+ <member><type>VkChromaLocation</type> <name>suggestedXChromaOffset</name></member>
+ <member><type>VkChromaLocation</type> <name>suggestedYChromaOffset</name></member>
+ </type>
+ <type category="struct" name="VkBufferConstraintsInfoFUCHSIA">
+ <member values="VK_STRUCTURE_TYPE_BUFFER_CONSTRAINTS_INFO_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkBufferCreateInfo</type> <name>createInfo</name></member>
+ <member optional="true"><type>VkFormatFeatureFlags</type> <name>requiredFormatFeatures</name></member>
+ <member><type>VkBufferCollectionConstraintsInfoFUCHSIA</type> <name>bufferCollectionConstraints</name></member>
+ </type>
+ <type category="struct" name="VkSysmemColorSpaceFUCHSIA">
+ <member values="VK_STRUCTURE_TYPE_SYSMEM_COLOR_SPACE_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>colorSpace</name></member>
+ </type>
+ <type category="struct" name="VkImageFormatConstraintsInfoFUCHSIA">
+ <member values="VK_STRUCTURE_TYPE_IMAGE_FORMAT_CONSTRAINTS_INFO_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkImageCreateInfo</type> <name>imageCreateInfo</name></member>
+ <member><type>VkFormatFeatureFlags</type> <name>requiredFormatFeatures</name></member>
+ <member optional="true"><type>VkImageFormatConstraintsFlagsFUCHSIA</type> <name>flags</name></member>
+ <member optional="true"><type>uint64_t</type> <name>sysmemPixelFormat</name></member>
+ <member><type>uint32_t</type> <name>colorSpaceCount</name></member>
+ <member len="colorSpaceCount">const <type>VkSysmemColorSpaceFUCHSIA</type>* <name>pColorSpaces</name></member>
+ </type>
+ <type category="struct" name="VkImageConstraintsInfoFUCHSIA">
+ <member values="VK_STRUCTURE_TYPE_IMAGE_CONSTRAINTS_INFO_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>formatConstraintsCount</name></member>
+ <member len="formatConstraintsCount">const <type>VkImageFormatConstraintsInfoFUCHSIA</type>* <name>pFormatConstraints</name></member>
+ <member><type>VkBufferCollectionConstraintsInfoFUCHSIA</type> <name>bufferCollectionConstraints</name></member>
+ <member optional="true"><type>VkImageConstraintsInfoFlagsFUCHSIA</type> <name>flags</name></member>
+ </type>
+ <type category="struct" name="VkBufferCollectionConstraintsInfoFUCHSIA">
+ <member values="VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CONSTRAINTS_INFO_FUCHSIA"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>minBufferCount</name></member>
+ <member><type>uint32_t</type> <name>maxBufferCount</name></member>
+ <member><type>uint32_t</type> <name>minBufferCountForCamping</name></member>
+ <member><type>uint32_t</type> <name>minBufferCountForDedicatedSlack</name></member>
+ <member><type>uint32_t</type> <name>minBufferCountForSharedSlack</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RGBA10X6_FORMATS_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>formatRgba10x6WithoutYCbCrSampler</name></member>
+ </type>
+ <type category="struct" name="VkFormatProperties3" returnedonly="true" structextends="VkFormatProperties2">
+ <member values="VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member optional="true" limittype="bitmask"><type>VkFormatFeatureFlags2</type> <name>linearTilingFeatures</name></member>
+ <member optional="true" limittype="bitmask"><type>VkFormatFeatureFlags2</type> <name>optimalTilingFeatures</name></member>
+ <member optional="true" limittype="bitmask"><type>VkFormatFeatureFlags2</type> <name>bufferFeatures</name></member>
+ </type>
+ <type category="struct" name="VkFormatProperties3KHR" alias="VkFormatProperties3"/>
+ <type category="struct" name="VkDrmFormatModifierPropertiesList2EXT" returnedonly="true" structextends="VkFormatProperties2">
+ <member values="VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_2_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>uint32_t</type> <name>drmFormatModifierCount</name></member>
+ <member optional="true" len="drmFormatModifierCount"><type>VkDrmFormatModifierProperties2EXT</type>* <name>pDrmFormatModifierProperties</name></member>
+ </type>
+ <type category="struct" name="VkDrmFormatModifierProperties2EXT" returnedonly="true">
+ <member><type>uint64_t</type> <name>drmFormatModifier</name></member>
+ <member><type>uint32_t</type> <name>drmFormatModifierPlaneCount</name></member>
+ <member><type>VkFormatFeatureFlags2</type> <name>drmFormatModifierTilingFeatures</name></member>
+ </type>
+ <type category="struct" name="VkAndroidHardwareBufferFormatProperties2ANDROID" structextends="VkAndroidHardwareBufferPropertiesANDROID" returnedonly="true">
+ <member values="VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_2_ANDROID"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkFormat</type> <name>format</name></member>
+ <member><type>uint64_t</type> <name>externalFormat</name></member>
+ <member><type>VkFormatFeatureFlags2</type> <name>formatFeatures</name></member>
+ <member><type>VkComponentMapping</type> <name>samplerYcbcrConversionComponents</name></member>
+ <member><type>VkSamplerYcbcrModelConversion</type> <name>suggestedYcbcrModel</name></member>
+ <member><type>VkSamplerYcbcrRange</type> <name>suggestedYcbcrRange</name></member>
+ <member><type>VkChromaLocation</type> <name>suggestedXChromaOffset</name></member>
+ <member><type>VkChromaLocation</type> <name>suggestedYChromaOffset</name></member>
+ </type>
+ <type category="struct" name="VkPipelineRenderingCreateInfo" structextends="VkGraphicsPipelineCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>uint32_t</type> <name>viewMask</name></member>
+ <member optional="true"><type>uint32_t</type> <name>colorAttachmentCount</name></member>
+ <member noautovalidity="true" len="colorAttachmentCount">const <type>VkFormat</type>* <name>pColorAttachmentFormats</name></member>
+ <member noautovalidity="true"><type>VkFormat</type> <name>depthAttachmentFormat</name></member>
+ <member noautovalidity="true"><type>VkFormat</type> <name>stencilAttachmentFormat</name></member>
+ </type>
+ <type category="struct" name="VkPipelineRenderingCreateInfoKHR" alias="VkPipelineRenderingCreateInfo"/>
+ <type category="struct" name="VkRenderingInfo">
+ <member values="VK_STRUCTURE_TYPE_RENDERING_INFO"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkRenderingFlags</type> <name>flags</name></member>
+ <member><type>VkRect2D</type> <name>renderArea</name></member>
+ <member><type>uint32_t</type> <name>layerCount</name></member>
+ <member><type>uint32_t</type> <name>viewMask</name></member>
+ <member optional="true"><type>uint32_t</type> <name>colorAttachmentCount</name></member>
+ <member len="colorAttachmentCount">const <type>VkRenderingAttachmentInfo</type>* <name>pColorAttachments</name></member>
+ <member optional="true">const <type>VkRenderingAttachmentInfo</type>* <name>pDepthAttachment</name></member>
+ <member optional="true">const <type>VkRenderingAttachmentInfo</type>* <name>pStencilAttachment</name></member>
+ </type>
+ <type category="struct" name="VkRenderingInfoKHR" alias="VkRenderingInfo"/>
+ <type category="struct" name="VkRenderingAttachmentInfo">
+ <member values="VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkImageView</type> <name>imageView</name></member>
+ <member><type>VkImageLayout</type> <name>imageLayout</name></member>
+ <member optional="true"><type>VkResolveModeFlagBits</type> <name>resolveMode</name></member>
+ <member optional="true"><type>VkImageView</type> <name>resolveImageView</name></member>
+ <member><type>VkImageLayout</type> <name>resolveImageLayout</name></member>
+ <member><type>VkAttachmentLoadOp</type> <name>loadOp</name></member>
+ <member><type>VkAttachmentStoreOp</type> <name>storeOp</name></member>
+ <member><type>VkClearValue</type> <name>clearValue</name></member>
+ </type>
+ <type category="struct" name="VkRenderingAttachmentInfoKHR" alias="VkRenderingAttachmentInfo"/>
+ <type category="struct" name="VkRenderingFragmentShadingRateAttachmentInfoKHR" structextends="VkRenderingInfo">
+ <member values="VK_STRUCTURE_TYPE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkImageView</type> <name>imageView</name></member>
+ <member><type>VkImageLayout</type> <name>imageLayout</name></member>
+ <member><type>VkExtent2D</type> <name>shadingRateAttachmentTexelSize</name></member>
+ </type>
+ <type category="struct" name="VkRenderingFragmentDensityMapAttachmentInfoEXT" structextends="VkRenderingInfo">
+ <member values="VK_STRUCTURE_TYPE_RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkImageView</type> <name>imageView</name></member>
+ <member><type>VkImageLayout</type> <name>imageLayout</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceDynamicRenderingFeatures" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>dynamicRendering</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceDynamicRenderingFeaturesKHR" alias="VkPhysicalDeviceDynamicRenderingFeatures"/>
+ <type category="struct" name="VkCommandBufferInheritanceRenderingInfo" structextends="VkCommandBufferInheritanceInfo">
+ <member values="VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDERING_INFO"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkRenderingFlags</type> <name>flags</name></member>
+ <member><type>uint32_t</type> <name>viewMask</name></member>
+ <member optional="true"><type>uint32_t</type> <name>colorAttachmentCount</name></member>
+ <member len="colorAttachmentCount">const <type>VkFormat</type>* <name>pColorAttachmentFormats</name></member>
+ <member><type>VkFormat</type> <name>depthAttachmentFormat</name></member>
+ <member><type>VkFormat</type> <name>stencilAttachmentFormat</name></member>
+ <member optional="true"><type>VkSampleCountFlagBits</type> <name>rasterizationSamples</name></member>
+ </type>
+ <type category="struct" name="VkCommandBufferInheritanceRenderingInfoKHR" alias="VkCommandBufferInheritanceRenderingInfo"/>
+ <type category="struct" name="VkAttachmentSampleCountInfoAMD" structextends="VkCommandBufferInheritanceInfo,VkGraphicsPipelineCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_ATTACHMENT_SAMPLE_COUNT_INFO_AMD"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>uint32_t</type> <name>colorAttachmentCount</name></member>
+ <member noautovalidity="true" len="colorAttachmentCount">const <type>VkSampleCountFlagBits</type>* <name>pColorAttachmentSamples</name></member>
+ <member noautovalidity="true" optional="true"><type>VkSampleCountFlagBits</type> <name>depthStencilAttachmentSamples</name></member>
+ </type>
+ <type category="struct" name="VkAttachmentSampleCountInfoNV" alias="VkAttachmentSampleCountInfoAMD"/>
+ <type category="struct" name="VkMultiviewPerViewAttributesInfoNVX" structextends="VkCommandBufferInheritanceInfo,VkGraphicsPipelineCreateInfo,VkRenderingInfo">
+ <member values="VK_STRUCTURE_TYPE_MULTIVIEW_PER_VIEW_ATTRIBUTES_INFO_NVX"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>perViewAttributes</name></member>
+ <member><type>VkBool32</type> <name>perViewAttributesPositionXOnly</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceImageViewMinLodFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_MIN_LOD_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>minLod</name></member>
+ </type>
+ <type category="struct" name="VkImageViewMinLodCreateInfoEXT" structextends="VkImageViewCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_IMAGE_VIEW_MIN_LOD_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>float</type> <name>minLod</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesARM" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_ARM"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>rasterizationOrderColorAttachmentAccess</name></member>
+ <member><type>VkBool32</type> <name>rasterizationOrderDepthAttachmentAccess</name></member>
+ <member><type>VkBool32</type> <name>rasterizationOrderStencilAttachmentAccess</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceLinearColorAttachmentFeaturesNV" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINEAR_COLOR_ATTACHMENT_FEATURES_NV"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>linearColorAttachment</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>graphicsPipelineLibrary</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT" structextends="VkPhysicalDeviceProperties2">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>graphicsPipelineLibraryFastLinking</name></member>
+ <member limittype="bitmask"><type>VkBool32</type> <name>graphicsPipelineLibraryIndependentInterpolationDecoration</name></member>
+ </type>
+ <type category="struct" name="VkGraphicsPipelineLibraryCreateInfoEXT" structextends="VkGraphicsPipelineCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkGraphicsPipelineLibraryFlagsEXT</type> <name>flags</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceDescriptorSetHostMappingFeaturesVALVE" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_SET_HOST_MAPPING_FEATURES_VALVE"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>descriptorSetHostMapping</name></member>
+ </type>
+ <type category="struct" name="VkDescriptorSetBindingReferenceVALVE">
+ <member values="VK_STRUCTURE_TYPE_DESCRIPTOR_SET_BINDING_REFERENCE_VALVE"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkDescriptorSetLayout</type> <name>descriptorSetLayout</name></member>
+ <member><type>uint32_t</type> <name>binding</name></member>
+ </type>
+ <type category="struct" name="VkDescriptorSetLayoutHostMappingInfoVALVE">
+ <member values="VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_HOST_MAPPING_INFO_VALVE"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>size_t</type> <name>descriptorOffset</name></member>
+ <member><type>uint32_t</type> <name>descriptorSize</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceShaderModuleIdentifierFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MODULE_IDENTIFIER_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>shaderModuleIdentifier</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceShaderModuleIdentifierPropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MODULE_IDENTIFIER_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="noauto"><type>uint8_t</type> <name>shaderModuleIdentifierAlgorithmUUID</name>[<enum>VK_UUID_SIZE</enum>]</member>
+ </type>
+ <type category="struct" name="VkPipelineShaderStageModuleIdentifierCreateInfoEXT" structextends="VkPipelineShaderStageCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_MODULE_IDENTIFIER_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>uint32_t</type> <name>identifierSize</name></member>
+ <member len="identifierSize">const <type>uint8_t</type>* <name>pIdentifier</name></member>
+ </type>
+ <type category="struct" name="VkShaderModuleIdentifierEXT" returnedonly="true">
+ <member values="VK_STRUCTURE_TYPE_SHADER_MODULE_IDENTIFIER_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member noautovalidity="true"><type>uint32_t</type> <name>identifierSize</name></member>
+ <member><type>uint8_t</type> <name>identifier</name>[<enum>VK_MAX_SHADER_MODULE_IDENTIFIER_SIZE_EXT</enum>]</member>
+ </type>
+ <type category="struct" name="VkImageCompressionControlEXT" structextends="VkImageCreateInfo,VkSwapchainCreateInfoKHR,VkPhysicalDeviceImageFormatInfo2">
+ <member values="VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member noautovalidity="true"><type>VkImageCompressionFlagsEXT</type> <name>flags</name></member>
+ <member optional="true"><type>uint32_t</type> <name>compressionControlPlaneCount</name></member>
+ <member noautovalidity="true" len="compressionControlPlaneCount"><type>VkImageCompressionFixedRateFlagsEXT</type>* <name>pFixedRateFlags</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceImageCompressionControlFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>imageCompressionControl</name></member>
+ </type>
+ <type category="struct" name="VkImageCompressionPropertiesEXT" structextends="VkImageFormatProperties2,VkSurfaceFormat2KHR,VkSubresourceLayout2EXT" returnedonly="true">
+ <member values="VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkImageCompressionFlagsEXT</type> <name>imageCompressionFlags</name></member>
+ <member><type>VkImageCompressionFixedRateFlagsEXT</type> <name>imageCompressionFixedRateFlags</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>imageCompressionControlSwapchain</name></member>
+ </type>
+ <type category="struct" name="VkImageSubresource2EXT">
+ <member values="VK_STRUCTURE_TYPE_IMAGE_SUBRESOURCE_2_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkImageSubresource</type> <name>imageSubresource</name></member>
+ </type>
+ <type category="struct" name="VkSubresourceLayout2EXT" returnedonly="true">
+ <member values="VK_STRUCTURE_TYPE_SUBRESOURCE_LAYOUT_2_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkSubresourceLayout</type> <name>subresourceLayout</name></member>
+ </type>
+ <type category="struct" name="VkRenderPassCreationControlEXT" structextends="VkRenderPassCreateInfo2,VkSubpassDescription2">
+ <member values="VK_STRUCTURE_TYPE_RENDER_PASS_CREATION_CONTROL_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>disallowMerging</name></member>
+ </type>
+ <type category="struct" name="VkRenderPassCreationFeedbackInfoEXT" returnedonly="true">
+ <member><type>uint32_t</type> <name>postMergeSubpassCount</name></member>
+ </type>
+ <type category="struct" name="VkRenderPassCreationFeedbackCreateInfoEXT" structextends="VkRenderPassCreateInfo2">
+ <member values="VK_STRUCTURE_TYPE_RENDER_PASS_CREATION_FEEDBACK_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkRenderPassCreationFeedbackInfoEXT</type>* <name>pRenderPassFeedback</name></member>
+ </type>
+ <type category="struct" name="VkRenderPassSubpassFeedbackInfoEXT" returnedonly="true">
+ <member><type>VkSubpassMergeStatusEXT</type> <name>subpassMergeStatus</name></member>
+ <member><type>char</type> <name>description</name>[<enum>VK_MAX_DESCRIPTION_SIZE</enum>]</member>
+ <member><type>uint32_t</type> <name>postMergeIndex</name></member>
+ </type>
+ <type category="struct" name="VkRenderPassSubpassFeedbackCreateInfoEXT" structextends="VkSubpassDescription2">
+ <member values="VK_STRUCTURE_TYPE_RENDER_PASS_SUBPASS_FEEDBACK_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkRenderPassSubpassFeedbackInfoEXT</type>* <name>pSubpassFeedback</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceSubpassMergeFeedbackFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_MERGE_FEEDBACK_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>subpassMergeFeedback</name></member>
+ </type>
+ <type category="struct" name="VkPipelinePropertiesIdentifierEXT">
+ <member values="VK_STRUCTURE_TYPE_PIPELINE_PROPERTIES_IDENTIFIER_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>uint8_t</type> <name>pipelineIdentifier</name>[<enum>VK_UUID_SIZE</enum>]</member>
+ </type>
+ <type category="struct" name="VkPhysicalDevicePipelinePropertiesFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_PROPERTIES_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>pipelinePropertiesIdentifier</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceShaderEarlyAndLateFragmentTestsFeaturesAMD" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_EARLY_AND_LATE_FRAGMENT_TESTS_FEATURES_AMD"><type>VkStructureType</type> <name>sType</name></member>
+ <member noautovalidity="true" optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>shaderEarlyAndLateFragmentTests</name></member>
+ </type>
+ <type category="struct" name="VkExportMetalObjectCreateInfoEXT" structextends="VkInstanceCreateInfo,VkMemoryAllocateInfo,VkImageCreateInfo,VkImageViewCreateInfo,VkBufferViewCreateInfo,VkSemaphoreCreateInfo,VkEventCreateInfo" allowduplicate="true">
+ <member values="VK_STRUCTURE_TYPE_EXPORT_METAL_OBJECT_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkExportMetalObjectTypeFlagBitsEXT</type> <name>exportObjectType</name></member>
+ </type>
+ <type category="struct" name="VkExportMetalObjectsInfoEXT">
+ <member values="VK_STRUCTURE_TYPE_EXPORT_METAL_OBJECTS_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ </type>
+ <type category="struct" name="VkExportMetalDeviceInfoEXT" structextends="VkExportMetalObjectsInfoEXT">
+ <member values="VK_STRUCTURE_TYPE_EXPORT_METAL_DEVICE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>MTLDevice_id</type> <name>mtlDevice</name></member>
+ </type>
+ <type category="struct" name="VkExportMetalCommandQueueInfoEXT" structextends="VkExportMetalObjectsInfoEXT" allowduplicate="true">
+ <member values="VK_STRUCTURE_TYPE_EXPORT_METAL_COMMAND_QUEUE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkQueue</type> <name>queue</name></member>
+ <member><type>MTLCommandQueue_id</type> <name>mtlCommandQueue</name></member>
+ </type>
+ <type category="struct" name="VkExportMetalBufferInfoEXT" structextends="VkExportMetalObjectsInfoEXT" allowduplicate="true">
+ <member values="VK_STRUCTURE_TYPE_EXPORT_METAL_BUFFER_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkDeviceMemory</type> <name>memory</name></member>
+ <member><type>MTLBuffer_id</type> <name>mtlBuffer</name></member>
+ </type>
+ <type category="struct" name="VkImportMetalBufferInfoEXT" structextends="VkMemoryAllocateInfo" allowduplicate="false">
+ <member values="VK_STRUCTURE_TYPE_IMPORT_METAL_BUFFER_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>MTLBuffer_id</type> <name>mtlBuffer</name></member>
+ </type>
+ <type category="struct" name="VkExportMetalTextureInfoEXT" structextends="VkExportMetalObjectsInfoEXT" allowduplicate="true">
+ <member values="VK_STRUCTURE_TYPE_EXPORT_METAL_TEXTURE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkImage</type> <name>image</name></member>
+ <member optional="true"><type>VkImageView</type> <name>imageView</name></member>
+ <member optional="true"><type>VkBufferView</type> <name>bufferView</name></member>
+ <member><type>VkImageAspectFlagBits</type> <name>plane</name></member>
+ <member><type>MTLTexture_id</type> <name>mtlTexture</name></member>
+ </type>
+ <type category="struct" name="VkImportMetalTextureInfoEXT" structextends="VkImageCreateInfo" allowduplicate="true">
+ <member values="VK_STRUCTURE_TYPE_IMPORT_METAL_TEXTURE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkImageAspectFlagBits</type> <name>plane</name></member>
+ <member><type>MTLTexture_id</type> <name>mtlTexture</name></member>
+ </type>
+ <type category="struct" name="VkExportMetalIOSurfaceInfoEXT" structextends="VkExportMetalObjectsInfoEXT" allowduplicate="true">
+ <member values="VK_STRUCTURE_TYPE_EXPORT_METAL_IO_SURFACE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkImage</type> <name>image</name></member>
+ <member><type>IOSurfaceRef</type> <name>ioSurface</name></member>
+ </type>
+ <type category="struct" name="VkImportMetalIOSurfaceInfoEXT" structextends="VkImageCreateInfo" allowduplicate="false">
+ <member values="VK_STRUCTURE_TYPE_IMPORT_METAL_IO_SURFACE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>IOSurfaceRef</type> <name>ioSurface</name></member>
+ </type>
+ <type category="struct" name="VkExportMetalSharedEventInfoEXT" structextends="VkExportMetalObjectsInfoEXT" allowduplicate="true">
+ <member values="VK_STRUCTURE_TYPE_EXPORT_METAL_SHARED_EVENT_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member optional="true"><type>VkSemaphore</type> <name>semaphore</name></member>
+ <member optional="true"><type>VkEvent</type> <name>event</name></member>
+ <member><type>MTLSharedEvent_id</type> <name>mtlSharedEvent</name></member>
+ </type>
+ <type category="struct" name="VkImportMetalSharedEventInfoEXT" structextends="VkSemaphoreCreateInfo,VkEventCreateInfo" allowduplicate="false">
+ <member values="VK_STRUCTURE_TYPE_IMPORT_METAL_SHARED_EVENT_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>MTLSharedEvent_id</type> <name>mtlSharedEvent</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceNonSeamlessCubeMapFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NON_SEAMLESS_CUBE_MAP_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>nonSeamlessCubeMap</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDevicePipelineRobustnessFeaturesEXT" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_FEATURES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member noautovalidity="true" optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>pipelineRobustness</name></member>
+ </type>
+ <type category="struct" name="VkPipelineRobustnessCreateInfoEXT" structextends="VkGraphicsPipelineCreateInfo,VkComputePipelineCreateInfo,VkPipelineShaderStageCreateInfo,VkRayTracingPipelineCreateInfoKHR">
+ <member values="VK_STRUCTURE_TYPE_PIPELINE_ROBUSTNESS_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member noautovalidity="true" optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkPipelineRobustnessBufferBehaviorEXT</type> <name>storageBuffers</name></member>
+ <member><type>VkPipelineRobustnessBufferBehaviorEXT</type> <name>uniformBuffers</name></member>
+ <member><type>VkPipelineRobustnessBufferBehaviorEXT</type> <name>vertexInputs</name></member>
+ <member><type>VkPipelineRobustnessImageBehaviorEXT</type> <name>images</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDevicePipelineRobustnessPropertiesEXT" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_PROPERTIES_EXT"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="exact"><type>VkPipelineRobustnessBufferBehaviorEXT</type> <name>defaultRobustnessStorageBuffers</name></member>
+ <member limittype="exact"><type>VkPipelineRobustnessBufferBehaviorEXT</type> <name>defaultRobustnessUniformBuffers</name></member>
+ <member limittype="exact"><type>VkPipelineRobustnessBufferBehaviorEXT</type> <name>defaultRobustnessVertexInputs</name></member>
+ <member limittype="exact"><type>VkPipelineRobustnessImageBehaviorEXT</type> <name>defaultRobustnessImages</name></member>
+ </type>
+ <type category="struct" name="VkImageViewSampleWeightCreateInfoQCOM" structextends="VkImageViewCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_IMAGE_VIEW_SAMPLE_WEIGHT_CREATE_INFO_QCOM"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true">const <type>void</type>* <name>pNext</name></member>
+ <member><type>VkOffset2D</type> <name>filterCenter</name></member>
+ <member><type>VkExtent2D</type> <name>filterSize</name></member>
+ <member><type>uint32_t</type> <name>numPhases</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceImageProcessingFeaturesQCOM" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_FEATURES_QCOM"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true" noautovalidity="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>textureSampleWeighted</name></member>
+ <member><type>VkBool32</type> <name>textureBoxFilter</name></member>
+ <member><type>VkBool32</type> <name>textureBlockMatch</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceImageProcessingPropertiesQCOM" returnedonly="true" structextends="VkPhysicalDeviceProperties2">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_PROPERTIES_QCOM"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member limittype="max" optional="true"><type>uint32_t</type> <name>maxWeightFilterPhases</name></member>
+ <member limittype="max" optional="true"><type>VkExtent2D</type> <name>maxWeightFilterDimension</name></member>
+ <member limittype="max" optional="true"><type>VkExtent2D</type> <name>maxBlockMatchRegion</name></member>
+ <member limittype="max" optional="true"><type>VkExtent2D</type> <name>maxBoxFilterBlockSize</name></member>
+ </type>
+ <type category="struct" name="VkPhysicalDeviceTilePropertiesFeaturesQCOM" structextends="VkPhysicalDeviceFeatures2,VkDeviceCreateInfo">
+ <member values="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TILE_PROPERTIES_FEATURES_QCOM"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkBool32</type> <name>tileProperties</name></member>
+ </type>
+ <type category="struct" name="VkTilePropertiesQCOM">
+ <member values="VK_STRUCTURE_TYPE_TILE_PROPERTIES_QCOM"><type>VkStructureType</type> <name>sType</name></member>
+ <member optional="true"><type>void</type>* <name>pNext</name></member>
+ <member><type>VkExtent3D</type> <name>tileSize</name></member>
+ <member><type>VkExtent2D</type> <name>apronSize</name></member>
+ <member><type>VkOffset2D</type> <name>origin</name></member>
+ </type>
</types>
-
<comment>Vulkan enumerant (token) definitions</comment>
<enums name="API Constants" comment="Vulkan hardcoded constants - not an enumerated type, part of the header boilerplate">
- <enum value="256" name="VK_MAX_PHYSICAL_DEVICE_NAME_SIZE"/>
- <enum value="16" name="VK_UUID_SIZE"/>
- <enum value="8" name="VK_LUID_SIZE"/>
- <enum name="VK_LUID_SIZE_KHR" alias="VK_LUID_SIZE"/>
- <enum value="256" name="VK_MAX_EXTENSION_NAME_SIZE"/>
- <enum value="256" name="VK_MAX_DESCRIPTION_SIZE"/>
- <enum value="32" name="VK_MAX_MEMORY_TYPES"/>
- <enum value="16" name="VK_MAX_MEMORY_HEAPS" comment="The maximum number of unique memory heaps, each of which supporting 1 or more memory types"/>
- <enum value="1000.0f" name="VK_LOD_CLAMP_NONE"/>
- <enum value="(~0U)" name="VK_REMAINING_MIP_LEVELS"/>
- <enum value="(~0U)" name="VK_REMAINING_ARRAY_LAYERS"/>
- <enum value="(~0ULL)" name="VK_WHOLE_SIZE"/>
- <enum value="(~0U)" name="VK_ATTACHMENT_UNUSED"/>
- <enum value="1" name="VK_TRUE"/>
- <enum value="0" name="VK_FALSE"/>
- <enum value="(~0U)" name="VK_QUEUE_FAMILY_IGNORED"/>
- <enum value="(~0U-1)" name="VK_QUEUE_FAMILY_EXTERNAL"/>
- <enum name="VK_QUEUE_FAMILY_EXTERNAL_KHR" alias="VK_QUEUE_FAMILY_EXTERNAL"/>
- <enum value="(~0U-2)" name="VK_QUEUE_FAMILY_FOREIGN_EXT"/>
- <enum value="(~0U)" name="VK_SUBPASS_EXTERNAL"/>
- <enum value="32" name="VK_MAX_DEVICE_GROUP_SIZE"/>
- <enum name="VK_MAX_DEVICE_GROUP_SIZE_KHR" alias="VK_MAX_DEVICE_GROUP_SIZE"/>
- <enum value="256" name="VK_MAX_DRIVER_NAME_SIZE"/>
- <enum name="VK_MAX_DRIVER_NAME_SIZE_KHR" alias="VK_MAX_DRIVER_NAME_SIZE"/>
- <enum value="256" name="VK_MAX_DRIVER_INFO_SIZE"/>
- <enum name="VK_MAX_DRIVER_INFO_SIZE_KHR" alias="VK_MAX_DRIVER_INFO_SIZE"/>
- <enum value="(~0U)" name="VK_SHADER_UNUSED_KHR"/>
- <enum name="VK_SHADER_UNUSED_NV" alias="VK_SHADER_UNUSED_KHR"/>
+ <enum type="uint32_t" value="256" name="VK_MAX_PHYSICAL_DEVICE_NAME_SIZE"/>
+ <enum type="uint32_t" value="16" name="VK_UUID_SIZE"/>
+ <enum type="uint32_t" value="8" name="VK_LUID_SIZE"/>
+ <enum name="VK_LUID_SIZE_KHR" alias="VK_LUID_SIZE"/>
+ <enum type="uint32_t" value="256" name="VK_MAX_EXTENSION_NAME_SIZE"/>
+ <enum type="uint32_t" value="256" name="VK_MAX_DESCRIPTION_SIZE"/>
+ <enum type="uint32_t" value="32" name="VK_MAX_MEMORY_TYPES"/>
+ <enum type="uint32_t" value="16" name="VK_MAX_MEMORY_HEAPS" comment="The maximum number of unique memory heaps, each of which supporting 1 or more memory types"/>
+ <enum type="float" value="1000.0F" name="VK_LOD_CLAMP_NONE"/>
+ <enum type="uint32_t" value="(~0U)" name="VK_REMAINING_MIP_LEVELS"/>
+ <enum type="uint32_t" value="(~0U)" name="VK_REMAINING_ARRAY_LAYERS"/>
+ <enum type="uint64_t" value="(~0ULL)" name="VK_WHOLE_SIZE"/>
+ <enum type="uint32_t" value="(~0U)" name="VK_ATTACHMENT_UNUSED"/>
+ <enum type="uint32_t" value="1" name="VK_TRUE"/>
+ <enum type="uint32_t" value="0" name="VK_FALSE"/>
+ <enum type="uint32_t" value="(~0U)" name="VK_QUEUE_FAMILY_IGNORED"/>
+ <enum type="uint32_t" value="(~1U)" name="VK_QUEUE_FAMILY_EXTERNAL"/>
+ <enum name="VK_QUEUE_FAMILY_EXTERNAL_KHR" alias="VK_QUEUE_FAMILY_EXTERNAL"/>
+ <enum type="uint32_t" value="(~2U)" name="VK_QUEUE_FAMILY_FOREIGN_EXT"/>
+ <enum type="uint32_t" value="(~0U)" name="VK_SUBPASS_EXTERNAL"/>
+ <enum type="uint32_t" value="32" name="VK_MAX_DEVICE_GROUP_SIZE"/>
+ <enum name="VK_MAX_DEVICE_GROUP_SIZE_KHR" alias="VK_MAX_DEVICE_GROUP_SIZE"/>
+ <enum type="uint32_t" value="256" name="VK_MAX_DRIVER_NAME_SIZE"/>
+ <enum name="VK_MAX_DRIVER_NAME_SIZE_KHR" alias="VK_MAX_DRIVER_NAME_SIZE"/>
+ <enum type="uint32_t" value="256" name="VK_MAX_DRIVER_INFO_SIZE"/>
+ <enum name="VK_MAX_DRIVER_INFO_SIZE_KHR" alias="VK_MAX_DRIVER_INFO_SIZE"/>
+ <enum type="uint32_t" value="(~0U)" name="VK_SHADER_UNUSED_KHR"/>
+ <enum name="VK_SHADER_UNUSED_NV" alias="VK_SHADER_UNUSED_KHR"/>
+ <enum type="uint32_t" value="16" name="VK_MAX_GLOBAL_PRIORITY_SIZE_KHR"/>
+ <enum name="VK_MAX_GLOBAL_PRIORITY_SIZE_EXT" alias="VK_MAX_GLOBAL_PRIORITY_SIZE_KHR"/>
+ <enum type="uint32_t" value="32" name="VK_MAX_SHADER_MODULE_IDENTIFIER_SIZE_EXT"/>
</enums>
<comment>
@@ -5263,7 +7219,8 @@ typedef void <name>CAMetalLayer</name>;
<enums name="VkPipelineCacheHeaderVersion" type="enum">
<enum value="1" name="VK_PIPELINE_CACHE_HEADER_VERSION_ONE"/>
</enums>
- <enums name="VkPipelineCacheCreateFlagBits" type="bitmask"></enums>
+ <enums name="VkPipelineCacheCreateFlagBits" type="bitmask">
+ </enums>
<enums name="VkPrimitiveTopology" type="enum">
<enum value="0" name="VK_PRIMITIVE_TOPOLOGY_POINT_LIST"/>
<enum value="1" name="VK_PRIMITIVE_TOPOLOGY_LINE_LIST"/>
@@ -5653,7 +7610,7 @@ typedef void <name>CAMetalLayer</name>;
<comment>Error codes (negative values)</comment>
<enum value="-1" name="VK_ERROR_OUT_OF_HOST_MEMORY" comment="A host memory allocation has failed"/>
<enum value="-2" name="VK_ERROR_OUT_OF_DEVICE_MEMORY" comment="A device memory allocation has failed"/>
- <enum value="-3" name="VK_ERROR_INITIALIZATION_FAILED" comment="Initialization of a object has failed"/>
+ <enum value="-3" name="VK_ERROR_INITIALIZATION_FAILED" comment="Initialization of an object has failed"/>
<enum value="-4" name="VK_ERROR_DEVICE_LOST" comment="The logical device has been lost. See &lt;&lt;devsandqueues-lost-device&gt;&gt;"/>
<enum value="-5" name="VK_ERROR_MEMORY_MAP_FAILED" comment="Mapping of a memory object has failed"/>
<enum value="-6" name="VK_ERROR_LAYER_NOT_PRESENT" comment="Layer specified does not exist"/>
@@ -5722,8 +7679,10 @@ typedef void <name>CAMetalLayer</name>;
<enum bitpos="1" name="VK_CULL_MODE_BACK_BIT"/>
<enum value="0x00000003" name="VK_CULL_MODE_FRONT_AND_BACK"/>
</enums>
- <enums name="VkRenderPassCreateFlagBits" type="bitmask"></enums>
- <enums name="VkDeviceQueueCreateFlagBits" type="bitmask"></enums>
+ <enums name="VkRenderPassCreateFlagBits" type="bitmask">
+ </enums>
+ <enums name="VkDeviceQueueCreateFlagBits" type="bitmask">
+ </enums>
<enums name="VkMemoryPropertyFlagBits" type="bitmask">
<enum bitpos="0" name="VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT" comment="If otherwise stated, then allocate memory on device"/>
<enum bitpos="1" name="VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT" comment="Memory is mappable by host"/>
@@ -5767,7 +7726,7 @@ typedef void <name>CAMetalLayer</name>;
<enums name="VkBufferCreateFlagBits" type="bitmask">
<enum bitpos="0" name="VK_BUFFER_CREATE_SPARSE_BINDING_BIT" comment="Buffer should support sparse backing"/>
<enum bitpos="1" name="VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT" comment="Buffer should support sparse backing with partial residency"/>
- <enum bitpos="2" name="VK_BUFFER_CREATE_SPARSE_ALIASED_BIT" comment="Buffer should support constent data access to physical memory ranges mapped into multiple locations of sparse buffers"/>
+ <enum bitpos="2" name="VK_BUFFER_CREATE_SPARSE_ALIASED_BIT" comment="Buffer should support constant data access to physical memory ranges mapped into multiple locations of sparse buffers"/>
</enums>
<enums name="VkShaderStageFlagBits" type="bitmask">
<enum bitpos="0" name="VK_SHADER_STAGE_VERTEX_BIT"/>
@@ -5792,7 +7751,7 @@ typedef void <name>CAMetalLayer</name>;
<enums name="VkImageCreateFlagBits" type="bitmask">
<enum bitpos="0" name="VK_IMAGE_CREATE_SPARSE_BINDING_BIT" comment="Image should support sparse backing"/>
<enum bitpos="1" name="VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT" comment="Image should support sparse backing with partial residency"/>
- <enum bitpos="2" name="VK_IMAGE_CREATE_SPARSE_ALIASED_BIT" comment="Image should support constent data access to physical memory ranges mapped into multiple locations of sparse images"/>
+ <enum bitpos="2" name="VK_IMAGE_CREATE_SPARSE_ALIASED_BIT" comment="Image should support constant data access to physical memory ranges mapped into multiple locations of sparse images"/>
<enum bitpos="3" name="VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT" comment="Allows image views to have different format than the base image"/>
<enum bitpos="4" name="VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT" comment="Allows creating image views with cube type from the created image"/>
</enums>
@@ -5800,7 +7759,7 @@ typedef void <name>CAMetalLayer</name>;
</enums>
<enums name="VkSamplerCreateFlagBits" type="bitmask">
</enums>
- <enums name="VkPipelineCreateFlagBits" type="bitmask">
+ <enums name="VkPipelineCreateFlagBits" type="bitmask" comment="Note that the gap at bitpos 10 is unused, and can be reserved">
<enum bitpos="0" name="VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT"/>
<enum bitpos="1" name="VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT"/>
<enum bitpos="2" name="VK_PIPELINE_CREATE_DERIVATIVE_BIT"/>
@@ -5816,7 +7775,8 @@ typedef void <name>CAMetalLayer</name>;
<enums name="VkFenceCreateFlagBits" type="bitmask">
<enum bitpos="0" name="VK_FENCE_CREATE_SIGNALED_BIT"/>
</enums>
- <comment>When VkSemaphoreCreateFlagBits is first extended, need to add a bitmask enums tag for it here</comment>
+ <enums name="VkSemaphoreCreateFlagBits" type="bitmask">
+ </enums>
<enums name="VkFormatFeatureFlagBits" type="bitmask">
<enum bitpos="0" name="VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT" comment="Format can be used for sampled images (SAMPLED_IMAGE and COMBINED_IMAGE_SAMPLER descriptor types)"/>
<enum bitpos="1" name="VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT" comment="Format can be used for storage images (STORAGE_IMAGE descriptor type)"/>
@@ -5918,7 +7878,7 @@ typedef void <name>CAMetalLayer</name>;
<enum bitpos="0" name="VK_STENCIL_FACE_FRONT_BIT" comment="Front face"/>
<enum bitpos="1" name="VK_STENCIL_FACE_BACK_BIT" comment="Back face"/>
<enum value="0x00000003" name="VK_STENCIL_FACE_FRONT_AND_BACK" comment="Front and back faces"/>
- <enum name="VK_STENCIL_FRONT_AND_BACK" alias="VK_STENCIL_FACE_FRONT_AND_BACK" comment="Alias for backwards compatibility"/>
+ <enum name="VK_STENCIL_FRONT_AND_BACK" alias="VK_STENCIL_FACE_FRONT_AND_BACK" comment="Backwards-compatible alias containing a typo"/>
</enums>
<enums name="VkDescriptorPoolCreateFlagBits" type="bitmask">
<enum bitpos="0" name="VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT" comment="Descriptor sets may be freed individually"/>
@@ -6017,8 +7977,10 @@ typedef void <name>CAMetalLayer</name>;
<enum name="VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT" alias="VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT" comment="Backwards-compatible alias containing a typo"/>
<enum value="29" name="VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT"/>
<enum value="30" name="VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT"/>
- <!--<enum value="31" name="VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT" comment="Removed NVX_device_generated_commands"/>-->
- <!--<enum value="32" name="VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT" comment="Removed NVX_device_generated_commands"/>-->
+ <comment>NVX_device_generated_commands formerly used these enum values, but that extension has been removed
+ value 31 / name VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT
+ value 32 / name VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT
+ </comment>
<enum value="33" name="VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT"/>
<enum name="VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT" alias="VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT" comment="Backwards-compatible alias containing a typo"/>
</enums>
@@ -6063,6 +8025,7 @@ typedef void <name>CAMetalLayer</name>;
<enum value="4" name="VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT"/>
<enum value="5" name="VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT"/>
<enum value="6" name="VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT"/>
+ <enum value="7" name="VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHE_EXT"/>
</enums>
<enums name="VkSubgroupFeatureFlagBits" type="bitmask">
<enum bitpos="0" name="VK_SUBGROUP_FEATURE_BASIC_BIT" comment="Basic subgroup operations"/>
@@ -6092,7 +8055,7 @@ typedef void <name>CAMetalLayer</name>;
<enum value="6" name="VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_NV"/>
<enum value="7" name="VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_TASKS_NV"/>
</enums>
- <enums name="VkPrivateDataSlotCreateFlagBitsEXT" type="bitmask">
+ <enums name="VkPrivateDataSlotCreateFlagBits" type="bitmask">
</enums>
<enums name="VkDescriptorSetLayoutCreateFlagBits" type="bitmask">
</enums>
@@ -6237,11 +8200,15 @@ typedef void <name>CAMetalLayer</name>;
<enum value="1" name="VK_SHADER_INFO_TYPE_BINARY_AMD"/>
<enum value="2" name="VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD"/>
</enums>
- <enums name="VkQueueGlobalPriorityEXT" type="enum">
- <enum value="128" name="VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT"/>
- <enum value="256" name="VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT"/>
- <enum value="512" name="VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT"/>
- <enum value="1024" name="VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT"/>
+ <enums name="VkQueueGlobalPriorityKHR" type="enum">
+ <enum value="128" name="VK_QUEUE_GLOBAL_PRIORITY_LOW_KHR"/>
+ <enum value="256" name="VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR"/>
+ <enum value="512" name="VK_QUEUE_GLOBAL_PRIORITY_HIGH_KHR"/>
+ <enum value="1024" name="VK_QUEUE_GLOBAL_PRIORITY_REALTIME_KHR"/>
+ <enum name="VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT" alias="VK_QUEUE_GLOBAL_PRIORITY_LOW_KHR"/>
+ <enum name="VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT" alias="VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR"/>
+ <enum name="VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT" alias="VK_QUEUE_GLOBAL_PRIORITY_HIGH_KHR"/>
+ <enum name="VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT" alias="VK_QUEUE_GLOBAL_PRIORITY_REALTIME_KHR"/>
</enums>
<enums name="VkDebugUtilsMessageSeverityFlagBitsEXT" type="bitmask">
<enum bitpos="0" name="VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT"/>
@@ -6295,6 +8262,15 @@ typedef void <name>CAMetalLayer</name>;
<enum value="12" name="VK_DRIVER_ID_BROADCOM_PROPRIETARY" comment="Broadcom Inc."/>
<enum value="13" name="VK_DRIVER_ID_MESA_LLVMPIPE" comment="Mesa"/>
<enum value="14" name="VK_DRIVER_ID_MOLTENVK" comment="MoltenVK"/>
+ <enum value="15" name="VK_DRIVER_ID_COREAVI_PROPRIETARY" comment="Core Avionics &amp; Industrial Inc."/>
+ <enum value="16" name="VK_DRIVER_ID_JUICE_PROPRIETARY" comment="Juice Technologies, Inc."/>
+ <enum value="17" name="VK_DRIVER_ID_VERISILICON_PROPRIETARY" comment="Verisilicon, Inc."/>
+ <enum value="18" name="VK_DRIVER_ID_MESA_TURNIP" comment="Mesa open source project"/>
+ <enum value="19" name="VK_DRIVER_ID_MESA_V3DV" comment="Mesa open source project"/>
+ <enum value="20" name="VK_DRIVER_ID_MESA_PANVK" comment="Mesa open source project"/>
+ <enum value="21" name="VK_DRIVER_ID_SAMSUNG_PROPRIETARY" comment="Samsung Electronics Co., Ltd."/>
+ <enum value="22" name="VK_DRIVER_ID_MESA_VENUS" comment="Mesa open source project"/>
+ <enum value="23" name="VK_DRIVER_ID_MESA_DOZEN" comment="Mesa open source project"/>
</enums>
<enums name="VkConditionalRenderingFlagBitsEXT" type="bitmask">
<enum bitpos="0" name="VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT"/>
@@ -6328,9 +8304,10 @@ typedef void <name>CAMetalLayer</name>;
</enums>
<enums name="VkGeometryInstanceFlagBitsKHR" type="bitmask">
<enum bitpos="0" name="VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR"/>
- <enum bitpos="1" name="VK_GEOMETRY_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE_BIT_KHR"/>
+ <enum bitpos="1" name="VK_GEOMETRY_INSTANCE_TRIANGLE_FLIP_FACING_BIT_KHR"/>
<enum bitpos="2" name="VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR"/>
<enum bitpos="3" name="VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_KHR"/>
+ <enum name="VK_GEOMETRY_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE_BIT_KHR" alias="VK_GEOMETRY_INSTANCE_TRIANGLE_FLIP_FACING_BIT_KHR"/>
</enums>
<enums name="VkGeometryFlagBitsKHR" type="bitmask">
<enum bitpos="0" name="VK_GEOMETRY_OPAQUE_BIT_KHR"/>
@@ -6421,11 +8398,15 @@ typedef void <name>CAMetalLayer</name>;
<enum bitpos="0" name="VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV"/>
<enum bitpos="1" name="VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV"/>
<enum bitpos="2" name="VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV"/>
+ <enum bitpos="3" name="VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_ERROR_REPORTING_BIT_NV"/>
</enums>
- <enums name="VkPipelineCreationFeedbackFlagBitsEXT" type="bitmask">
- <enum bitpos="0" name="VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT_EXT"/>
- <enum bitpos="1" name="VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT"/>
- <enum bitpos="2" name="VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT_EXT"/>
+ <enums name="VkPipelineCreationFeedbackFlagBits" type="bitmask">
+ <enum bitpos="0" name="VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT"/>
+ <enum name="VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT_EXT" alias="VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT"/>
+ <enum bitpos="1" name="VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT"/>
+ <enum name="VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT" alias="VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT"/>
+ <enum bitpos="2" name="VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT"/>
+ <enum name="VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT_EXT" alias="VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT"/>
</enums>
<enums name="VkFullScreenExclusiveEXT" type="enum">
<enum value="0" name="VK_FULL_SCREEN_EXCLUSIVE_DEFAULT_EXT"/>
@@ -6437,9 +8418,9 @@ typedef void <name>CAMetalLayer</name>;
<enum value="0" name="VK_PERFORMANCE_COUNTER_SCOPE_COMMAND_BUFFER_KHR"/>
<enum value="1" name="VK_PERFORMANCE_COUNTER_SCOPE_RENDER_PASS_KHR"/>
<enum value="2" name="VK_PERFORMANCE_COUNTER_SCOPE_COMMAND_KHR"/>
- <enum name="VK_QUERY_SCOPE_COMMAND_BUFFER_KHR" alias="VK_PERFORMANCE_COUNTER_SCOPE_COMMAND_BUFFER_KHR"/>
- <enum name="VK_QUERY_SCOPE_RENDER_PASS_KHR" alias="VK_PERFORMANCE_COUNTER_SCOPE_RENDER_PASS_KHR"/>
- <enum name="VK_QUERY_SCOPE_COMMAND_KHR" alias="VK_PERFORMANCE_COUNTER_SCOPE_COMMAND_KHR"/>
+ <enum name="VK_QUERY_SCOPE_COMMAND_BUFFER_KHR" alias="VK_PERFORMANCE_COUNTER_SCOPE_COMMAND_BUFFER_KHR" comment="Backwards-compatible alias containing a typo"/>
+ <enum name="VK_QUERY_SCOPE_RENDER_PASS_KHR" alias="VK_PERFORMANCE_COUNTER_SCOPE_RENDER_PASS_KHR" comment="Backwards-compatible alias containing a typo"/>
+ <enum name="VK_QUERY_SCOPE_COMMAND_KHR" alias="VK_PERFORMANCE_COUNTER_SCOPE_COMMAND_KHR" comment="Backwards-compatible alias containing a typo"/>
</enums>
<enums name="VkPerformanceCounterUnitKHR" type="enum">
<enum value="0" name="VK_PERFORMANCE_COUNTER_UNIT_GENERIC_KHR"/>
@@ -6514,12 +8495,17 @@ typedef void <name>CAMetalLayer</name>;
</enums>
<enums name="VkPipelineCompilerControlFlagBitsAMD" type="bitmask">
</enums>
- <enums name="VkToolPurposeFlagBitsEXT" type="bitmask">
- <enum bitpos="0" name="VK_TOOL_PURPOSE_VALIDATION_BIT_EXT"/>
- <enum bitpos="1" name="VK_TOOL_PURPOSE_PROFILING_BIT_EXT"/>
- <enum bitpos="2" name="VK_TOOL_PURPOSE_TRACING_BIT_EXT"/>
- <enum bitpos="3" name="VK_TOOL_PURPOSE_ADDITIONAL_FEATURES_BIT_EXT"/>
- <enum bitpos="4" name="VK_TOOL_PURPOSE_MODIFYING_FEATURES_BIT_EXT"/>
+ <enums name="VkToolPurposeFlagBits" type="bitmask">
+ <enum bitpos="0" name="VK_TOOL_PURPOSE_VALIDATION_BIT"/>
+ <enum name="VK_TOOL_PURPOSE_VALIDATION_BIT_EXT" alias="VK_TOOL_PURPOSE_VALIDATION_BIT"/>
+ <enum bitpos="1" name="VK_TOOL_PURPOSE_PROFILING_BIT"/>
+ <enum name="VK_TOOL_PURPOSE_PROFILING_BIT_EXT" alias="VK_TOOL_PURPOSE_PROFILING_BIT"/>
+ <enum bitpos="2" name="VK_TOOL_PURPOSE_TRACING_BIT"/>
+ <enum name="VK_TOOL_PURPOSE_TRACING_BIT_EXT" alias="VK_TOOL_PURPOSE_TRACING_BIT"/>
+ <enum bitpos="3" name="VK_TOOL_PURPOSE_ADDITIONAL_FEATURES_BIT"/>
+ <enum name="VK_TOOL_PURPOSE_ADDITIONAL_FEATURES_BIT_EXT" alias="VK_TOOL_PURPOSE_ADDITIONAL_FEATURES_BIT"/>
+ <enum bitpos="4" name="VK_TOOL_PURPOSE_MODIFYING_FEATURES_BIT"/>
+ <enum name="VK_TOOL_PURPOSE_MODIFYING_FEATURES_BIT_EXT" alias="VK_TOOL_PURPOSE_MODIFYING_FEATURES_BIT"/>
</enums>
<enums name="VkFragmentShadingRateCombinerOpKHR" type="enum">
<enum value="0" name="VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR"/>
@@ -6546,7 +8532,443 @@ typedef void <name>CAMetalLayer</name>;
<enum value="0" name="VK_FRAGMENT_SHADING_RATE_TYPE_FRAGMENT_SIZE_NV"/>
<enum value="1" name="VK_FRAGMENT_SHADING_RATE_TYPE_ENUMS_NV"/>
</enums>
+ <enums name="VkSubpassMergeStatusEXT" type="enum">
+ <enum value="0" name="VK_SUBPASS_MERGE_STATUS_MERGED_EXT"/>
+ <enum value="1" name="VK_SUBPASS_MERGE_STATUS_DISALLOWED_EXT"/>
+ <enum value="2" name="VK_SUBPASS_MERGE_STATUS_NOT_MERGED_SIDE_EFFECTS_EXT"/>
+ <enum value="3" name="VK_SUBPASS_MERGE_STATUS_NOT_MERGED_SAMPLES_MISMATCH_EXT"/>
+ <enum value="4" name="VK_SUBPASS_MERGE_STATUS_NOT_MERGED_VIEWS_MISMATCH_EXT"/>
+ <enum value="5" name="VK_SUBPASS_MERGE_STATUS_NOT_MERGED_ALIASING_EXT"/>
+ <enum value="6" name="VK_SUBPASS_MERGE_STATUS_NOT_MERGED_DEPENDENCIES_EXT"/>
+ <enum value="7" name="VK_SUBPASS_MERGE_STATUS_NOT_MERGED_INCOMPATIBLE_INPUT_ATTACHMENT_EXT"/>
+ <enum value="8" name="VK_SUBPASS_MERGE_STATUS_NOT_MERGED_TOO_MANY_ATTACHMENTS_EXT"/>
+ <enum value="9" name="VK_SUBPASS_MERGE_STATUS_NOT_MERGED_INSUFFICIENT_STORAGE_EXT"/>
+ <enum value="10" name="VK_SUBPASS_MERGE_STATUS_NOT_MERGED_DEPTH_STENCIL_COUNT_EXT"/>
+ <enum value="11" name="VK_SUBPASS_MERGE_STATUS_NOT_MERGED_RESOLVE_ATTACHMENT_REUSE_EXT"/>
+ <enum value="12" name="VK_SUBPASS_MERGE_STATUS_NOT_MERGED_SINGLE_SUBPASS_EXT"/>
+ <enum value="13" name="VK_SUBPASS_MERGE_STATUS_NOT_MERGED_UNSPECIFIED_EXT"/>
+ </enums>
+ <enums name="VkAccessFlagBits2" type="bitmask" bitwidth="64">
+ <enum value="0" name="VK_ACCESS_2_NONE"/>
+ <enum name="VK_ACCESS_2_NONE_KHR" alias="VK_ACCESS_2_NONE"/>
+ <enum bitpos="0" name="VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT"/>
+ <enum name="VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT_KHR" alias="VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT"/>
+ <enum bitpos="1" name="VK_ACCESS_2_INDEX_READ_BIT"/>
+ <enum name="VK_ACCESS_2_INDEX_READ_BIT_KHR" alias="VK_ACCESS_2_INDEX_READ_BIT"/>
+ <enum bitpos="2" name="VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT"/>
+ <enum name="VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT_KHR" alias="VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT"/>
+ <enum bitpos="3" name="VK_ACCESS_2_UNIFORM_READ_BIT"/>
+ <enum name="VK_ACCESS_2_UNIFORM_READ_BIT_KHR" alias="VK_ACCESS_2_UNIFORM_READ_BIT"/>
+ <enum bitpos="4" name="VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT"/>
+ <enum name="VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT_KHR" alias="VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT"/>
+ <enum bitpos="5" name="VK_ACCESS_2_SHADER_READ_BIT"/>
+ <enum name="VK_ACCESS_2_SHADER_READ_BIT_KHR" alias="VK_ACCESS_2_SHADER_READ_BIT"/>
+ <enum bitpos="6" name="VK_ACCESS_2_SHADER_WRITE_BIT"/>
+ <enum name="VK_ACCESS_2_SHADER_WRITE_BIT_KHR" alias="VK_ACCESS_2_SHADER_WRITE_BIT"/>
+ <enum bitpos="7" name="VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT"/>
+ <enum name="VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT_KHR" alias="VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT"/>
+ <enum bitpos="8" name="VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT"/>
+ <enum name="VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT_KHR" alias="VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT"/>
+ <enum bitpos="9" name="VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT"/>
+ <enum name="VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT_KHR" alias="VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT"/>
+ <enum bitpos="10" name="VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT"/>
+ <enum name="VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT_KHR" alias="VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT"/>
+ <enum bitpos="11" name="VK_ACCESS_2_TRANSFER_READ_BIT"/>
+ <enum name="VK_ACCESS_2_TRANSFER_READ_BIT_KHR" alias="VK_ACCESS_2_TRANSFER_READ_BIT"/>
+ <enum bitpos="12" name="VK_ACCESS_2_TRANSFER_WRITE_BIT"/>
+ <enum name="VK_ACCESS_2_TRANSFER_WRITE_BIT_KHR" alias="VK_ACCESS_2_TRANSFER_WRITE_BIT"/>
+ <enum bitpos="13" name="VK_ACCESS_2_HOST_READ_BIT"/>
+ <enum name="VK_ACCESS_2_HOST_READ_BIT_KHR" alias="VK_ACCESS_2_HOST_READ_BIT"/>
+ <enum bitpos="14" name="VK_ACCESS_2_HOST_WRITE_BIT"/>
+ <enum name="VK_ACCESS_2_HOST_WRITE_BIT_KHR" alias="VK_ACCESS_2_HOST_WRITE_BIT"/>
+ <enum bitpos="15" name="VK_ACCESS_2_MEMORY_READ_BIT"/>
+ <enum name="VK_ACCESS_2_MEMORY_READ_BIT_KHR" alias="VK_ACCESS_2_MEMORY_READ_BIT"/>
+ <enum bitpos="16" name="VK_ACCESS_2_MEMORY_WRITE_BIT"/>
+ <enum name="VK_ACCESS_2_MEMORY_WRITE_BIT_KHR" alias="VK_ACCESS_2_MEMORY_WRITE_BIT"/>
+ <comment>bitpos 17-31 are specified by extensions to the original VkAccessFlagBits enum</comment>
+ <enum bitpos="32" name="VK_ACCESS_2_SHADER_SAMPLED_READ_BIT"/>
+ <enum name="VK_ACCESS_2_SHADER_SAMPLED_READ_BIT_KHR" alias="VK_ACCESS_2_SHADER_SAMPLED_READ_BIT"/>
+ <enum bitpos="33" name="VK_ACCESS_2_SHADER_STORAGE_READ_BIT"/>
+ <enum name="VK_ACCESS_2_SHADER_STORAGE_READ_BIT_KHR" alias="VK_ACCESS_2_SHADER_STORAGE_READ_BIT"/>
+ <enum bitpos="34" name="VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT"/>
+ <enum name="VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT_KHR" alias="VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT"/>
+ </enums>
+ <enums name="VkPipelineStageFlagBits2" type="bitmask" bitwidth="64">
+ <enum value="0" name="VK_PIPELINE_STAGE_2_NONE"/>
+ <enum name="VK_PIPELINE_STAGE_2_NONE_KHR" alias="VK_PIPELINE_STAGE_2_NONE"/>
+ <enum bitpos="0" name="VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT_KHR" alias="VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT"/>
+ <enum bitpos="1" name="VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT_KHR" alias="VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT"/>
+ <enum bitpos="2" name="VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT_KHR" alias="VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT"/>
+ <enum bitpos="3" name="VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT_KHR" alias="VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT"/>
+ <enum bitpos="4" name="VK_PIPELINE_STAGE_2_TESSELLATION_CONTROL_SHADER_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_TESSELLATION_CONTROL_SHADER_BIT_KHR" alias="VK_PIPELINE_STAGE_2_TESSELLATION_CONTROL_SHADER_BIT"/>
+ <enum bitpos="5" name="VK_PIPELINE_STAGE_2_TESSELLATION_EVALUATION_SHADER_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_TESSELLATION_EVALUATION_SHADER_BIT_KHR" alias="VK_PIPELINE_STAGE_2_TESSELLATION_EVALUATION_SHADER_BIT"/>
+ <enum bitpos="6" name="VK_PIPELINE_STAGE_2_GEOMETRY_SHADER_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_GEOMETRY_SHADER_BIT_KHR" alias="VK_PIPELINE_STAGE_2_GEOMETRY_SHADER_BIT"/>
+ <enum bitpos="7" name="VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT_KHR" alias="VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT"/>
+ <enum bitpos="8" name="VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT_KHR" alias="VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT"/>
+ <enum bitpos="9" name="VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT_KHR" alias="VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT"/>
+ <enum bitpos="10" name="VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR" alias="VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT"/>
+ <enum bitpos="11" name="VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT_KHR" alias="VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT"/>
+ <enum bitpos="12" name="VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT_KHR" alias="VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_TRANSFER_BIT" alias="VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT_KHR"/>
+ <enum name="VK_PIPELINE_STAGE_2_TRANSFER_BIT_KHR" alias="VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT"/>
+ <enum bitpos="13" name="VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT_KHR" alias="VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT"/>
+ <enum bitpos="14" name="VK_PIPELINE_STAGE_2_HOST_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_HOST_BIT_KHR" alias="VK_PIPELINE_STAGE_2_HOST_BIT"/>
+ <enum bitpos="15" name="VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT_KHR" alias="VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT"/>
+ <enum bitpos="16" name="VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR" alias="VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT"/>
+ <comment>bitpos 17-31 are specified by extensions to the original VkPipelineStageFlagBits enum</comment>
+ <enum bitpos="32" name="VK_PIPELINE_STAGE_2_COPY_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_COPY_BIT_KHR" alias="VK_PIPELINE_STAGE_2_COPY_BIT"/>
+ <enum bitpos="33" name="VK_PIPELINE_STAGE_2_RESOLVE_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_RESOLVE_BIT_KHR" alias="VK_PIPELINE_STAGE_2_RESOLVE_BIT"/>
+ <enum bitpos="34" name="VK_PIPELINE_STAGE_2_BLIT_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_BLIT_BIT_KHR" alias="VK_PIPELINE_STAGE_2_BLIT_BIT"/>
+ <enum bitpos="35" name="VK_PIPELINE_STAGE_2_CLEAR_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_CLEAR_BIT_KHR" alias="VK_PIPELINE_STAGE_2_CLEAR_BIT"/>
+ <enum bitpos="36" name="VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT_KHR" alias="VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT"/>
+ <enum bitpos="37" name="VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT_KHR" alias="VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT"/>
+ <enum bitpos="38" name="VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT"/>
+ <enum name="VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT_KHR" alias="VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT"/>
+ </enums>
+ <enums name="VkSubmitFlagBits" type="bitmask">
+ <enum bitpos="0" name="VK_SUBMIT_PROTECTED_BIT"/>
+ <enum name="VK_SUBMIT_PROTECTED_BIT_KHR" alias="VK_SUBMIT_PROTECTED_BIT"/>
+ </enums>
+ <enums name="VkEventCreateFlagBits" type="bitmask">
+ </enums>
+ <enums name="VkPipelineLayoutCreateFlagBits" type="bitmask">
+ </enums>
+ <enums name="VkProvokingVertexModeEXT" type="enum">
+ <enum value="0" name="VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT"/>
+ <enum value="1" name="VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT"/>
+ </enums>
+ <enums name="VkAccelerationStructureMotionInstanceTypeNV" type="enum">
+ <enum value="0" name="VK_ACCELERATION_STRUCTURE_MOTION_INSTANCE_TYPE_STATIC_NV"/>
+ <enum value="1" name="VK_ACCELERATION_STRUCTURE_MOTION_INSTANCE_TYPE_MATRIX_MOTION_NV"/>
+ <enum value="2" name="VK_ACCELERATION_STRUCTURE_MOTION_INSTANCE_TYPE_SRT_MOTION_NV"/>
+ </enums>
+ <enums name="VkPipelineColorBlendStateCreateFlagBits" type="bitmask">
+ </enums>
+ <enums name="VkPipelineDepthStencilStateCreateFlagBits" type="bitmask">
+ </enums>
+ <enums name="VkGraphicsPipelineLibraryFlagBitsEXT" type="bitmask">
+ <enum bitpos="0" name="VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT"/>
+ <enum bitpos="1" name="VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT"/>
+ <enum bitpos="2" name="VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT"/>
+ <enum bitpos="3" name="VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT"/>
+ </enums>
+ <enums name="VkVideoCodecOperationFlagBitsKHR" type="bitmask">
+ <enum value="0" name="VK_VIDEO_CODEC_OPERATION_INVALID_BIT_KHR"/>
+ </enums>
+ <enums name="VkVideoChromaSubsamplingFlagBitsKHR" type="bitmask" comment="Vulkan video chroma subsampling definitions">
+ <enum value="0" name="VK_VIDEO_CHROMA_SUBSAMPLING_INVALID_BIT_KHR"/>
+ <enum bitpos="0" name="VK_VIDEO_CHROMA_SUBSAMPLING_MONOCHROME_BIT_KHR"/>
+ <enum bitpos="1" name="VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR"/>
+ <enum bitpos="2" name="VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR"/>
+ <enum bitpos="3" name="VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR"/>
+ </enums>
+ <enums name="VkVideoComponentBitDepthFlagBitsKHR" type="bitmask" comment="Vulkan video component bit depth definitions">
+ <enum value="0" name="VK_VIDEO_COMPONENT_BIT_DEPTH_INVALID_KHR"/>
+ <enum bitpos="0" name="VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR"/>
+ <enum bitpos="2" name="VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR"/>
+ <enum bitpos="4" name="VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR"/>
+ </enums>
+ <enums name="VkVideoCapabilityFlagBitsKHR" type="bitmask">
+ <enum bitpos="0" name="VK_VIDEO_CAPABILITY_PROTECTED_CONTENT_BIT_KHR"/>
+ <enum bitpos="1" name="VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR"/>
+ </enums>
+ <enums name="VkVideoSessionCreateFlagBitsKHR" type="bitmask">
+ <enum value="0" name="VK_VIDEO_SESSION_CREATE_DEFAULT_KHR"/>
+ <enum bitpos="0" name="VK_VIDEO_SESSION_CREATE_PROTECTED_CONTENT_BIT_KHR"/>
+ </enums>
+ <enums name="VkVideoCodingQualityPresetFlagBitsKHR" type="bitmask">
+ <enum bitpos="0" name="VK_VIDEO_CODING_QUALITY_PRESET_NORMAL_BIT_KHR"/>
+ <enum bitpos="1" name="VK_VIDEO_CODING_QUALITY_PRESET_POWER_BIT_KHR"/>
+ <enum bitpos="2" name="VK_VIDEO_CODING_QUALITY_PRESET_QUALITY_BIT_KHR"/>
+ </enums>
+ <enums name="VkVideoDecodeH264PictureLayoutFlagBitsEXT" type="bitmask">
+ <enum value="0" name="VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_PROGRESSIVE_EXT"/>
+ <enum bitpos="0" name="VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_INTERLEAVED_LINES_BIT_EXT"/>
+ <enum bitpos="1" name="VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_SEPARATE_PLANES_BIT_EXT"/>
+ </enums>
+ <enums name="VkVideoCodingControlFlagBitsKHR" type="bitmask">
+ <enum value="0" name="VK_VIDEO_CODING_CONTROL_DEFAULT_KHR"/>
+ <enum bitpos="0" name="VK_VIDEO_CODING_CONTROL_RESET_BIT_KHR"/>
+ </enums>
+ <enums name="VkQueryResultStatusKHR" type="enum">
+ <enum value="-1" name="VK_QUERY_RESULT_STATUS_ERROR_KHR"/>
+ <enum value="0" name="VK_QUERY_RESULT_STATUS_NOT_READY_KHR"/>
+ <enum value="1" name="VK_QUERY_RESULT_STATUS_COMPLETE_KHR"/>
+ </enums>
+ <enums name="VkVideoDecodeCapabilityFlagBitsKHR" type="bitmask">
+ <enum value="0" name="VK_VIDEO_DECODE_CAPABILITY_DEFAULT_KHR"/>
+ <enum bitpos="0" name="VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR"/>
+ <enum bitpos="1" name="VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR"/>
+ </enums>
+ <enums name="VkVideoDecodeFlagBitsKHR" type="bitmask">
+ <enum value="0" name="VK_VIDEO_DECODE_DEFAULT_KHR"/>
+ <enum bitpos="0" name="VK_VIDEO_DECODE_RESERVED_0_BIT_KHR"/>
+ </enums>
+ <enums name="VkVideoEncodeFlagBitsKHR" type="bitmask">
+ <enum value="0" name="VK_VIDEO_ENCODE_DEFAULT_KHR"/>
+ <enum bitpos="0" name="VK_VIDEO_ENCODE_RESERVED_0_BIT_KHR"/>
+ </enums>
+ <enums name="VkVideoEncodeCapabilityFlagBitsKHR" type="bitmask">
+ <enum value="0" name="VK_VIDEO_ENCODE_CAPABILITY_DEFAULT_KHR"/>
+ <enum bitpos="0" name="VK_VIDEO_ENCODE_CAPABILITY_PRECEDING_EXTERNALLY_ENCODED_BYTES_BIT_KHR"/>
+ </enums>
+ <enums name="VkVideoEncodeRateControlFlagBitsKHR" type="bitmask">
+ <enum value="0" name="VK_VIDEO_ENCODE_RATE_CONTROL_DEFAULT_KHR"/>
+ <enum bitpos="0" name="VK_VIDEO_ENCODE_RATE_CONTROL_RESERVED_0_BIT_KHR"/>
+ </enums>
+ <enums name="VkVideoEncodeRateControlModeFlagBitsKHR" type="bitmask">
+ <enum value="0" name="VK_VIDEO_ENCODE_RATE_CONTROL_MODE_NONE_BIT_KHR"/>
+ <enum value="1" name="VK_VIDEO_ENCODE_RATE_CONTROL_MODE_CBR_BIT_KHR"/>
+ <enum value="2" name="VK_VIDEO_ENCODE_RATE_CONTROL_MODE_VBR_BIT_KHR"/>
+ </enums>
+ <enums name="VkVideoEncodeH264CapabilityFlagBitsEXT" type="bitmask">
+ <enum bitpos="0" name="VK_VIDEO_ENCODE_H264_CAPABILITY_DIRECT_8X8_INFERENCE_ENABLED_BIT_EXT"/>
+ <enum bitpos="1" name="VK_VIDEO_ENCODE_H264_CAPABILITY_DIRECT_8X8_INFERENCE_DISABLED_BIT_EXT"/>
+ <enum bitpos="2" name="VK_VIDEO_ENCODE_H264_CAPABILITY_SEPARATE_COLOUR_PLANE_BIT_EXT"/>
+ <enum bitpos="3" name="VK_VIDEO_ENCODE_H264_CAPABILITY_QPPRIME_Y_ZERO_TRANSFORM_BYPASS_BIT_EXT"/>
+ <enum bitpos="4" name="VK_VIDEO_ENCODE_H264_CAPABILITY_SCALING_LISTS_BIT_EXT"/>
+ <enum bitpos="5" name="VK_VIDEO_ENCODE_H264_CAPABILITY_HRD_COMPLIANCE_BIT_EXT"/>
+ <enum bitpos="6" name="VK_VIDEO_ENCODE_H264_CAPABILITY_CHROMA_QP_OFFSET_BIT_EXT"/>
+ <enum bitpos="7" name="VK_VIDEO_ENCODE_H264_CAPABILITY_SECOND_CHROMA_QP_OFFSET_BIT_EXT"/>
+ <enum bitpos="8" name="VK_VIDEO_ENCODE_H264_CAPABILITY_PIC_INIT_QP_MINUS26_BIT_EXT"/>
+ <enum bitpos="9" name="VK_VIDEO_ENCODE_H264_CAPABILITY_WEIGHTED_PRED_BIT_EXT"/>
+ <enum bitpos="10" name="VK_VIDEO_ENCODE_H264_CAPABILITY_WEIGHTED_BIPRED_EXPLICIT_BIT_EXT"/>
+ <enum bitpos="11" name="VK_VIDEO_ENCODE_H264_CAPABILITY_WEIGHTED_BIPRED_IMPLICIT_BIT_EXT"/>
+ <enum bitpos="12" name="VK_VIDEO_ENCODE_H264_CAPABILITY_WEIGHTED_PRED_NO_TABLE_BIT_EXT"/>
+ <enum bitpos="13" name="VK_VIDEO_ENCODE_H264_CAPABILITY_TRANSFORM_8X8_BIT_EXT"/>
+ <enum bitpos="14" name="VK_VIDEO_ENCODE_H264_CAPABILITY_CABAC_BIT_EXT"/>
+ <enum bitpos="15" name="VK_VIDEO_ENCODE_H264_CAPABILITY_CAVLC_BIT_EXT"/>
+ <enum bitpos="16" name="VK_VIDEO_ENCODE_H264_CAPABILITY_DEBLOCKING_FILTER_DISABLED_BIT_EXT"/>
+ <enum bitpos="17" name="VK_VIDEO_ENCODE_H264_CAPABILITY_DEBLOCKING_FILTER_ENABLED_BIT_EXT"/>
+ <enum bitpos="18" name="VK_VIDEO_ENCODE_H264_CAPABILITY_DEBLOCKING_FILTER_PARTIAL_BIT_EXT"/>
+ <enum bitpos="19" name="VK_VIDEO_ENCODE_H264_CAPABILITY_DISABLE_DIRECT_SPATIAL_MV_PRED_BIT_EXT"/>
+ <enum bitpos="20" name="VK_VIDEO_ENCODE_H264_CAPABILITY_MULTIPLE_SLICE_PER_FRAME_BIT_EXT"/>
+ <enum bitpos="21" name="VK_VIDEO_ENCODE_H264_CAPABILITY_SLICE_MB_COUNT_BIT_EXT"/>
+ <enum bitpos="22" name="VK_VIDEO_ENCODE_H264_CAPABILITY_ROW_UNALIGNED_SLICE_BIT_EXT"/>
+ <enum bitpos="23" name="VK_VIDEO_ENCODE_H264_CAPABILITY_DIFFERENT_SLICE_TYPE_BIT_EXT"/>
+ <enum bitpos="24" name="VK_VIDEO_ENCODE_H264_CAPABILITY_B_FRAME_IN_L1_LIST_BIT_EXT"/>
+ </enums>
+ <enums name="VkVideoEncodeH264InputModeFlagBitsEXT" type="bitmask">
+ <enum bitpos="0" name="VK_VIDEO_ENCODE_H264_INPUT_MODE_FRAME_BIT_EXT"/>
+ <enum bitpos="1" name="VK_VIDEO_ENCODE_H264_INPUT_MODE_SLICE_BIT_EXT"/>
+ <enum bitpos="2" name="VK_VIDEO_ENCODE_H264_INPUT_MODE_NON_VCL_BIT_EXT"/>
+ </enums>
+ <enums name="VkVideoEncodeH264OutputModeFlagBitsEXT" type="bitmask">
+ <enum bitpos="0" name="VK_VIDEO_ENCODE_H264_OUTPUT_MODE_FRAME_BIT_EXT"/>
+ <enum bitpos="1" name="VK_VIDEO_ENCODE_H264_OUTPUT_MODE_SLICE_BIT_EXT"/>
+ <enum bitpos="2" name="VK_VIDEO_ENCODE_H264_OUTPUT_MODE_NON_VCL_BIT_EXT"/>
+ </enums>
+ <enums name="VkVideoEncodeH264RateControlStructureFlagBitsEXT" type="bitmask">
+ <enum value="0" name="VK_VIDEO_ENCODE_H264_RATE_CONTROL_STRUCTURE_UNKNOWN_EXT"/>
+ <enum bitpos="0" name="VK_VIDEO_ENCODE_H264_RATE_CONTROL_STRUCTURE_FLAT_BIT_EXT"/>
+ <enum bitpos="1" name="VK_VIDEO_ENCODE_H264_RATE_CONTROL_STRUCTURE_DYADIC_BIT_EXT"/>
+ </enums>
+ <enums name="VkImageFormatConstraintsFlagBitsFUCHSIA" type="bitmask">
+ </enums>
+ <enums name="VkImageConstraintsInfoFlagBitsFUCHSIA" type="bitmask">
+ <enum bitpos="0" name="VK_IMAGE_CONSTRAINTS_INFO_CPU_READ_RARELY_FUCHSIA"/>
+ <enum bitpos="1" name="VK_IMAGE_CONSTRAINTS_INFO_CPU_READ_OFTEN_FUCHSIA"/>
+ <enum bitpos="2" name="VK_IMAGE_CONSTRAINTS_INFO_CPU_WRITE_RARELY_FUCHSIA"/>
+ <enum bitpos="3" name="VK_IMAGE_CONSTRAINTS_INFO_CPU_WRITE_OFTEN_FUCHSIA"/>
+ <enum bitpos="4" name="VK_IMAGE_CONSTRAINTS_INFO_PROTECTED_OPTIONAL_FUCHSIA"/>
+ </enums>
+ <enums name="VkFormatFeatureFlagBits2" type="bitmask" bitwidth="64">
+ <enum bitpos="0" name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT_KHR" alias="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT"/>
+ <enum bitpos="1" name="VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT_KHR" alias="VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT"/>
+ <enum bitpos="2" name="VK_FORMAT_FEATURE_2_STORAGE_IMAGE_ATOMIC_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_STORAGE_IMAGE_ATOMIC_BIT_KHR" alias="VK_FORMAT_FEATURE_2_STORAGE_IMAGE_ATOMIC_BIT"/>
+ <enum bitpos="3" name="VK_FORMAT_FEATURE_2_UNIFORM_TEXEL_BUFFER_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_UNIFORM_TEXEL_BUFFER_BIT_KHR" alias="VK_FORMAT_FEATURE_2_UNIFORM_TEXEL_BUFFER_BIT"/>
+ <enum bitpos="4" name="VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_BIT_KHR" alias="VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_BIT"/>
+ <enum bitpos="5" name="VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_ATOMIC_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_ATOMIC_BIT_KHR" alias="VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_ATOMIC_BIT"/>
+ <enum bitpos="6" name="VK_FORMAT_FEATURE_2_VERTEX_BUFFER_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_VERTEX_BUFFER_BIT_KHR" alias="VK_FORMAT_FEATURE_2_VERTEX_BUFFER_BIT"/>
+ <enum bitpos="7" name="VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT_KHR" alias="VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT"/>
+ <enum bitpos="8" name="VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT_KHR" alias="VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT"/>
+ <enum bitpos="9" name="VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT_KHR" alias="VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT"/>
+ <enum bitpos="10" name="VK_FORMAT_FEATURE_2_BLIT_SRC_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_BLIT_SRC_BIT_KHR" alias="VK_FORMAT_FEATURE_2_BLIT_SRC_BIT"/>
+ <enum bitpos="11" name="VK_FORMAT_FEATURE_2_BLIT_DST_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_BLIT_DST_BIT_KHR" alias="VK_FORMAT_FEATURE_2_BLIT_DST_BIT"/>
+ <enum bitpos="12" name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_LINEAR_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_LINEAR_BIT_KHR" alias="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_LINEAR_BIT"/>
+ <enum bitpos="13" name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_CUBIC_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_CUBIC_BIT_EXT" alias="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_CUBIC_BIT"/>
+ <enum bitpos="14" name="VK_FORMAT_FEATURE_2_TRANSFER_SRC_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_TRANSFER_SRC_BIT_KHR" alias="VK_FORMAT_FEATURE_2_TRANSFER_SRC_BIT"/>
+ <enum bitpos="15" name="VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT_KHR" alias="VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT"/>
+ <enum bitpos="16" name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_MINMAX_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_MINMAX_BIT_KHR" alias="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_MINMAX_BIT"/>
+ <enum bitpos="17" name="VK_FORMAT_FEATURE_2_MIDPOINT_CHROMA_SAMPLES_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_MIDPOINT_CHROMA_SAMPLES_BIT_KHR" alias="VK_FORMAT_FEATURE_2_MIDPOINT_CHROMA_SAMPLES_BIT"/>
+ <enum bitpos="18" name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR" alias="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT"/>
+ <enum bitpos="19" name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR" alias="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT"/>
+ <enum bitpos="20" name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT_KHR" alias="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT"/>
+ <enum bitpos="21" name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR" alias="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT"/>
+ <enum bitpos="22" name="VK_FORMAT_FEATURE_2_DISJOINT_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_DISJOINT_BIT_KHR" alias="VK_FORMAT_FEATURE_2_DISJOINT_BIT"/>
+ <enum bitpos="23" name="VK_FORMAT_FEATURE_2_COSITED_CHROMA_SAMPLES_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_COSITED_CHROMA_SAMPLES_BIT_KHR" alias="VK_FORMAT_FEATURE_2_COSITED_CHROMA_SAMPLES_BIT"/>
+ <enum bitpos="31" name="VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT_KHR" alias="VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT"/>
+ <enum bitpos="32" name="VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT_KHR" alias="VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT"/>
+ <enum bitpos="33" name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT"/>
+ <enum name="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT_KHR" alias="VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT"/>
+ </enums>
+ <enums name="VkRenderingFlagBits" type="bitmask">
+ <enum bitpos="0" name="VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT"/>
+ <enum name="VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR" alias="VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT"/>
+ <enum bitpos="1" name="VK_RENDERING_SUSPENDING_BIT"/>
+ <enum name="VK_RENDERING_SUSPENDING_BIT_KHR" alias="VK_RENDERING_SUSPENDING_BIT"/>
+ <enum bitpos="2" name="VK_RENDERING_RESUMING_BIT"/>
+ <enum name="VK_RENDERING_RESUMING_BIT_KHR" alias="VK_RENDERING_RESUMING_BIT"/>
+ </enums>
+ <enums name="VkVideoEncodeH265CapabilityFlagBitsEXT" type="bitmask">
+ <enum bitpos="0" name="VK_VIDEO_ENCODE_H265_CAPABILITY_SEPARATE_COLOUR_PLANE_BIT_EXT"/>
+ <enum bitpos="1" name="VK_VIDEO_ENCODE_H265_CAPABILITY_SCALING_LISTS_BIT_EXT"/>
+ <enum bitpos="2" name="VK_VIDEO_ENCODE_H265_CAPABILITY_SAMPLE_ADAPTIVE_OFFSET_ENABLED_BIT_EXT"/>
+ <enum bitpos="3" name="VK_VIDEO_ENCODE_H265_CAPABILITY_PCM_ENABLE_BIT_EXT"/>
+ <enum bitpos="4" name="VK_VIDEO_ENCODE_H265_CAPABILITY_SPS_TEMPORAL_MVP_ENABLED_BIT_EXT"/>
+ <enum bitpos="5" name="VK_VIDEO_ENCODE_H265_CAPABILITY_HRD_COMPLIANCE_BIT_EXT"/>
+ <enum bitpos="6" name="VK_VIDEO_ENCODE_H265_CAPABILITY_INIT_QP_MINUS26_BIT_EXT"/>
+ <enum bitpos="7" name="VK_VIDEO_ENCODE_H265_CAPABILITY_LOG2_PARALLEL_MERGE_LEVEL_MINUS2_BIT_EXT"/>
+ <enum bitpos="8" name="VK_VIDEO_ENCODE_H265_CAPABILITY_SIGN_DATA_HIDING_ENABLED_BIT_EXT"/>
+ <enum bitpos="9" name="VK_VIDEO_ENCODE_H265_CAPABILITY_TRANSFORM_SKIP_ENABLED_BIT_EXT"/>
+ <enum bitpos="10" name="VK_VIDEO_ENCODE_H265_CAPABILITY_TRANSFORM_SKIP_DISABLED_BIT_EXT"/>
+ <enum bitpos="11" name="VK_VIDEO_ENCODE_H265_CAPABILITY_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_BIT_EXT"/>
+ <enum bitpos="12" name="VK_VIDEO_ENCODE_H265_CAPABILITY_WEIGHTED_PRED_BIT_EXT"/>
+ <enum bitpos="13" name="VK_VIDEO_ENCODE_H265_CAPABILITY_WEIGHTED_BIPRED_BIT_EXT"/>
+ <enum bitpos="14" name="VK_VIDEO_ENCODE_H265_CAPABILITY_WEIGHTED_PRED_NO_TABLE_BIT_EXT"/>
+ <enum bitpos="15" name="VK_VIDEO_ENCODE_H265_CAPABILITY_TRANSQUANT_BYPASS_ENABLED_BIT_EXT"/>
+ <enum bitpos="16" name="VK_VIDEO_ENCODE_H265_CAPABILITY_ENTROPY_CODING_SYNC_ENABLED_BIT_EXT"/>
+ <enum bitpos="17" name="VK_VIDEO_ENCODE_H265_CAPABILITY_DEBLOCKING_FILTER_OVERRIDE_ENABLED_BIT_EXT"/>
+ <enum bitpos="18" name="VK_VIDEO_ENCODE_H265_CAPABILITY_MULTIPLE_TILE_PER_FRAME_BIT_EXT"/>
+ <enum bitpos="19" name="VK_VIDEO_ENCODE_H265_CAPABILITY_MULTIPLE_SLICE_PER_TILE_BIT_EXT"/>
+ <enum bitpos="20" name="VK_VIDEO_ENCODE_H265_CAPABILITY_MULTIPLE_TILE_PER_SLICE_BIT_EXT"/>
+ <enum bitpos="21" name="VK_VIDEO_ENCODE_H265_CAPABILITY_SLICE_SEGMENT_CTB_COUNT_BIT_EXT"/>
+ <enum bitpos="22" name="VK_VIDEO_ENCODE_H265_CAPABILITY_ROW_UNALIGNED_SLICE_SEGMENT_BIT_EXT"/>
+ <enum bitpos="23" name="VK_VIDEO_ENCODE_H265_CAPABILITY_DEPENDENT_SLICE_SEGMENT_BIT_EXT"/>
+ <enum bitpos="24" name="VK_VIDEO_ENCODE_H265_CAPABILITY_DIFFERENT_SLICE_TYPE_BIT_EXT"/>
+ <enum bitpos="25" name="VK_VIDEO_ENCODE_H265_CAPABILITY_B_FRAME_IN_L1_LIST_BIT_EXT"/>
+ </enums>
+ <enums name="VkVideoEncodeH265InputModeFlagBitsEXT" type="bitmask">
+ <enum bitpos="0" name="VK_VIDEO_ENCODE_H265_INPUT_MODE_FRAME_BIT_EXT"/>
+ <enum bitpos="1" name="VK_VIDEO_ENCODE_H265_INPUT_MODE_SLICE_SEGMENT_BIT_EXT"/>
+ <enum bitpos="2" name="VK_VIDEO_ENCODE_H265_INPUT_MODE_NON_VCL_BIT_EXT"/>
+ </enums>
+ <enums name="VkVideoEncodeH265OutputModeFlagBitsEXT" type="bitmask">
+ <enum bitpos="0" name="VK_VIDEO_ENCODE_H265_OUTPUT_MODE_FRAME_BIT_EXT"/>
+ <enum bitpos="1" name="VK_VIDEO_ENCODE_H265_OUTPUT_MODE_SLICE_SEGMENT_BIT_EXT"/>
+ <enum bitpos="2" name="VK_VIDEO_ENCODE_H265_OUTPUT_MODE_NON_VCL_BIT_EXT"/>
+ </enums>
+ <enums name="VkVideoEncodeH265RateControlStructureFlagBitsEXT" type="bitmask">
+ <enum value="0" name="VK_VIDEO_ENCODE_H265_RATE_CONTROL_STRUCTURE_UNKNOWN_EXT"/>
+ <enum bitpos="0" name="VK_VIDEO_ENCODE_H265_RATE_CONTROL_STRUCTURE_FLAT_BIT_EXT"/>
+ <enum bitpos="1" name="VK_VIDEO_ENCODE_H265_RATE_CONTROL_STRUCTURE_DYADIC_BIT_EXT"/>
+ </enums>
+ <enums name="VkVideoEncodeH265CtbSizeFlagBitsEXT" type="bitmask">
+ <enum bitpos="0" name="VK_VIDEO_ENCODE_H265_CTB_SIZE_16_BIT_EXT"/>
+ <enum bitpos="1" name="VK_VIDEO_ENCODE_H265_CTB_SIZE_32_BIT_EXT"/>
+ <enum bitpos="2" name="VK_VIDEO_ENCODE_H265_CTB_SIZE_64_BIT_EXT"/>
+ </enums>
+ <enums name="VkVideoEncodeH265TransformBlockSizeFlagBitsEXT" type="bitmask">
+ <enum bitpos="0" name="VK_VIDEO_ENCODE_H265_TRANSFORM_BLOCK_SIZE_4_BIT_EXT"/>
+ <enum bitpos="1" name="VK_VIDEO_ENCODE_H265_TRANSFORM_BLOCK_SIZE_8_BIT_EXT"/>
+ <enum bitpos="2" name="VK_VIDEO_ENCODE_H265_TRANSFORM_BLOCK_SIZE_16_BIT_EXT"/>
+ <enum bitpos="3" name="VK_VIDEO_ENCODE_H265_TRANSFORM_BLOCK_SIZE_32_BIT_EXT"/>
+ </enums>
+ <enums name="VkExportMetalObjectTypeFlagBitsEXT" type="bitmask">
+ <enum bitpos="0" name="VK_EXPORT_METAL_OBJECT_TYPE_METAL_DEVICE_BIT_EXT"/>
+ <enum bitpos="1" name="VK_EXPORT_METAL_OBJECT_TYPE_METAL_COMMAND_QUEUE_BIT_EXT"/>
+ <enum bitpos="2" name="VK_EXPORT_METAL_OBJECT_TYPE_METAL_BUFFER_BIT_EXT"/>
+ <enum bitpos="3" name="VK_EXPORT_METAL_OBJECT_TYPE_METAL_TEXTURE_BIT_EXT"/>
+ <enum bitpos="4" name="VK_EXPORT_METAL_OBJECT_TYPE_METAL_IOSURFACE_BIT_EXT"/>
+ <enum bitpos="5" name="VK_EXPORT_METAL_OBJECT_TYPE_METAL_SHARED_EVENT_BIT_EXT"/>
+ </enums>
+ <enums name="VkInstanceCreateFlagBits" type="bitmask">
+ </enums>
+ <enums name="VkImageCompressionFlagBitsEXT" type="bitmask">
+ <enum value="0" name="VK_IMAGE_COMPRESSION_DEFAULT_EXT"/>
+ <enum bitpos="0" name="VK_IMAGE_COMPRESSION_FIXED_RATE_DEFAULT_EXT"/>
+ <enum bitpos="1" name="VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT"/>
+ <enum bitpos="2" name="VK_IMAGE_COMPRESSION_DISABLED_EXT"/>
+ </enums>
+ <enums name="VkImageCompressionFixedRateFlagBitsEXT" type="bitmask">
+ <enum value="0" name="VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT"/>
+ <enum bitpos="0" name="VK_IMAGE_COMPRESSION_FIXED_RATE_1BPC_BIT_EXT"/>
+ <enum bitpos="1" name="VK_IMAGE_COMPRESSION_FIXED_RATE_2BPC_BIT_EXT"/>
+ <enum bitpos="2" name="VK_IMAGE_COMPRESSION_FIXED_RATE_3BPC_BIT_EXT"/>
+ <enum bitpos="3" name="VK_IMAGE_COMPRESSION_FIXED_RATE_4BPC_BIT_EXT"/>
+ <enum bitpos="4" name="VK_IMAGE_COMPRESSION_FIXED_RATE_5BPC_BIT_EXT"/>
+ <enum bitpos="5" name="VK_IMAGE_COMPRESSION_FIXED_RATE_6BPC_BIT_EXT"/>
+ <enum bitpos="6" name="VK_IMAGE_COMPRESSION_FIXED_RATE_7BPC_BIT_EXT"/>
+ <enum bitpos="7" name="VK_IMAGE_COMPRESSION_FIXED_RATE_8BPC_BIT_EXT"/>
+ <enum bitpos="8" name="VK_IMAGE_COMPRESSION_FIXED_RATE_9BPC_BIT_EXT"/>
+ <enum bitpos="9" name="VK_IMAGE_COMPRESSION_FIXED_RATE_10BPC_BIT_EXT"/>
+ <enum bitpos="10" name="VK_IMAGE_COMPRESSION_FIXED_RATE_11BPC_BIT_EXT"/>
+ <enum bitpos="11" name="VK_IMAGE_COMPRESSION_FIXED_RATE_12BPC_BIT_EXT"/>
+ <enum bitpos="12" name="VK_IMAGE_COMPRESSION_FIXED_RATE_13BPC_BIT_EXT"/>
+ <enum bitpos="13" name="VK_IMAGE_COMPRESSION_FIXED_RATE_14BPC_BIT_EXT"/>
+ <enum bitpos="14" name="VK_IMAGE_COMPRESSION_FIXED_RATE_15BPC_BIT_EXT"/>
+ <enum bitpos="15" name="VK_IMAGE_COMPRESSION_FIXED_RATE_16BPC_BIT_EXT"/>
+ <enum bitpos="16" name="VK_IMAGE_COMPRESSION_FIXED_RATE_17BPC_BIT_EXT"/>
+ <enum bitpos="17" name="VK_IMAGE_COMPRESSION_FIXED_RATE_18BPC_BIT_EXT"/>
+ <enum bitpos="18" name="VK_IMAGE_COMPRESSION_FIXED_RATE_19BPC_BIT_EXT"/>
+ <enum bitpos="19" name="VK_IMAGE_COMPRESSION_FIXED_RATE_20BPC_BIT_EXT"/>
+ <enum bitpos="20" name="VK_IMAGE_COMPRESSION_FIXED_RATE_21BPC_BIT_EXT"/>
+ <enum bitpos="21" name="VK_IMAGE_COMPRESSION_FIXED_RATE_22BPC_BIT_EXT"/>
+ <enum bitpos="22" name="VK_IMAGE_COMPRESSION_FIXED_RATE_23BPC_BIT_EXT"/>
+ <enum bitpos="23" name="VK_IMAGE_COMPRESSION_FIXED_RATE_24BPC_BIT_EXT"/>
+ </enums>
+ <enums name="VkPipelineRobustnessBufferBehaviorEXT" type="enum">
+ <enum value="0" name="VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DEVICE_DEFAULT_EXT" />
+ <enum value="1" name="VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DISABLED_EXT" />
+ <enum value="2" name="VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_EXT" />
+ <enum value="3" name="VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_2_EXT" />
+ </enums>
+ <enums name="VkPipelineRobustnessImageBehaviorEXT" type="enum">
+ <enum value="0" name="VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_DEVICE_DEFAULT_EXT" />
+ <enum value="1" name="VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_DISABLED_EXT" />
+ <enum value="2" name="VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_ROBUST_IMAGE_ACCESS_EXT" />
+ <enum value="3" name="VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_ROBUST_IMAGE_ACCESS_2_EXT" />
+ </enums>
<commands comment="Vulkan command definitions">
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_INITIALIZATION_FAILED,VK_ERROR_LAYER_NOT_PRESENT,VK_ERROR_EXTENSION_NOT_PRESENT,VK_ERROR_INCOMPATIBLE_DRIVER">
<proto><type>VkResult</type> <name>vkCreateInstance</name></proto>
@@ -6627,7 +9049,7 @@ typedef void <name>CAMetalLayer</name>;
<param optional="true" externsync="true"><type>VkDevice</type> <name>device</name></param>
<param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
<implicitexternsyncparams>
- <param>all sname:VkQueue objects received from pname:device</param>
+ <param>all sname:VkQueue objects created from pname:device</param>
</implicitexternsyncparams>
</command>
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY">
@@ -6776,7 +9198,7 @@ typedef void <name>CAMetalLayer</name>;
<proto><type>VkResult</type> <name>vkQueueBindSparse</name></proto>
<param externsync="true"><type>VkQueue</type> <name>queue</name></param>
<param optional="true"><type>uint32_t</type> <name>bindInfoCount</name></param>
- <param len="bindInfoCount" externsync="pBindInfo[].pBufferBinds[].buffer,pBindInfo[].pImageOpaqueBinds[].image,pBindInfo[].pImageBinds[].image">const <type>VkBindSparseInfo</type>* <name>pBindInfo</name></param>
+ <param len="bindInfoCount">const <type>VkBindSparseInfo</type>* <name>pBindInfo</name></param>
<param optional="true" externsync="true"><type>VkFence</type> <name>fence</name></param>
</command>
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY">
@@ -6910,7 +9332,7 @@ typedef void <name>CAMetalLayer</name>;
<param optional="true" externsync="true"><type>VkBufferView</type> <name>bufferView</name></param>
<param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
</command>
- <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY">
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_COMPRESSION_EXHAUSTED_EXT">
<proto><type>VkResult</type> <name>vkCreateImage</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param>const <type>VkImageCreateInfo</type>* <name>pCreateInfo</name></param>
@@ -7001,6 +9423,12 @@ typedef void <name>CAMetalLayer</name>;
<param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
<param len="createInfoCount"><type>VkPipeline</type>* <name>pPipelines</name></param>
</command>
+ <command successcodes="VK_SUCCESS,VK_INCOMPLETE" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_SURFACE_LOST_KHR">
+ <proto><type>VkResult</type> <name>vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkRenderPass</type> <name>renderpass</name></param>
+ <param><type>VkExtent2D</type>* <name>pMaxWorkgroupSize</name></param>
+ </command>
<command>
<proto><type>void</type> <name>vkDestroyPipeline</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
@@ -7085,9 +9513,9 @@ typedef void <name>CAMetalLayer</name>;
<proto><type>void</type> <name>vkUpdateDescriptorSets</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param optional="true"><type>uint32_t</type> <name>descriptorWriteCount</name></param>
- <param len="descriptorWriteCount" externsync="pDescriptorWrites[].dstSet">const <type>VkWriteDescriptorSet</type>* <name>pDescriptorWrites</name></param>
+ <param len="descriptorWriteCount">const <type>VkWriteDescriptorSet</type>* <name>pDescriptorWrites</name></param>
<param optional="true"><type>uint32_t</type> <name>descriptorCopyCount</name></param>
- <param len="descriptorCopyCount" externsync="pDescriptorCopies[].dstSet">const <type>VkCopyDescriptorSet</type>* <name>pDescriptorCopies</name></param>
+ <param len="descriptorCopyCount">const <type>VkCopyDescriptorSet</type>* <name>pDescriptorCopies</name></param>
</command>
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY">
<proto><type>VkResult</type> <name>vkCreateFramebuffer</name></proto>
@@ -7172,6 +9600,9 @@ typedef void <name>CAMetalLayer</name>;
<proto><type>VkResult</type> <name>vkResetCommandBuffer</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param optional="true"><type>VkCommandBufferResetFlags</type> <name>flags</name></param>
+ <implicitexternsyncparams>
+ <param>the sname:VkCommandPool that pname:commandBuffer was allocated from</param>
+ </implicitexternsyncparams>
</command>
<command queues="graphics,compute" renderpass="both" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdBindPipeline</name></proto>
@@ -7241,7 +9672,7 @@ typedef void <name>CAMetalLayer</name>;
<param><type>VkPipelineLayout</type> <name>layout</name></param>
<param><type>uint32_t</type> <name>firstSet</name></param>
<param><type>uint32_t</type> <name>descriptorSetCount</name></param>
- <param len="descriptorSetCount">const <type>VkDescriptorSet</type>* <name>pDescriptorSets</name></param>
+ <param len="descriptorSetCount" optional="false,true">const <type>VkDescriptorSet</type>* <name>pDescriptorSets</name></param>
<param optional="true"><type>uint32_t</type> <name>dynamicOffsetCount</name></param>
<param len="dynamicOffsetCount">const <type>uint32_t</type>* <name>pDynamicOffsets</name></param>
</command>
@@ -7260,7 +9691,7 @@ typedef void <name>CAMetalLayer</name>;
<param len="bindingCount" optional="false,true">const <type>VkBuffer</type>* <name>pBuffers</name></param>
<param len="bindingCount">const <type>VkDeviceSize</type>* <name>pOffsets</name></param>
</command>
- <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary" pipeline="graphics">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdDraw</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>uint32_t</type> <name>vertexCount</name></param>
@@ -7268,7 +9699,7 @@ typedef void <name>CAMetalLayer</name>;
<param><type>uint32_t</type> <name>firstVertex</name></param>
<param><type>uint32_t</type> <name>firstInstance</name></param>
</command>
- <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary" pipeline="graphics">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdDrawIndexed</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>uint32_t</type> <name>indexCount</name></param>
@@ -7277,7 +9708,26 @@ typedef void <name>CAMetalLayer</name>;
<param><type>int32_t</type> <name>vertexOffset</name></param>
<param><type>uint32_t</type> <name>firstInstance</name></param>
</command>
- <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary" pipeline="graphics">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdDrawMultiEXT</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param optional="true"><type>uint32_t</type> <name>drawCount</name></param>
+ <param noautovalidity="true" len="drawCount">const <type>VkMultiDrawInfoEXT</type>* <name>pVertexInfo</name></param>
+ <param><type>uint32_t</type> <name>instanceCount</name></param>
+ <param><type>uint32_t</type> <name>firstInstance</name></param>
+ <param><type>uint32_t</type> <name>stride</name></param>
+ </command>
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdDrawMultiIndexedEXT</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param optional="true"><type>uint32_t</type> <name>drawCount</name></param>
+ <param noautovalidity="true" len="drawCount">const <type>VkMultiDrawIndexedInfoEXT</type>* <name>pIndexInfo</name></param>
+ <param><type>uint32_t</type> <name>instanceCount</name></param>
+ <param><type>uint32_t</type> <name>firstInstance</name></param>
+ <param><type>uint32_t</type> <name>stride</name></param>
+ <param optional="true">const <type>int32_t</type>* <name>pVertexOffset</name></param>
+ </command>
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdDrawIndirect</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkBuffer</type> <name>buffer</name></param>
@@ -7285,7 +9735,7 @@ typedef void <name>CAMetalLayer</name>;
<param><type>uint32_t</type> <name>drawCount</name></param>
<param><type>uint32_t</type> <name>stride</name></param>
</command>
- <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary" pipeline="graphics">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdDrawIndexedIndirect</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkBuffer</type> <name>buffer</name></param>
@@ -7293,20 +9743,24 @@ typedef void <name>CAMetalLayer</name>;
<param><type>uint32_t</type> <name>drawCount</name></param>
<param><type>uint32_t</type> <name>stride</name></param>
</command>
- <command queues="compute" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="compute">
+ <command queues="compute" renderpass="outside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdDispatch</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>uint32_t</type> <name>groupCountX</name></param>
<param><type>uint32_t</type> <name>groupCountY</name></param>
<param><type>uint32_t</type> <name>groupCountZ</name></param>
</command>
- <command queues="compute" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="compute">
+ <command queues="compute" renderpass="outside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdDispatchIndirect</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkBuffer</type> <name>buffer</name></param>
<param><type>VkDeviceSize</type> <name>offset</name></param>
</command>
- <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdSubpassShadingHUAWEI</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ </command>
+ <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdCopyBuffer</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkBuffer</type> <name>srcBuffer</name></param>
@@ -7314,7 +9768,7 @@ typedef void <name>CAMetalLayer</name>;
<param><type>uint32_t</type> <name>regionCount</name></param>
<param len="regionCount">const <type>VkBufferCopy</type>* <name>pRegions</name></param>
</command>
- <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
+ <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdCopyImage</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkImage</type> <name>srcImage</name></param>
@@ -7324,7 +9778,7 @@ typedef void <name>CAMetalLayer</name>;
<param><type>uint32_t</type> <name>regionCount</name></param>
<param len="regionCount">const <type>VkImageCopy</type>* <name>pRegions</name></param>
</command>
- <command queues="graphics" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
+ <command queues="graphics" renderpass="outside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdBlitImage</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkImage</type> <name>srcImage</name></param>
@@ -7335,7 +9789,7 @@ typedef void <name>CAMetalLayer</name>;
<param len="regionCount">const <type>VkImageBlit</type>* <name>pRegions</name></param>
<param><type>VkFilter</type> <name>filter</name></param>
</command>
- <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
+ <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdCopyBufferToImage</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkBuffer</type> <name>srcBuffer</name></param>
@@ -7344,7 +9798,7 @@ typedef void <name>CAMetalLayer</name>;
<param><type>uint32_t</type> <name>regionCount</name></param>
<param len="regionCount">const <type>VkBufferImageCopy</type>* <name>pRegions</name></param>
</command>
- <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
+ <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdCopyImageToBuffer</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkImage</type> <name>srcImage</name></param>
@@ -7353,7 +9807,7 @@ typedef void <name>CAMetalLayer</name>;
<param><type>uint32_t</type> <name>regionCount</name></param>
<param len="regionCount">const <type>VkBufferImageCopy</type>* <name>pRegions</name></param>
</command>
- <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
+ <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdUpdateBuffer</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkBuffer</type> <name>dstBuffer</name></param>
@@ -7361,7 +9815,7 @@ typedef void <name>CAMetalLayer</name>;
<param><type>VkDeviceSize</type> <name>dataSize</name></param>
<param len="dataSize">const <type>void</type>* <name>pData</name></param>
</command>
- <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer" comment="transfer support is only available when VK_KHR_maintenance1 is enabled, as documented in valid usage language in the specification">
+ <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary" comment="transfer support is only available when VK_KHR_maintenance1 is enabled, as documented in valid usage language in the specification">
<proto><type>void</type> <name>vkCmdFillBuffer</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkBuffer</type> <name>dstBuffer</name></param>
@@ -7369,16 +9823,16 @@ typedef void <name>CAMetalLayer</name>;
<param><type>VkDeviceSize</type> <name>size</name></param>
<param><type>uint32_t</type> <name>data</name></param>
</command>
- <command queues="graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
+ <command queues="graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdClearColorImage</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkImage</type> <name>image</name></param>
<param><type>VkImageLayout</type> <name>imageLayout</name></param>
- <param>const <type>VkClearColorValue</type>* <name>pColor</name></param>
+ <param noautovalidity="true">const <type>VkClearColorValue</type>* <name>pColor</name></param>
<param><type>uint32_t</type> <name>rangeCount</name></param>
<param len="rangeCount">const <type>VkImageSubresourceRange</type>* <name>pRanges</name></param>
</command>
- <command queues="graphics" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
+ <command queues="graphics" renderpass="outside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdClearDepthStencilImage</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkImage</type> <name>image</name></param>
@@ -7387,7 +9841,7 @@ typedef void <name>CAMetalLayer</name>;
<param><type>uint32_t</type> <name>rangeCount</name></param>
<param len="rangeCount">const <type>VkImageSubresourceRange</type>* <name>pRanges</name></param>
</command>
- <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary" pipeline="graphics">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdClearAttachments</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>uint32_t</type> <name>attachmentCount</name></param>
@@ -7395,7 +9849,7 @@ typedef void <name>CAMetalLayer</name>;
<param><type>uint32_t</type> <name>rectCount</name></param>
<param len="rectCount">const <type>VkClearRect</type>* <name>pRects</name></param>
</command>
- <command queues="graphics" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
+ <command queues="graphics" renderpass="outside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdResolveImage</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkImage</type> <name>srcImage</name></param>
@@ -7405,25 +9859,25 @@ typedef void <name>CAMetalLayer</name>;
<param><type>uint32_t</type> <name>regionCount</name></param>
<param len="regionCount">const <type>VkImageResolve</type>* <name>pRegions</name></param>
</command>
- <command queues="graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary">
+ <command queues="graphics,compute" renderpass="outside" videocoding="both" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdSetEvent</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkEvent</type> <name>event</name></param>
- <param><type>VkPipelineStageFlags</type> <name>stageMask</name></param>
+ <param optional="true"><type>VkPipelineStageFlags</type> <name>stageMask</name></param>
</command>
- <command queues="graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary">
+ <command queues="graphics,compute" renderpass="outside" videocoding="both" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdResetEvent</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkEvent</type> <name>event</name></param>
- <param><type>VkPipelineStageFlags</type> <name>stageMask</name></param>
+ <param optional="true"><type>VkPipelineStageFlags</type> <name>stageMask</name></param>
</command>
- <command queues="graphics,compute" renderpass="both" cmdbufferlevel="primary,secondary">
+ <command queues="graphics,compute" renderpass="both" videocoding="both" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdWaitEvents</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>uint32_t</type> <name>eventCount</name></param>
<param len="eventCount">const <type>VkEvent</type>* <name>pEvents</name></param>
- <param><type>VkPipelineStageFlags</type> <name>srcStageMask</name></param>
- <param><type>VkPipelineStageFlags</type> <name>dstStageMask</name></param>
+ <param optional="true"><type>VkPipelineStageFlags</type> <name>srcStageMask</name></param>
+ <param optional="true"><type>VkPipelineStageFlags</type> <name>dstStageMask</name></param>
<param optional="true"><type>uint32_t</type> <name>memoryBarrierCount</name></param>
<param len="memoryBarrierCount">const <type>VkMemoryBarrier</type>* <name>pMemoryBarriers</name></param>
<param optional="true"><type>uint32_t</type> <name>bufferMemoryBarrierCount</name></param>
@@ -7431,11 +9885,11 @@ typedef void <name>CAMetalLayer</name>;
<param optional="true"><type>uint32_t</type> <name>imageMemoryBarrierCount</name></param>
<param len="imageMemoryBarrierCount">const <type>VkImageMemoryBarrier</type>* <name>pImageMemoryBarriers</name></param>
</command>
- <command queues="transfer,graphics,compute" renderpass="both" cmdbufferlevel="primary,secondary">
+ <command queues="transfer,graphics,compute" renderpass="both" videocoding="both" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdPipelineBarrier</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
- <param><type>VkPipelineStageFlags</type> <name>srcStageMask</name></param>
- <param><type>VkPipelineStageFlags</type> <name>dstStageMask</name></param>
+ <param optional="true"><type>VkPipelineStageFlags</type> <name>srcStageMask</name></param>
+ <param optional="true"><type>VkPipelineStageFlags</type> <name>dstStageMask</name></param>
<param optional="true"><type>VkDependencyFlags</type> <name>dependencyFlags</name></param>
<param optional="true"><type>uint32_t</type> <name>memoryBarrierCount</name></param>
<param len="memoryBarrierCount">const <type>VkMemoryBarrier</type>* <name>pMemoryBarriers</name></param>
@@ -7444,14 +9898,14 @@ typedef void <name>CAMetalLayer</name>;
<param optional="true"><type>uint32_t</type> <name>imageMemoryBarrierCount</name></param>
<param len="imageMemoryBarrierCount">const <type>VkImageMemoryBarrier</type>* <name>pImageMemoryBarriers</name></param>
</command>
- <command queues="graphics,compute" renderpass="both" cmdbufferlevel="primary,secondary">
+ <command queues="graphics,compute,decode,encode" renderpass="both" videocoding="both" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdBeginQuery</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkQueryPool</type> <name>queryPool</name></param>
<param><type>uint32_t</type> <name>query</name></param>
<param optional="true"><type>VkQueryControlFlags</type> <name>flags</name></param>
</command>
- <command queues="graphics,compute" renderpass="both" cmdbufferlevel="primary,secondary">
+ <command queues="graphics,compute,decode,encode" renderpass="both" videocoding="both" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdEndQuery</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkQueryPool</type> <name>queryPool</name></param>
@@ -7466,21 +9920,21 @@ typedef void <name>CAMetalLayer</name>;
<proto><type>void</type> <name>vkCmdEndConditionalRenderingEXT</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
</command>
- <command queues="graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary">
+ <command queues="graphics,compute,decode,encode" renderpass="outside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdResetQueryPool</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkQueryPool</type> <name>queryPool</name></param>
<param><type>uint32_t</type> <name>firstQuery</name></param>
<param><type>uint32_t</type> <name>queryCount</name></param>
</command>
- <command queues="transfer,graphics,compute" renderpass="both" cmdbufferlevel="primary,secondary" pipeline="transfer">
+ <command queues="transfer,graphics,compute,decode,encode" renderpass="both" videocoding="both" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdWriteTimestamp</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkPipelineStageFlagBits</type> <name>pipelineStage</name></param>
<param><type>VkQueryPool</type> <name>queryPool</name></param>
<param><type>uint32_t</type> <name>query</name></param>
</command>
- <command queues="graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
+ <command queues="graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdCopyQueryPoolResults</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkQueryPool</type> <name>queryPool</name></param>
@@ -7500,18 +9954,18 @@ typedef void <name>CAMetalLayer</name>;
<param><type>uint32_t</type> <name>size</name></param>
<param len="size">const <type>void</type>* <name>pValues</name></param>
</command>
- <command queues="graphics" renderpass="outside" cmdbufferlevel="primary" pipeline="graphics">
+ <command queues="graphics" renderpass="outside" cmdbufferlevel="primary">
<proto><type>void</type> <name>vkCmdBeginRenderPass</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param>const <type>VkRenderPassBeginInfo</type>* <name>pRenderPassBegin</name></param>
<param><type>VkSubpassContents</type> <name>contents</name></param>
</command>
- <command queues="graphics" renderpass="inside" cmdbufferlevel="primary" pipeline="graphics">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary">
<proto><type>void</type> <name>vkCmdNextSubpass</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkSubpassContents</type> <name>contents</name></param>
</command>
- <command queues="graphics" renderpass="inside" cmdbufferlevel="primary" pipeline="graphics">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary">
<proto><type>void</type> <name>vkCmdEndRenderPass</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
</command>
@@ -7606,18 +10060,18 @@ typedef void <name>CAMetalLayer</name>;
<command successcodes="VK_SUCCESS,VK_INCOMPLETE" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_SURFACE_LOST_KHR">
<proto><type>VkResult</type> <name>vkGetPhysicalDeviceSurfaceFormatsKHR</name></proto>
<param><type>VkPhysicalDevice</type> <name>physicalDevice</name></param>
- <param><type>VkSurfaceKHR</type> <name>surface</name></param>
+ <param optional="true"><type>VkSurfaceKHR</type> <name>surface</name></param>
<param optional="false,true"><type>uint32_t</type>* <name>pSurfaceFormatCount</name></param>
<param optional="true" len="pSurfaceFormatCount"><type>VkSurfaceFormatKHR</type>* <name>pSurfaceFormats</name></param>
</command>
<command successcodes="VK_SUCCESS,VK_INCOMPLETE" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_SURFACE_LOST_KHR">
<proto><type>VkResult</type> <name>vkGetPhysicalDeviceSurfacePresentModesKHR</name></proto>
<param><type>VkPhysicalDevice</type> <name>physicalDevice</name></param>
- <param><type>VkSurfaceKHR</type> <name>surface</name></param>
+ <param optional="true"><type>VkSurfaceKHR</type> <name>surface</name></param>
<param optional="false,true"><type>uint32_t</type>* <name>pPresentModeCount</name></param>
<param optional="true" len="pPresentModeCount"><type>VkPresentModeKHR</type>* <name>pPresentModes</name></param>
</command>
- <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_DEVICE_LOST,VK_ERROR_SURFACE_LOST_KHR,VK_ERROR_NATIVE_WINDOW_IN_USE_KHR,VK_ERROR_INITIALIZATION_FAILED">
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_DEVICE_LOST,VK_ERROR_SURFACE_LOST_KHR,VK_ERROR_NATIVE_WINDOW_IN_USE_KHR,VK_ERROR_INITIALIZATION_FAILED,VK_ERROR_COMPRESSION_EXHAUSTED_EXT">
<proto><type>VkResult</type> <name>vkCreateSwapchainKHR</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param externsync="pCreateInfo-&gt;surface,pCreateInfo-&gt;oldSwapchain">const <type>VkSwapchainCreateInfoKHR</type>* <name>pCreateInfo</name></param>
@@ -7738,6 +10192,19 @@ typedef void <name>CAMetalLayer</name>;
<param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
<param><type>VkSurfaceKHR</type>* <name>pSurface</name></param>
</command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY">
+ <proto><type>VkResult</type> <name>vkCreateScreenSurfaceQNX</name></proto>
+ <param><type>VkInstance</type> <name>instance</name></param>
+ <param>const <type>VkScreenSurfaceCreateInfoQNX</type>* <name>pCreateInfo</name></param>
+ <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
+ <param><type>VkSurfaceKHR</type>* <name>pSurface</name></param>
+ </command>
+ <command>
+ <proto><type>VkBool32</type> <name>vkGetPhysicalDeviceScreenPresentationSupportQNX</name></proto>
+ <param><type>VkPhysicalDevice</type> <name>physicalDevice</name></param>
+ <param><type>uint32_t</type> <name>queueFamilyIndex</name></param>
+ <param>struct <type>_screen_window</type>* <name>window</name></param>
+ </command>
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY">
<proto><type>VkResult</type> <name>vkCreateDebugReportCallbackEXT</name></proto>
<param><type>VkInstance</type> <name>instance</name></param>
@@ -7756,7 +10223,7 @@ typedef void <name>CAMetalLayer</name>;
<param><type>VkInstance</type> <name>instance</name></param>
<param><type>VkDebugReportFlagsEXT</type> <name>flags</name></param>
<param><type>VkDebugReportObjectTypeEXT</type> <name>objectType</name></param>
- <param><type>uint64_t</type> <name>object</name></param>
+ <param objecttype="objectType"><type>uint64_t</type> <name>object</name></param>
<param><type>size_t</type> <name>location</name></param>
<param><type>int32_t</type> <name>messageCode</name></param>
<param len="null-terminated">const <type>char</type>* <name>pLayerPrefix</name></param>
@@ -7860,7 +10327,7 @@ typedef void <name>CAMetalLayer</name>;
<param><type>VkFormatProperties2</type>* <name>pFormatProperties</name></param>
</command>
<command name="vkGetPhysicalDeviceFormatProperties2KHR" alias="vkGetPhysicalDeviceFormatProperties2"/>
- <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_FORMAT_NOT_SUPPORTED">
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_FORMAT_NOT_SUPPORTED,VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR,VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR,VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR,VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR,VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR">
<proto><type>VkResult</type> <name>vkGetPhysicalDeviceImageFormatProperties2</name></proto>
<param><type>VkPhysicalDevice</type> <name>physicalDevice</name></param>
<param>const <type>VkPhysicalDeviceImageFormatInfo2</type>* <name>pImageFormatInfo</name></param>
@@ -7937,6 +10404,25 @@ typedef void <name>CAMetalLayer</name>;
<param><type>int</type> <name>fd</name></param>
<param><type>VkMemoryFdPropertiesKHR</type>* <name>pMemoryFdProperties</name></param>
</command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_TOO_MANY_OBJECTS,VK_ERROR_OUT_OF_HOST_MEMORY">
+ <proto><type>VkResult</type> <name>vkGetMemoryZirconHandleFUCHSIA</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkMemoryGetZirconHandleInfoFUCHSIA</type>* <name>pGetZirconHandleInfo</name></param>
+ <param><type>zx_handle_t</type>* <name>pZirconHandle</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_INVALID_EXTERNAL_HANDLE">
+ <proto><type>VkResult</type> <name>vkGetMemoryZirconHandlePropertiesFUCHSIA</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkExternalMemoryHandleTypeFlagBits</type> <name>handleType</name></param>
+ <param><type>zx_handle_t</type> <name>zirconHandle</name></param>
+ <param><type>VkMemoryZirconHandlePropertiesFUCHSIA</type>* <name>pMemoryZirconHandleProperties</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_INVALID_EXTERNAL_HANDLE">
+ <proto><type>VkResult</type> <name>vkGetMemoryRemoteAddressNV</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkMemoryGetRemoteAddressInfoNV</type>* <name>pMemoryGetRemoteAddressInfo</name></param>
+ <param><type>VkRemoteAddressNV</type>* <name>pAddress</name></param>
+ </command>
<command>
<proto><type>void</type> <name>vkGetPhysicalDeviceExternalSemaphoreProperties</name></proto>
<param><type>VkPhysicalDevice</type> <name>physicalDevice</name></param>
@@ -7966,6 +10452,17 @@ typedef void <name>CAMetalLayer</name>;
<param><type>VkDevice</type> <name>device</name></param>
<param>const <type>VkImportSemaphoreFdInfoKHR</type>* <name>pImportSemaphoreFdInfo</name></param>
</command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_TOO_MANY_OBJECTS,VK_ERROR_OUT_OF_HOST_MEMORY">
+ <proto><type>VkResult</type> <name>vkGetSemaphoreZirconHandleFUCHSIA</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkSemaphoreGetZirconHandleInfoFUCHSIA</type>* <name>pGetZirconHandleInfo</name></param>
+ <param><type>zx_handle_t</type>* <name>pZirconHandle</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_INVALID_EXTERNAL_HANDLE">
+ <proto><type>VkResult</type> <name>vkImportSemaphoreZirconHandleFUCHSIA</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkImportSemaphoreZirconHandleInfoFUCHSIA</type>* <name>pImportSemaphoreZirconHandleInfo</name></param>
+ </command>
<command>
<proto><type>void</type> <name>vkGetPhysicalDeviceExternalFenceProperties</name></proto>
<param><type>VkPhysicalDevice</type> <name>physicalDevice</name></param>
@@ -8088,7 +10585,7 @@ typedef void <name>CAMetalLayer</name>;
<param len="bindInfoCount">const <type>VkBindImageMemoryInfo</type>* <name>pBindInfos</name></param>
</command>
<command name="vkBindImageMemory2KHR" alias="vkBindImageMemory2"/>
- <command queues="graphics,compute,transfer" renderpass="both" cmdbufferlevel="primary,secondary">
+ <command queues="graphics,compute,transfer" renderpass="both" videocoding="both" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdSetDeviceMask</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>uint32_t</type> <name>deviceMask</name></param>
@@ -8147,7 +10644,7 @@ typedef void <name>CAMetalLayer</name>;
<command>
<proto><type>void</type> <name>vkUpdateDescriptorSetWithTemplate</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
- <param externsync="true"><type>VkDescriptorSet</type> <name>descriptorSet</name></param>
+ <param><type>VkDescriptorSet</type> <name>descriptorSet</name></param>
<param><type>VkDescriptorUpdateTemplate</type> <name>descriptorUpdateTemplate</name></param>
<param noautovalidity="true">const <type>void</type>* <name>pData</name></param>
</command>
@@ -8291,6 +10788,28 @@ typedef void <name>CAMetalLayer</name>;
<param optional="true" len="pSparseMemoryRequirementCount"><type>VkSparseImageMemoryRequirements2</type>* <name>pSparseMemoryRequirements</name></param>
</command>
<command name="vkGetImageSparseMemoryRequirements2KHR" alias="vkGetImageSparseMemoryRequirements2"/>
+ <command>
+ <proto><type>void</type> <name>vkGetDeviceBufferMemoryRequirements</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkDeviceBufferMemoryRequirements</type>* <name>pInfo</name></param>
+ <param><type>VkMemoryRequirements2</type>* <name>pMemoryRequirements</name></param>
+ </command>
+ <command name="vkGetDeviceBufferMemoryRequirementsKHR" alias="vkGetDeviceBufferMemoryRequirements"/>
+ <command>
+ <proto><type>void</type> <name>vkGetDeviceImageMemoryRequirements</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkDeviceImageMemoryRequirements</type>* <name>pInfo</name></param>
+ <param><type>VkMemoryRequirements2</type>* <name>pMemoryRequirements</name></param>
+ </command>
+ <command name="vkGetDeviceImageMemoryRequirementsKHR" alias="vkGetDeviceImageMemoryRequirements"/>
+ <command>
+ <proto><type>void</type> <name>vkGetDeviceImageSparseMemoryRequirements</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkDeviceImageMemoryRequirements</type>* <name>pInfo</name></param>
+ <param optional="false,true"><type>uint32_t</type>* <name>pSparseMemoryRequirementCount</name></param>
+ <param optional="true" len="pSparseMemoryRequirementCount"><type>VkSparseImageMemoryRequirements2</type>* <name>pSparseMemoryRequirements</name></param>
+ </command>
+ <command name="vkGetDeviceImageSparseMemoryRequirementsKHR" alias="vkGetDeviceImageSparseMemoryRequirements"/>
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY">
<proto><type>VkResult</type> <name>vkCreateSamplerYcbcrConversion</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
@@ -8472,10 +10991,10 @@ typedef void <name>CAMetalLayer</name>;
<param optional="false">const <type>void</type>* <name>pHostPointer</name></param>
<param><type>VkMemoryHostPointerPropertiesEXT</type>* <name>pMemoryHostPointerProperties</name></param>
</command>
- <command queues="transfer,graphics,compute" renderpass="both" cmdbufferlevel="primary,secondary" pipeline="transfer">
+ <command queues="transfer,graphics,compute" renderpass="both" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdWriteBufferMarkerAMD</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
- <param><type>VkPipelineStageFlagBits</type> <name>pipelineStage</name></param>
+ <param optional="true"><type>VkPipelineStageFlagBits</type> <name>pipelineStage</name></param>
<param><type>VkBuffer</type> <name>dstBuffer</name></param>
<param><type>VkDeviceSize</type> <name>dstOffset</name></param>
<param><type>uint32_t</type> <name>marker</name></param>
@@ -8488,21 +11007,21 @@ typedef void <name>CAMetalLayer</name>;
<param><type>VkRenderPass</type>* <name>pRenderPass</name></param>
</command>
<command name="vkCreateRenderPass2KHR" alias="vkCreateRenderPass2"/>
- <command queues="graphics" renderpass="outside" cmdbufferlevel="primary" pipeline="graphics">
+ <command queues="graphics" renderpass="outside" cmdbufferlevel="primary">
<proto><type>void</type> <name>vkCmdBeginRenderPass2</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param>const <type>VkRenderPassBeginInfo</type>* <name>pRenderPassBegin</name></param>
<param>const <type>VkSubpassBeginInfo</type>* <name>pSubpassBeginInfo</name></param>
</command>
<command name="vkCmdBeginRenderPass2KHR" alias="vkCmdBeginRenderPass2"/>
- <command queues="graphics" renderpass="inside" cmdbufferlevel="primary" pipeline="graphics">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary">
<proto><type>void</type> <name>vkCmdNextSubpass2</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param>const <type>VkSubpassBeginInfo</type>* <name>pSubpassBeginInfo</name></param>
<param>const <type>VkSubpassEndInfo</type>* <name>pSubpassEndInfo</name></param>
</command>
<command name="vkCmdNextSubpass2KHR" alias="vkCmdNextSubpass2"/>
- <command queues="graphics" renderpass="inside" cmdbufferlevel="primary" pipeline="graphics">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary">
<proto><type>void</type> <name>vkCmdEndRenderPass2</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param>const <type>VkSubpassEndInfo</type>* <name>pSubpassEndInfo</name></param>
@@ -8540,7 +11059,7 @@ typedef void <name>CAMetalLayer</name>;
<param>const <type>VkMemoryGetAndroidHardwareBufferInfoANDROID</type>* <name>pInfo</name></param>
<param>struct <type>AHardwareBuffer</type>** <name>pBuffer</name></param>
</command>
- <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary" pipeline="graphics">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdDrawIndirectCount</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkBuffer</type> <name>buffer</name></param>
@@ -8552,7 +11071,7 @@ typedef void <name>CAMetalLayer</name>;
</command>
<command name="vkCmdDrawIndirectCountKHR" alias="vkCmdDrawIndirectCount"/>
<command name="vkCmdDrawIndirectCountAMD" alias="vkCmdDrawIndirectCount"/>
- <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary" pipeline="graphics">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdDrawIndexedIndirectCount</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkBuffer</type> <name>buffer</name></param>
@@ -8615,7 +11134,7 @@ typedef void <name>CAMetalLayer</name>;
<param><type>uint32_t</type> <name>query</name></param>
<param><type>uint32_t</type> <name>index</name></param>
</command>
- <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary" pipeline="graphics">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdDrawIndirectByteCountEXT</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>uint32_t</type> <name>instanceCount</name></param>
@@ -8652,13 +11171,13 @@ typedef void <name>CAMetalLayer</name>;
<param optional="true"><type>uint32_t</type> <name>customSampleOrderCount</name></param>
<param len="customSampleOrderCount">const <type>VkCoarseSampleOrderCustomNV</type>* <name>pCustomSampleOrders</name></param>
</command>
- <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary" pipeline="graphics">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdDrawMeshTasksNV</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>uint32_t</type> <name>taskCount</name></param>
<param><type>uint32_t</type> <name>firstTask</name></param>
</command>
- <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary" pipeline="graphics">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdDrawMeshTasksIndirectNV</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkBuffer</type> <name>buffer</name></param>
@@ -8666,7 +11185,7 @@ typedef void <name>CAMetalLayer</name>;
<param><type>uint32_t</type> <name>drawCount</name></param>
<param><type>uint32_t</type> <name>stride</name></param>
</command>
- <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary" pipeline="graphics">
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdDrawMeshTasksIndirectCountNV</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkBuffer</type> <name>buffer</name></param>
@@ -8689,6 +11208,12 @@ typedef void <name>CAMetalLayer</name>;
<param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
<param><type>VkAccelerationStructureNV</type>* <name>pAccelerationStructure</name></param>
</command>
+ <command queues="compute" renderpass="outside" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdBindInvocationMaskHUAWEI</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param optional="true"><type>VkImageView</type> <name>imageView</name></param>
+ <param><type>VkImageLayout</type> <name>imageLayout</name></param>
+ </command>
<command>
<proto><type>void</type> <name>vkDestroyAccelerationStructureKHR</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
@@ -8882,6 +11407,11 @@ typedef void <name>CAMetalLayer</name>;
<param>const <type>VkStridedDeviceAddressRegionKHR</type>* <name>pCallableShaderBindingTable</name></param>
<param><type>VkDeviceAddress</type> <name>indirectDeviceAddress</name></param>
</command>
+ <command queues="compute" renderpass="outside" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdTraceRaysIndirect2KHR</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param><type>VkDeviceAddress</type> <name>indirectDeviceAddress</name></param>
+ </command>
<command>
<proto><type>void</type> <name>vkGetDeviceAccelerationStructureCompatibilityKHR</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
@@ -9069,11 +11599,12 @@ typedef void <name>CAMetalLayer</name>;
<param><type>uint16_t</type> <name>lineStipplePattern</name></param>
</command>
<command successcodes="VK_SUCCESS,VK_INCOMPLETE" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY">
- <proto><type>VkResult</type> <name>vkGetPhysicalDeviceToolPropertiesEXT</name></proto>
+ <proto><type>VkResult</type> <name>vkGetPhysicalDeviceToolProperties</name></proto>
<param><type>VkPhysicalDevice</type> <name>physicalDevice</name></param>
<param optional="false,true"><type>uint32_t</type>* <name>pToolCount</name></param>
- <param optional="true" len="pToolCount"><type>VkPhysicalDeviceToolPropertiesEXT</type>* <name>pToolProperties</name></param>
+ <param optional="true" len="pToolCount"><type>VkPhysicalDeviceToolProperties</type>* <name>pToolProperties</name></param>
</command>
+ <command name="vkGetPhysicalDeviceToolPropertiesEXT" alias="vkGetPhysicalDeviceToolProperties"/>
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR">
<proto><type>VkResult</type> <name>vkCreateAccelerationStructureKHR</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
@@ -9123,84 +11654,95 @@ typedef void <name>CAMetalLayer</name>;
<param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
</command>
<command>
- <proto><type>uint32_t</type> <name>vkGetDeferredOperationMaxConcurrencyKHR</name></proto>
- <param><type>VkDevice</type> <name>device</name></param>
- <param><type>VkDeferredOperationKHR</type> <name>operation</name></param>
+ <proto><type>uint32_t</type> <name>vkGetDeferredOperationMaxConcurrencyKHR</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkDeferredOperationKHR</type> <name>operation</name></param>
</command>
<command successcodes="VK_SUCCESS,VK_NOT_READY">
- <proto><type>VkResult</type> <name>vkGetDeferredOperationResultKHR</name></proto>
- <param><type>VkDevice</type> <name>device</name></param>
- <param><type>VkDeferredOperationKHR</type> <name>operation</name></param>
+ <proto><type>VkResult</type> <name>vkGetDeferredOperationResultKHR</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkDeferredOperationKHR</type> <name>operation</name></param>
</command>
<command successcodes="VK_SUCCESS,VK_THREAD_DONE_KHR,VK_THREAD_IDLE_KHR" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY">
- <proto><type>VkResult</type> <name>vkDeferredOperationJoinKHR</name></proto>
- <param><type>VkDevice</type> <name>device</name></param>
- <param><type>VkDeferredOperationKHR</type> <name>operation</name></param>
+ <proto><type>VkResult</type> <name>vkDeferredOperationJoinKHR</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkDeferredOperationKHR</type> <name>operation</name></param>
</command>
<command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
- <proto><type>void</type> <name>vkCmdSetCullModeEXT</name></proto>
+ <proto><type>void</type> <name>vkCmdSetCullMode</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param optional="true"><type>VkCullModeFlags</type> <name>cullMode</name></param>
</command>
+ <command name="vkCmdSetCullModeEXT" alias="vkCmdSetCullMode"/>
<command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
- <proto><type>void</type> <name>vkCmdSetFrontFaceEXT</name></proto>
+ <proto><type>void</type> <name>vkCmdSetFrontFace</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkFrontFace</type> <name>frontFace</name></param>
</command>
+ <command name="vkCmdSetFrontFaceEXT" alias="vkCmdSetFrontFace"/>
<command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
- <proto><type>void</type> <name>vkCmdSetPrimitiveTopologyEXT</name></proto>
+ <proto><type>void</type> <name>vkCmdSetPrimitiveTopology</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkPrimitiveTopology</type> <name>primitiveTopology</name></param>
</command>
+ <command name="vkCmdSetPrimitiveTopologyEXT" alias="vkCmdSetPrimitiveTopology"/>
<command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
- <proto><type>void</type> <name>vkCmdSetViewportWithCountEXT</name></proto>
+ <proto><type>void</type> <name>vkCmdSetViewportWithCount</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>uint32_t</type> <name>viewportCount</name></param>
<param len="viewportCount">const <type>VkViewport</type>* <name>pViewports</name></param>
</command>
+ <command name="vkCmdSetViewportWithCountEXT" alias="vkCmdSetViewportWithCount"/>
<command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
- <proto><type>void</type> <name>vkCmdSetScissorWithCountEXT</name></proto>
+ <proto><type>void</type> <name>vkCmdSetScissorWithCount</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>uint32_t</type> <name>scissorCount</name></param>
<param len="scissorCount">const <type>VkRect2D</type>* <name>pScissors</name></param>
</command>
+ <command name="vkCmdSetScissorWithCountEXT" alias="vkCmdSetScissorWithCount"/>
<command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
- <proto><type>void</type> <name>vkCmdBindVertexBuffers2EXT</name></proto>
+ <proto><type>void</type> <name>vkCmdBindVertexBuffers2</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>uint32_t</type> <name>firstBinding</name></param>
<param><type>uint32_t</type> <name>bindingCount</name></param>
- <param len="bindingCount">const <type>VkBuffer</type>* <name>pBuffers</name></param>
+ <param len="bindingCount" optional="false,true">const <type>VkBuffer</type>* <name>pBuffers</name></param>
<param len="bindingCount">const <type>VkDeviceSize</type>* <name>pOffsets</name></param>
<param optional="true" len="bindingCount">const <type>VkDeviceSize</type>* <name>pSizes</name></param>
<param optional="true" len="bindingCount">const <type>VkDeviceSize</type>* <name>pStrides</name></param>
</command>
+ <command name="vkCmdBindVertexBuffers2EXT" alias="vkCmdBindVertexBuffers2"/>
<command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
- <proto><type>void</type> <name>vkCmdSetDepthTestEnableEXT</name></proto>
+ <proto><type>void</type> <name>vkCmdSetDepthTestEnable</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkBool32</type> <name>depthTestEnable</name></param>
</command>
+ <command name="vkCmdSetDepthTestEnableEXT" alias="vkCmdSetDepthTestEnable"/>
<command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
- <proto><type>void</type> <name>vkCmdSetDepthWriteEnableEXT</name></proto>
+ <proto><type>void</type> <name>vkCmdSetDepthWriteEnable</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkBool32</type> <name>depthWriteEnable</name></param>
</command>
+ <command name="vkCmdSetDepthWriteEnableEXT" alias="vkCmdSetDepthWriteEnable"/>
<command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
- <proto><type>void</type> <name>vkCmdSetDepthCompareOpEXT</name></proto>
+ <proto><type>void</type> <name>vkCmdSetDepthCompareOp</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkCompareOp</type> <name>depthCompareOp</name></param>
</command>
+ <command name="vkCmdSetDepthCompareOpEXT" alias="vkCmdSetDepthCompareOp"/>
<command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
- <proto><type>void</type> <name>vkCmdSetDepthBoundsTestEnableEXT</name></proto>
+ <proto><type>void</type> <name>vkCmdSetDepthBoundsTestEnable</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkBool32</type> <name>depthBoundsTestEnable</name></param>
</command>
+ <command name="vkCmdSetDepthBoundsTestEnableEXT" alias="vkCmdSetDepthBoundsTestEnable"/>
<command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
- <proto><type>void</type> <name>vkCmdSetStencilTestEnableEXT</name></proto>
+ <proto><type>void</type> <name>vkCmdSetStencilTestEnable</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkBool32</type> <name>stencilTestEnable</name></param>
</command>
+ <command name="vkCmdSetStencilTestEnableEXT" alias="vkCmdSetStencilTestEnable"/>
<command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
- <proto><type>void</type> <name>vkCmdSetStencilOpEXT</name></proto>
+ <proto><type>void</type> <name>vkCmdSetStencilOp</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkStencilFaceFlags</type> <name>faceMask</name></param>
<param><type>VkStencilOp</type> <name>failOp</name></param>
@@ -9208,65 +11750,104 @@ typedef void <name>CAMetalLayer</name>;
<param><type>VkStencilOp</type> <name>depthFailOp</name></param>
<param><type>VkCompareOp</type> <name>compareOp</name></param>
</command>
+ <command name="vkCmdSetStencilOpEXT" alias="vkCmdSetStencilOp"/>
+ <command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdSetPatchControlPointsEXT</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param><type>uint32_t</type> <name>patchControlPoints</name></param>
+ </command>
+ <command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdSetRasterizerDiscardEnable</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param><type>VkBool32</type> <name>rasterizerDiscardEnable</name></param>
+ </command>
+ <command name="vkCmdSetRasterizerDiscardEnableEXT" alias="vkCmdSetRasterizerDiscardEnable"/>
+ <command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdSetDepthBiasEnable</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param><type>VkBool32</type> <name>depthBiasEnable</name></param>
+ </command>
+ <command name="vkCmdSetDepthBiasEnableEXT" alias="vkCmdSetDepthBiasEnable"/>
+ <command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdSetLogicOpEXT</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param><type>VkLogicOp</type> <name>logicOp</name></param>
+ </command>
+ <command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdSetPrimitiveRestartEnable</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param><type>VkBool32</type> <name>primitiveRestartEnable</name></param>
+ </command>
+ <command name="vkCmdSetPrimitiveRestartEnableEXT" alias="vkCmdSetPrimitiveRestartEnable"/>
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY">
- <proto><type>VkResult</type> <name>vkCreatePrivateDataSlotEXT</name></proto>
+ <proto><type>VkResult</type> <name>vkCreatePrivateDataSlot</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
- <param>const <type>VkPrivateDataSlotCreateInfoEXT</type>* <name>pCreateInfo</name></param>
+ <param>const <type>VkPrivateDataSlotCreateInfo</type>* <name>pCreateInfo</name></param>
<param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
- <param><type>VkPrivateDataSlotEXT</type>* <name>pPrivateDataSlot</name></param>
+ <param><type>VkPrivateDataSlot</type>* <name>pPrivateDataSlot</name></param>
</command>
+ <command name="vkCreatePrivateDataSlotEXT" alias="vkCreatePrivateDataSlot"/>
<command>
- <proto><type>void</type> <name>vkDestroyPrivateDataSlotEXT</name></proto>
+ <proto><type>void</type> <name>vkDestroyPrivateDataSlot</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
- <param optional="true" externsync="true"><type>VkPrivateDataSlotEXT</type> <name>privateDataSlot</name></param>
+ <param optional="true" externsync="true"><type>VkPrivateDataSlot</type> <name>privateDataSlot</name></param>
<param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
</command>
+ <command name="vkDestroyPrivateDataSlotEXT" alias="vkDestroyPrivateDataSlot"/>
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY">
- <proto><type>VkResult</type> <name>vkSetPrivateDataEXT</name></proto>
+ <proto><type>VkResult</type> <name>vkSetPrivateData</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param><type>VkObjectType</type> <name>objectType</name></param>
- <param><type>uint64_t</type> <name>objectHandle</name></param>
- <param><type>VkPrivateDataSlotEXT</type> <name>privateDataSlot</name></param>
+ <param objecttype="objectType"><type>uint64_t</type> <name>objectHandle</name></param>
+ <param><type>VkPrivateDataSlot</type> <name>privateDataSlot</name></param>
<param><type>uint64_t</type> <name>data</name></param>
</command>
+ <command name="vkSetPrivateDataEXT" alias="vkSetPrivateData"/>
<command>
- <proto><type>void</type> <name>vkGetPrivateDataEXT</name></proto>
+ <proto><type>void</type> <name>vkGetPrivateData</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param><type>VkObjectType</type> <name>objectType</name></param>
- <param><type>uint64_t</type> <name>objectHandle</name></param>
- <param><type>VkPrivateDataSlotEXT</type> <name>privateDataSlot</name></param>
+ <param objecttype="objectType"><type>uint64_t</type> <name>objectHandle</name></param>
+ <param><type>VkPrivateDataSlot</type> <name>privateDataSlot</name></param>
<param><type>uint64_t</type>* <name>pData</name></param>
</command>
- <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
- <proto><type>void</type> <name>vkCmdCopyBuffer2KHR</name></proto>
+ <command name="vkGetPrivateDataEXT" alias="vkGetPrivateData"/>
+ <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdCopyBuffer2</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
- <param>const <type>VkCopyBufferInfo2KHR</type>* <name>pCopyBufferInfo</name></param>
+ <param>const <type>VkCopyBufferInfo2</type>* <name>pCopyBufferInfo</name></param>
</command>
- <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
- <proto><type>void</type> <name>vkCmdCopyImage2KHR</name></proto>
+ <command name="vkCmdCopyBuffer2KHR" alias="vkCmdCopyBuffer2"/>
+ <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdCopyImage2</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
- <param>const <type>VkCopyImageInfo2KHR</type>* <name>pCopyImageInfo</name></param>
+ <param>const <type>VkCopyImageInfo2</type>* <name>pCopyImageInfo</name></param>
</command>
- <command queues="graphics" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
- <proto><type>void</type> <name>vkCmdBlitImage2KHR</name></proto>
+ <command name="vkCmdCopyImage2KHR" alias="vkCmdCopyImage2"/>
+ <command queues="graphics" renderpass="outside" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdBlitImage2</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
- <param>const <type>VkBlitImageInfo2KHR</type>* <name>pBlitImageInfo</name></param>
+ <param>const <type>VkBlitImageInfo2</type>* <name>pBlitImageInfo</name></param>
</command>
- <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
- <proto><type>void</type> <name>vkCmdCopyBufferToImage2KHR</name></proto>
+ <command name="vkCmdBlitImage2KHR" alias="vkCmdBlitImage2"/>
+ <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdCopyBufferToImage2</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
- <param>const <type>VkCopyBufferToImageInfo2KHR</type>* <name>pCopyBufferToImageInfo</name></param>
+ <param>const <type>VkCopyBufferToImageInfo2</type>* <name>pCopyBufferToImageInfo</name></param>
</command>
- <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
- <proto><type>void</type> <name>vkCmdCopyImageToBuffer2KHR</name></proto>
+ <command name="vkCmdCopyBufferToImage2KHR" alias="vkCmdCopyBufferToImage2"/>
+ <command queues="transfer,graphics,compute" renderpass="outside" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdCopyImageToBuffer2</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
- <param>const <type>VkCopyImageToBufferInfo2KHR</type>* <name>pCopyImageToBufferInfo</name></param>
+ <param>const <type>VkCopyImageToBufferInfo2</type>* <name>pCopyImageToBufferInfo</name></param>
</command>
- <command queues="graphics" renderpass="outside" cmdbufferlevel="primary,secondary" pipeline="transfer">
- <proto><type>void</type> <name>vkCmdResolveImage2KHR</name></proto>
+ <command name="vkCmdCopyImageToBuffer2KHR" alias="vkCmdCopyImageToBuffer2"/>
+ <command queues="graphics" renderpass="outside" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdResolveImage2</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
- <param>const <type>VkResolveImageInfo2KHR</type>* <name>pResolveImageInfo</name></param>
+ <param>const <type>VkResolveImageInfo2</type>* <name>pResolveImageInfo</name></param>
</command>
+ <command name="vkCmdResolveImage2KHR" alias="vkCmdResolveImage2"/>
<command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
<proto><type>void</type> <name>vkCmdSetFragmentShadingRateKHR</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
@@ -9290,16 +11871,328 @@ typedef void <name>CAMetalLayer</name>;
<param><type>VkDevice</type> <name>device</name></param>
<param><type>VkAccelerationStructureBuildTypeKHR</type> <name>buildType</name></param>
<param>const <type>VkAccelerationStructureBuildGeometryInfoKHR</type>* <name>pBuildInfo</name></param>
- <param len="pBuildInfo-&gt;geometryCount">const <type>uint32_t</type>* <name>pMaxPrimitiveCounts</name></param>
+ <param optional="true" len="pBuildInfo-&gt;geometryCount">const <type>uint32_t</type>* <name>pMaxPrimitiveCounts</name></param>
<param><type>VkAccelerationStructureBuildSizesInfoKHR</type>* <name>pSizeInfo</name></param>
</command>
+ <command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdSetVertexInputEXT</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param optional="true"><type>uint32_t</type> <name>vertexBindingDescriptionCount</name></param>
+ <param len="vertexBindingDescriptionCount">const <type>VkVertexInputBindingDescription2EXT</type>* <name>pVertexBindingDescriptions</name></param>
+ <param optional="true"><type>uint32_t</type> <name>vertexAttributeDescriptionCount</name></param>
+ <param len="vertexAttributeDescriptionCount">const <type>VkVertexInputAttributeDescription2EXT</type>* <name>pVertexAttributeDescriptions</name></param>
+ </command>
+ <command queues="graphics" renderpass="both" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdSetColorWriteEnableEXT</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param><type>uint32_t</type> <name>attachmentCount</name></param>
+ <param len="attachmentCount">const <type>VkBool32</type>* <name>pColorWriteEnables</name></param>
+ </command>
+ <command queues="graphics,compute" renderpass="outside" videocoding="both" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdSetEvent2</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param><type>VkEvent</type> <name>event</name></param>
+ <param>const <type>VkDependencyInfo</type>* <name>pDependencyInfo</name></param>
+ </command>
+ <command name="vkCmdSetEvent2KHR" alias="vkCmdSetEvent2"/>
+ <command queues="graphics,compute" renderpass="outside" videocoding="both" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdResetEvent2</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param><type>VkEvent</type> <name>event</name></param>
+ <param optional="true"><type>VkPipelineStageFlags2</type> <name>stageMask</name></param>
+ </command>
+ <command name="vkCmdResetEvent2KHR" alias="vkCmdResetEvent2"/>
+ <command queues="graphics,compute" renderpass="both" videocoding="both" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdWaitEvents2</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param><type>uint32_t</type> <name>eventCount</name></param>
+ <param len="eventCount">const <type>VkEvent</type>* <name>pEvents</name></param>
+ <param len="eventCount">const <type>VkDependencyInfo</type>* <name>pDependencyInfos</name></param>
+ </command>
+ <command name="vkCmdWaitEvents2KHR" alias="vkCmdWaitEvents2"/>
+ <command queues="transfer,graphics,compute" renderpass="both" videocoding="both" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdPipelineBarrier2</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param>const <type>VkDependencyInfo</type>* <name>pDependencyInfo</name></param>
+ </command>
+ <command name="vkCmdPipelineBarrier2KHR" alias="vkCmdPipelineBarrier2"/>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_DEVICE_LOST">
+ <proto><type>VkResult</type> <name>vkQueueSubmit2</name></proto>
+ <param externsync="true"><type>VkQueue</type> <name>queue</name></param>
+ <param optional="true"><type>uint32_t</type> <name>submitCount</name></param>
+ <param len="submitCount">const <type>VkSubmitInfo2</type>* <name>pSubmits</name></param>
+ <param optional="true" externsync="true"><type>VkFence</type> <name>fence</name></param>
+ </command>
+ <command name="vkQueueSubmit2KHR" alias="vkQueueSubmit2"/>
+ <command queues="transfer,graphics,compute,decode,encode" renderpass="both" videocoding="both" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdWriteTimestamp2</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param optional="true"><type>VkPipelineStageFlags2</type> <name>stage</name></param>
+ <param><type>VkQueryPool</type> <name>queryPool</name></param>
+ <param><type>uint32_t</type> <name>query</name></param>
+ </command>
+ <command name="vkCmdWriteTimestamp2KHR" alias="vkCmdWriteTimestamp2"/>
+ <command queues="transfer,graphics,compute" renderpass="both" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdWriteBufferMarker2AMD</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param optional="true"><type>VkPipelineStageFlags2</type> <name>stage</name></param>
+ <param><type>VkBuffer</type> <name>dstBuffer</name></param>
+ <param><type>VkDeviceSize</type> <name>dstOffset</name></param>
+ <param><type>uint32_t</type> <name>marker</name></param>
+ </command>
+ <command>
+ <proto><type>void</type> <name>vkGetQueueCheckpointData2NV</name></proto>
+ <param><type>VkQueue</type> <name>queue</name></param>
+ <param optional="false,true"><type>uint32_t</type>* <name>pCheckpointDataCount</name></param>
+ <param optional="true" len="pCheckpointDataCount"><type>VkCheckpointData2NV</type>* <name>pCheckpointData</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR,VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR,VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR,VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR">
+ <proto><type>VkResult</type> <name>vkGetPhysicalDeviceVideoCapabilitiesKHR</name></proto>
+ <param><type>VkPhysicalDevice</type> <name>physicalDevice</name></param>
+ <param>const <type>VkVideoProfileKHR</type>* <name>pVideoProfile</name></param>
+ <param><type>VkVideoCapabilitiesKHR</type>* <name>pCapabilities</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS,VK_INCOMPLETE" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR,VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR,VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR,VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR,VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR">
+ <proto><type>VkResult</type> <name>vkGetPhysicalDeviceVideoFormatPropertiesKHR</name></proto>
+ <param><type>VkPhysicalDevice</type> <name>physicalDevice</name></param>
+ <param>const <type>VkPhysicalDeviceVideoFormatInfoKHR</type>* <name>pVideoFormatInfo</name></param>
+ <param optional="false,true"><type>uint32_t</type>* <name>pVideoFormatPropertyCount</name></param>
+ <param optional="true" len="pVideoFormatPropertyCount"><type>VkVideoFormatPropertiesKHR</type>* <name>pVideoFormatProperties</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_INITIALIZATION_FAILED,VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR">
+ <proto><type>VkResult</type> <name>vkCreateVideoSessionKHR</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkVideoSessionCreateInfoKHR</type>* <name>pCreateInfo</name></param>
+ <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
+ <param><type>VkVideoSessionKHR</type>* <name>pVideoSession</name></param>
+ </command>
+ <command>
+ <proto><type>void</type> <name>vkDestroyVideoSessionKHR</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkVideoSessionKHR</type> <name>videoSession</name></param>
+ <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_INITIALIZATION_FAILED,VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_TOO_MANY_OBJECTS">
+ <proto><type>VkResult</type> <name>vkCreateVideoSessionParametersKHR</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkVideoSessionParametersCreateInfoKHR</type>* <name>pCreateInfo</name></param>
+ <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
+ <param><type>VkVideoSessionParametersKHR</type>* <name>pVideoSessionParameters</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_INITIALIZATION_FAILED,VK_ERROR_TOO_MANY_OBJECTS">
+ <proto><type>VkResult</type> <name>vkUpdateVideoSessionParametersKHR</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkVideoSessionParametersKHR</type> <name>videoSessionParameters</name></param>
+ <param>const <type>VkVideoSessionParametersUpdateInfoKHR</type>* <name>pUpdateInfo</name></param>
+ </command>
+ <command>
+ <proto><type>void</type> <name>vkDestroyVideoSessionParametersKHR</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkVideoSessionParametersKHR</type> <name>videoSessionParameters</name></param>
+ <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS,VK_INCOMPLETE" errorcodes="VK_ERROR_INITIALIZATION_FAILED">
+ <proto><type>VkResult</type> <name>vkGetVideoSessionMemoryRequirementsKHR</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkVideoSessionKHR</type> <name>videoSession</name></param>
+ <param optional="false,true"><type>uint32_t</type>* <name>pVideoSessionMemoryRequirementsCount</name></param>
+ <param optional="true" len="pVideoSessionMemoryRequirementsCount"><type>VkVideoGetMemoryPropertiesKHR</type>* <name>pVideoSessionMemoryRequirements</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_INITIALIZATION_FAILED">
+ <proto><type>VkResult</type> <name>vkBindVideoSessionMemoryKHR</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkVideoSessionKHR</type> <name>videoSession</name></param>
+ <param><type>uint32_t</type> <name>videoSessionBindMemoryCount</name></param>
+ <param len="videoSessionBindMemoryCount">const <type>VkVideoBindMemoryKHR</type>* <name>pVideoSessionBindMemories</name></param>
+ </command>
+ <command queues="decode" renderpass="outside" videocoding="inside" cmdbufferlevel="primary">
+ <proto><type>void</type> <name>vkCmdDecodeVideoKHR</name></proto>
+ <param><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param>const <type>VkVideoDecodeInfoKHR</type>* <name>pFrameInfo</name></param>
+ </command>
+ <command queues="decode,encode" renderpass="outside" videocoding="outside" cmdbufferlevel="primary">
+ <proto><type>void</type> <name>vkCmdBeginVideoCodingKHR</name></proto>
+ <param><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param>const <type>VkVideoBeginCodingInfoKHR</type>* <name>pBeginInfo</name></param>
+ </command>
+ <command queues="decode,encode" renderpass="outside" videocoding="inside" cmdbufferlevel="primary">
+ <proto><type>void</type> <name>vkCmdControlVideoCodingKHR</name></proto>
+ <param><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param>const <type>VkVideoCodingControlInfoKHR</type>* <name>pCodingControlInfo</name></param>
+ </command>
+ <command queues="decode,encode" renderpass="outside" videocoding="inside" cmdbufferlevel="primary">
+ <proto><type>void</type> <name>vkCmdEndVideoCodingKHR</name></proto>
+ <param><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param>const <type>VkVideoEndCodingInfoKHR</type>* <name>pEndCodingInfo</name></param>
+ </command>
+ <command queues="encode" renderpass="outside" videocoding="inside" cmdbufferlevel="primary">
+ <proto><type>void</type> <name>vkCmdEncodeVideoKHR</name></proto>
+ <param><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param>const <type>VkVideoEncodeInfoKHR</type>* <name>pEncodeInfo</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_INITIALIZATION_FAILED">
+ <proto><type>VkResult</type> <name>vkCreateCuModuleNVX</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkCuModuleCreateInfoNVX</type>* <name>pCreateInfo</name></param>
+ <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
+ <param><type>VkCuModuleNVX</type>* <name>pModule</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_INITIALIZATION_FAILED">
+ <proto><type>VkResult</type> <name>vkCreateCuFunctionNVX</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkCuFunctionCreateInfoNVX</type>* <name>pCreateInfo</name></param>
+ <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
+ <param><type>VkCuFunctionNVX</type>* <name>pFunction</name></param>
+ </command>
+ <command>
+ <proto><type>void</type> <name>vkDestroyCuModuleNVX</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkCuModuleNVX</type> <name>module</name></param>
+ <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
+ </command>
+ <command>
+ <proto><type>void</type> <name>vkDestroyCuFunctionNVX</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkCuFunctionNVX</type> <name>function</name></param>
+ <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
+ </command>
+ <command queues="graphics,compute" renderpass="both" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdCuLaunchKernelNVX</name></proto>
+ <param><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param>const <type>VkCuLaunchInfoNVX</type>* <name>pLaunchInfo</name></param>
+ </command>
+ <command>
+ <proto><type>void</type> <name>vkSetDeviceMemoryPriorityEXT</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkDeviceMemory</type> <name>memory</name></param>
+ <param><type>float</type> <name>priority</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_INITIALIZATION_FAILED">
+ <proto><type>VkResult</type> <name>vkAcquireDrmDisplayEXT</name></proto>
+ <param><type>VkPhysicalDevice</type> <name>physicalDevice</name></param>
+ <param><type>int32_t</type> <name>drmFd</name></param>
+ <param><type>VkDisplayKHR</type> <name>display</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_INITIALIZATION_FAILED,VK_ERROR_OUT_OF_HOST_MEMORY">
+ <proto><type>VkResult</type> <name>vkGetDrmDisplayEXT</name></proto>
+ <param><type>VkPhysicalDevice</type> <name>physicalDevice</name></param>
+ <param><type>int32_t</type> <name>drmFd</name></param>
+ <param><type>uint32_t</type> <name>connectorId</name></param>
+ <param><type>VkDisplayKHR</type>* <name>display</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS,VK_TIMEOUT,VK_SUBOPTIMAL_KHR" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_DEVICE_LOST,VK_ERROR_OUT_OF_DATE_KHR,VK_ERROR_SURFACE_LOST_KHR,VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT">
+ <proto><type>VkResult</type> <name>vkWaitForPresentKHR</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param externsync="true"><type>VkSwapchainKHR</type> <name>swapchain</name></param>
+ <param><type>uint64_t</type> <name>presentId</name></param>
+ <param><type>uint64_t</type> <name>timeout</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_INVALID_EXTERNAL_HANDLE,VK_ERROR_INITIALIZATION_FAILED">
+ <proto><type>VkResult</type> <name>vkCreateBufferCollectionFUCHSIA</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkBufferCollectionCreateInfoFUCHSIA</type>* <name>pCreateInfo</name></param>
+ <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
+ <param><type>VkBufferCollectionFUCHSIA</type>* <name>pCollection</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_INITIALIZATION_FAILED,VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_FORMAT_NOT_SUPPORTED">
+ <proto><type>VkResult</type> <name>vkSetBufferCollectionBufferConstraintsFUCHSIA</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkBufferCollectionFUCHSIA</type> <name>collection</name></param>
+ <param>const <type>VkBufferConstraintsInfoFUCHSIA</type>* <name>pBufferConstraintsInfo</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_INITIALIZATION_FAILED,VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_FORMAT_NOT_SUPPORTED">
+ <proto><type>VkResult</type> <name>vkSetBufferCollectionImageConstraintsFUCHSIA</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkBufferCollectionFUCHSIA</type> <name>collection</name></param>
+ <param>const <type>VkImageConstraintsInfoFUCHSIA</type>* <name>pImageConstraintsInfo</name></param>
+ </command>
+ <command>
+ <proto><type>void</type> <name>vkDestroyBufferCollectionFUCHSIA</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkBufferCollectionFUCHSIA</type> <name>collection</name></param>
+ <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_INITIALIZATION_FAILED">
+ <proto><type>VkResult</type> <name>vkGetBufferCollectionPropertiesFUCHSIA</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkBufferCollectionFUCHSIA</type> <name>collection</name></param>
+ <param><type>VkBufferCollectionPropertiesFUCHSIA</type>* <name>pProperties</name></param>
+ </command>
+ <command queues="graphics" renderpass="outside" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdBeginRendering</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ <param>const <type>VkRenderingInfo</type>* <name>pRenderingInfo</name></param>
+ </command>
+ <command name="vkCmdBeginRenderingKHR" alias="vkCmdBeginRendering"/>
+ <command queues="graphics" renderpass="inside" cmdbufferlevel="primary,secondary">
+ <proto><type>void</type> <name>vkCmdEndRendering</name></proto>
+ <param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
+ </command>
+ <command name="vkCmdEndRenderingKHR" alias="vkCmdEndRendering"/>
+ <command>
+ <proto><type>void</type> <name>vkGetDescriptorSetLayoutHostMappingInfoVALVE</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkDescriptorSetBindingReferenceVALVE</type>* <name>pBindingReference</name></param>
+ <param><type>VkDescriptorSetLayoutHostMappingInfoVALVE</type>* <name>pHostMapping</name></param>
+ </command>
+ <command>
+ <proto><type>void</type> <name>vkGetDescriptorSetHostMappingVALVE</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkDescriptorSet</type> <name>descriptorSet</name></param>
+ <param><type>void</type>** <name>ppData</name></param>
+ </command>
+ <command>
+ <proto><type>void</type> <name>vkGetShaderModuleIdentifierEXT</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkShaderModule</type> <name>shaderModule</name></param>
+ <param><type>VkShaderModuleIdentifierEXT</type>* <name>pIdentifier</name></param>
+ </command>
+ <command>
+ <proto><type>void</type> <name>vkGetShaderModuleCreateInfoIdentifierEXT</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkShaderModuleCreateInfo</type>* <name>pCreateInfo</name></param>
+ <param><type>VkShaderModuleIdentifierEXT</type>* <name>pIdentifier</name></param>
+ </command>
+ <command>
+ <proto><type>void</type> <name>vkGetImageSubresourceLayout2EXT</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkImage</type> <name>image</name></param>
+ <param>const <type>VkImageSubresource2EXT</type>* <name>pSubresource</name></param>
+ <param><type>VkSubresourceLayout2EXT</type>* <name>pLayout</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY">
+ <proto><type>VkResult</type> <name>vkGetPipelinePropertiesEXT</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkPipelineInfoEXT</type>* <name>pPipelineInfo</name></param>
+ <param noautovalidity="true" validstructs="VkPipelinePropertiesIdentifierEXT"><type>VkBaseOutStructure</type>* <name>pPipelineProperties</name></param>
+ </command>
+ <command>
+ <proto><type>void</type> <name>vkExportMetalObjectsEXT</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkExportMetalObjectsInfoEXT</type>* <name>pMetalObjectsInfo</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS,VK_INCOMPLETE">
+ <proto><type>VkResult</type> <name>vkGetFramebufferTilePropertiesQCOM</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param><type>VkFramebuffer</type> <name>framebuffer</name></param>
+ <param optional="false,true"><type>uint32_t</type>* <name>pPropertiesCount</name></param>
+ <param optional="true" len="pPropertiesCount"><type>VkTilePropertiesQCOM</type>* <name>pProperties</name></param>
+ </command>
+ <command successcodes="VK_SUCCESS">
+ <proto><type>VkResult</type> <name>vkGetDynamicRenderingTilePropertiesQCOM</name></proto>
+ <param><type>VkDevice</type> <name>device</name></param>
+ <param>const <type>VkRenderingInfo</type>* <name>pRenderingInfo</name></param>
+ <param><type>VkTilePropertiesQCOM</type>* <name>pProperties</name></param>
+ </command>
</commands>
<feature api="vulkan" name="VK_VERSION_1_0" number="1.0" comment="Vulkan core API interface definitions">
<require comment="Header boilerplate">
<type name="vk_platform"/>
<type name="VK_DEFINE_HANDLE"/>
+ <type name="VK_USE_64_BIT_PTR_DEFINES"/>
<type name="VK_DEFINE_NON_DISPATCHABLE_HANDLE"/>
+ <type name="VK_NULL_HANDLE"/>
</require>
<require comment="Fundamental types used by many commands and structures">
<type name="VkBool32"/>
@@ -9314,6 +12207,24 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkResult"/>
<type name="VkStructureType"/>
</require>
+ <require comment="API constants">
+ <enum name="VK_ATTACHMENT_UNUSED"/>
+ <enum name="VK_FALSE"/>
+ <enum name="VK_LOD_CLAMP_NONE"/>
+ <enum name="VK_QUEUE_FAMILY_IGNORED"/>
+ <enum name="VK_REMAINING_ARRAY_LAYERS"/>
+ <enum name="VK_REMAINING_MIP_LEVELS"/>
+ <enum name="VK_SUBPASS_EXTERNAL"/>
+ <enum name="VK_TRUE"/>
+ <enum name="VK_WHOLE_SIZE"/>
+ <enum name="VK_MAX_MEMORY_TYPES"/>
+ <enum name="VK_MAX_PHYSICAL_DEVICE_NAME_SIZE"/>
+ <enum name="VK_UUID_SIZE"/>
+ <enum name="VK_MAX_EXTENSION_NAME_SIZE"/>
+ <enum name="VK_MAX_DESCRIPTION_SIZE"/>
+ <enum name="VK_MAX_MEMORY_HEAPS"/>
+ <type name="VkPipelineCacheHeaderVersion"/>
+ </require>
<require comment="These types are part of the API, though not directly used in API commands or data structures">
<type name="VkBaseInStructure"/>
<type name="VkBaseOutStructure"/>
@@ -9324,6 +12235,7 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkImageMemoryBarrier"/>
<type name="VkMemoryBarrier"/>
<type name="VkObjectType"/>
+ <type name="VkPipelineCacheHeaderVersionOne"/>
<type name="VkVendorId"/>
</require>
<require comment="API version macros">
@@ -9335,19 +12247,11 @@ typedef void <name>CAMetalLayer</name>;
<type name="VK_VERSION_MAJOR"/>
<type name="VK_VERSION_MINOR"/>
<type name="VK_VERSION_PATCH"/>
- </require>
- <require comment="API constants">
- <enum name="VK_ATTACHMENT_UNUSED"/>
- <enum name="VK_FALSE"/>
- <enum name="VK_LOD_CLAMP_NONE"/>
- <enum name="VK_QUEUE_FAMILY_IGNORED"/>
- <enum name="VK_REMAINING_ARRAY_LAYERS"/>
- <enum name="VK_REMAINING_MIP_LEVELS"/>
- <enum name="VK_SUBPASS_EXTERNAL"/>
- <enum name="VK_TRUE"/>
- <enum name="VK_WHOLE_SIZE"/>
- <type name="VK_NULL_HANDLE"/>
- <type name="VkPipelineCacheHeaderVersion"/>
+ <type name="VK_MAKE_API_VERSION"/>
+ <type name="VK_API_VERSION_VARIANT"/>
+ <type name="VK_API_VERSION_MAJOR"/>
+ <type name="VK_API_VERSION_MINOR"/>
+ <type name="VK_API_VERSION_PATCH"/>
</require>
<require comment="Device initialization">
<type name="PFN_vkAllocationFunction"/>
@@ -9371,6 +12275,7 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkImageUsageFlags"/>
<type name="VkInstance"/>
<type name="VkInstanceCreateFlags"/>
+ <type name="VkInstanceCreateFlagBits"/>
<type name="VkInstanceCreateInfo"/>
<type name="VkInternalAllocationType"/>
<type name="VkMemoryHeap"/>
@@ -9391,7 +12296,6 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkQueueFlags"/>
<type name="VkSampleCountFlagBits"/>
<type name="VkSampleCountFlags"/>
- <type name="VkStructureType"/>
<type name="VkSystemAllocationScope"/>
<command name="vkCreateInstance"/>
<command name="vkDestroyInstance"/>
@@ -9407,10 +12311,9 @@ typedef void <name>CAMetalLayer</name>;
</require>
<require comment="Device commands">
<type name="VkDevice"/>
- <type name="VkDeviceCreateFlags"/>
+ <type name="VkDeviceCreateFlags" comment="Will add VkDeviceCreateFlagBits when bits are defined in the future"/>
<type name="VkDeviceCreateInfo"/>
- <type name="VkDeviceQueueCreateFlagBits"/>
- <type name="VkDeviceQueueCreateFlags"/>
+ <type name="VkDeviceQueueCreateFlags" comment="VkDeviceQueueCreateFlagBits was added later"/>
<type name="VkDeviceQueueCreateInfo"/>
<command name="vkCreateDevice"/>
<command name="vkDestroyDevice"/>
@@ -9488,7 +12391,7 @@ typedef void <name>CAMetalLayer</name>;
</require>
<require comment="Queue semaphore commands">
<type name="VkSemaphore"/>
- <type name="VkSemaphoreCreateFlags"/>
+ <type name="VkSemaphoreCreateFlags" comment="Will add VkSemaphoreCreateFlagBits when bits are defined in the future"/>
<type name="VkSemaphoreCreateInfo"/>
<command name="vkCreateSemaphore"/>
<command name="vkDestroySemaphore"/>
@@ -9496,6 +12399,7 @@ typedef void <name>CAMetalLayer</name>;
<require comment="Event commands">
<type name="VkEvent"/>
<type name="VkEventCreateFlags"/>
+ <type name="VkEventCreateFlagBits"/>
<type name="VkEventCreateInfo"/>
<command name="vkCreateEvent"/>
<command name="vkDestroyEvent"/>
@@ -9507,7 +12411,7 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkQueryPipelineStatisticFlagBits"/>
<type name="VkQueryPipelineStatisticFlags"/>
<type name="VkQueryPool"/>
- <type name="VkQueryPoolCreateFlags"/>
+ <type name="VkQueryPoolCreateFlags" comment="Will add VkQueryPoolCreateFlagBits when bits are defined in the future"/>
<type name="VkQueryPoolCreateInfo"/>
<type name="VkQueryResultFlagBits"/>
<type name="VkQueryResultFlags"/>
@@ -9529,7 +12433,7 @@ typedef void <name>CAMetalLayer</name>;
</require>
<require comment="Buffer view commands">
<type name="VkBufferView"/>
- <type name="VkBufferViewCreateFlags" comment="Will need FlagBits type eventually"/>
+ <type name="VkBufferViewCreateFlags" comment="Will add VkBufferViewFlagBits when bits are defined in the future"/>
<type name="VkBufferViewCreateInfo"/>
<command name="vkCreateBufferView"/>
<command name="vkDestroyBufferView"/>
@@ -9557,7 +12461,6 @@ typedef void <name>CAMetalLayer</name>;
</require>
<require comment="Shader commands">
<type name="VkShaderModule"/>
- <type name="VkShaderModuleCreateFlagBits"/>
<type name="VkShaderModuleCreateFlags"/>
<type name="VkShaderModuleCreateInfo"/>
<command name="vkCreateShaderModule"/>
@@ -9565,8 +12468,7 @@ typedef void <name>CAMetalLayer</name>;
</require>
<require comment="Pipeline Cache commands">
<type name="VkPipelineCache"/>
- <type name="VkPipelineCacheCreateFlagBits"/>
- <type name="VkPipelineCacheCreateFlags"/>
+ <type name="VkPipelineCacheCreateFlags" comment="VkPipelineCacheCreateFlagBits was added later"/>
<type name="VkPipelineCacheCreateInfo"/>
<command name="vkCreatePipelineCache"/>
<command name="vkDestroyPipelineCache"/>
@@ -9588,29 +12490,29 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkLogicOp"/>
<type name="VkPipeline"/>
<type name="VkPipelineColorBlendAttachmentState"/>
- <type name="VkPipelineColorBlendStateCreateFlags" comment="Will need FlagBits type eventually"/>
+ <type name="VkPipelineColorBlendStateCreateFlags" comment="Will add VkPipeline*StateFlagBits when bits are defined in the future"/>
<type name="VkPipelineColorBlendStateCreateInfo"/>
<type name="VkPipelineCreateFlagBits"/>
<type name="VkPipelineCreateFlags"/>
- <type name="VkPipelineDepthStencilStateCreateFlags" comment="Will need FlagBits type eventually"/>
+ <type name="VkPipelineDepthStencilStateCreateFlags" comment="Will add VkPipeline*StateFlagBits when bits are defined in the future"/>
<type name="VkPipelineDepthStencilStateCreateInfo"/>
- <type name="VkPipelineDynamicStateCreateFlags" comment="Will need FlagBits type eventually"/>
+ <type name="VkPipelineDynamicStateCreateFlags" comment="Will add VkPipeline*StateFlagBits when bits are defined in the future"/>
<type name="VkPipelineDynamicStateCreateInfo"/>
- <type name="VkPipelineInputAssemblyStateCreateFlags" comment="Will need FlagBits type eventually"/>
+ <type name="VkPipelineInputAssemblyStateCreateFlags" comment="Will add VkPipeline*StateFlagBits when bits are defined in the future"/>
<type name="VkPipelineInputAssemblyStateCreateInfo"/>
- <type name="VkPipelineLayoutCreateFlags" comment="Will need FlagBits type eventually"/>
- <type name="VkPipelineMultisampleStateCreateFlags" comment="Will need FlagBits type eventually"/>
+ <type name="VkPipelineLayoutCreateFlags" comment="Will add VkPipelineLayoutCreateFlagBits when bits are defined in the future"/>
+ <type name="VkPipelineMultisampleStateCreateFlags" comment="Will add VkPipelineMultisampleStateCreateFlagBits when bits are defined in the future"/>
<type name="VkPipelineMultisampleStateCreateInfo"/>
- <type name="VkPipelineRasterizationStateCreateFlags" comment="Will need FlagBits type eventually"/>
+ <type name="VkPipelineRasterizationStateCreateFlags" comment="Will add VkPipelineRasterizationStateCreateFlagBits when bits are defined in the future"/>
<type name="VkPipelineRasterizationStateCreateInfo"/>
<type name="VkPipelineShaderStageCreateFlagBits"/>
<type name="VkPipelineShaderStageCreateFlags"/>
<type name="VkPipelineShaderStageCreateInfo"/>
- <type name="VkPipelineTessellationStateCreateFlags" comment="Will need FlagBits type eventually"/>
+ <type name="VkPipelineTessellationStateCreateFlags" comment="Will add VkPipelineTessellationStateCreateFlagBits when bits are defined in the future"/>
<type name="VkPipelineTessellationStateCreateInfo"/>
- <type name="VkPipelineVertexInputStateCreateFlags" comment="Will need FlagBits type eventually"/>
+ <type name="VkPipelineVertexInputStateCreateFlags" comment="Will add VkPipelineVertexInputStateCreateFlagBits when bits are defined in the future"/>
<type name="VkPipelineVertexInputStateCreateInfo"/>
- <type name="VkPipelineViewportStateCreateFlags" comment="Will need FlagBits type eventually"/>
+ <type name="VkPipelineViewportStateCreateFlags" comment="Will add VkPipelineViewportStateCreateFlagBits when bits are defined in the future"/>
<type name="VkPipelineViewportStateCreateInfo"/>
<type name="VkPolygonMode"/>
<type name="VkPrimitiveTopology"/>
@@ -9956,13 +12858,14 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkPhysicalDeviceVariablePointerFeatures"/>
<type name="VkPhysicalDeviceVariablePointersFeatures"/>
</require>
- <require comment="Originally based on VK_KHR_protected_memory (extension 146), which was never published; thus the mystifying large value= numbers below. These are not aliased since they weren't actually promoted from an extension.">
+ <require comment="Originally based on VK_KHR_protected_memory (extension 146), which was never published; thus the mystifying large value= numbers below. These are not aliased since they were not actually promoted from an extension.">
<enum extends="VkStructureType" extnumber="146" offset="0" name="VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO"/>
<enum extends="VkStructureType" extnumber="146" offset="1" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES"/>
<enum extends="VkStructureType" extnumber="146" offset="2" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES"/>
<enum extends="VkStructureType" extnumber="146" offset="3" name="VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2"/>
<enum bitpos="4" extends="VkQueueFlagBits" name="VK_QUEUE_PROTECTED_BIT" comment="Queues may support protected operations"/>
<enum bitpos="0" extends="VkDeviceQueueCreateFlagBits" name="VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT" comment="Queue is a protected-capable device queue"/>
+ <type name="VkDeviceQueueCreateFlagBits" comment="This is a temporary workaround for processors not recognizing that VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT above also requires this type"/>
<enum bitpos="5" extends="VkMemoryPropertyFlagBits" name="VK_MEMORY_PROPERTY_PROTECTED_BIT" comment="Memory is protected"/>
<enum bitpos="3" extends="VkBufferCreateFlagBits" name="VK_BUFFER_CREATE_PROTECTED_BIT" comment="Buffer requires protected memory"/>
<enum bitpos="11" extends="VkImageCreateFlagBits" name="VK_IMAGE_CREATE_PROTECTED_BIT" comment="Image requires protected memory"/>
@@ -10245,7 +13148,6 @@ typedef void <name>CAMetalLayer</name>;
<enum offset="0" extends="VkStructureType" extnumber="212" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES"/>
<type name="VkPhysicalDeviceVulkanMemoryModelFeatures"/>
</require>
- <!-- Phase 2 features -->
<require comment="Promoted from VK_KHR_imageless_framebuffer (extension 109)">
<type name="VkPhysicalDeviceImagelessFramebufferFeatures"/>
<type name="VkFramebufferAttachmentsCreateInfo"/>
@@ -10325,6 +13227,270 @@ typedef void <name>CAMetalLayer</name>;
<command name="vkGetDeviceMemoryOpaqueCaptureAddress"/>
</require>
</feature>
+ <feature api="vulkan" name="VK_VERSION_1_3" number="1.3" comment="Vulkan 1.3 core API interface definitions.">
+ <require>
+ <type name="VK_API_VERSION_1_3"/>
+ </require>
+ <require>
+ <type name="VkFlags64"/>
+ </require>
+ <require>
+ <enum extends="VkStructureType" value="53" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES"/>
+ <enum extends="VkStructureType" value="54" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES"/>
+ <type name="VkPhysicalDeviceVulkan13Features"/>
+ <type name="VkPhysicalDeviceVulkan13Properties"/>
+ </require>
+ <require comment="Promoted from VK_EXT_pipeline_creation_feedback (extension 193)">
+ <enum offset="0" extends="VkStructureType" extnumber="193" name="VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO"/>
+ <type name="VkPipelineCreationFeedbackFlagBits"/>
+ <type name="VkPipelineCreationFeedbackFlags"/>
+ <type name="VkPipelineCreationFeedbackCreateInfo"/>
+ <type name="VkPipelineCreationFeedback"/>
+ </require>
+ <require comment="Promoted from VK_KHR_shader_terminate_invocation (extension 216)">
+ <enum offset="0" extends="VkStructureType" extnumber="216" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES"/>
+ <type name="VkPhysicalDeviceShaderTerminateInvocationFeatures"/>
+ </require>
+ <require comment="Promoted from VK_EXT_tooling_info (extension 246)">
+ <enum offset="0" extends="VkStructureType" extnumber="246" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TOOL_PROPERTIES"/>
+ <type name="VkToolPurposeFlagBits"/>
+ <type name="VkToolPurposeFlags"/>
+ <type name="VkPhysicalDeviceToolProperties"/>
+ <command name="vkGetPhysicalDeviceToolProperties"/>
+ </require>
+ <require comment="Promoted from VK_EXT_shader_demote_to_helper_invocation (extension 277)">
+ <enum offset="0" extends="VkStructureType" extnumber="277" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES"/>
+ <type name="VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures"/>
+ </require>
+ <require comment="Promoted from VK_KHR_shader_non_semantic_info (extension 294)">
+ </require>
+ <require comment="Promoted from VK_EXT_private_data (extension 296)">
+ <enum offset="0" extends="VkStructureType" extnumber="296" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES"/>
+ <enum offset="1" extends="VkStructureType" extnumber="296" name="VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO"/>
+ <enum offset="2" extends="VkStructureType" extnumber="296" name="VK_STRUCTURE_TYPE_PRIVATE_DATA_SLOT_CREATE_INFO"/>
+ <enum offset="0" extends="VkObjectType" extnumber="296" name="VK_OBJECT_TYPE_PRIVATE_DATA_SLOT"/>
+ <type name="VkPhysicalDevicePrivateDataFeatures"/>
+ <type name="VkDevicePrivateDataCreateInfo"/>
+ <type name="VkPrivateDataSlotCreateInfo"/>
+ <type name="VkPrivateDataSlot"/>
+ <type name="VkPrivateDataSlotCreateFlags" comment="Will add VkPrivateDataSlotCreateFlagBits when bits are defined in the future"/>
+ <command name="vkCreatePrivateDataSlot"/>
+ <command name="vkDestroyPrivateDataSlot"/>
+ <command name="vkSetPrivateData"/>
+ <command name="vkGetPrivateData"/>
+ </require>
+ <require comment="Promoted from VK_EXT_pipeline_creation_cache_control (extension 298)">
+ <enum offset="0" extends="VkStructureType" extnumber="298" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES"/>
+ <type name="VkPhysicalDevicePipelineCreationCacheControlFeatures"/>
+ <enum bitpos="8" extends="VkPipelineCreateFlagBits" name="VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT"/>
+ <enum bitpos="9" extends="VkPipelineCreateFlagBits" name="VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT"/>
+ <enum offset="0" extends="VkResult" extnumber="298" name="VK_PIPELINE_COMPILE_REQUIRED"/>
+ <enum bitpos="0" extends="VkPipelineCacheCreateFlagBits" name="VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT"/>
+ </require>
+ <require comment="Promoted from VK_KHR_synchronization2 (extension 315)">
+ <enum offset="0" extends="VkStructureType" extnumber="315" name="VK_STRUCTURE_TYPE_MEMORY_BARRIER_2"/>
+ <enum offset="1" extends="VkStructureType" extnumber="315" name="VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2"/>
+ <enum offset="2" extends="VkStructureType" extnumber="315" name="VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2"/>
+ <enum offset="3" extends="VkStructureType" extnumber="315" name="VK_STRUCTURE_TYPE_DEPENDENCY_INFO"/>
+ <enum offset="4" extends="VkStructureType" extnumber="315" name="VK_STRUCTURE_TYPE_SUBMIT_INFO_2"/>
+ <enum offset="5" extends="VkStructureType" extnumber="315" name="VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO"/>
+ <enum offset="6" extends="VkStructureType" extnumber="315" name="VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO"/>
+ <enum offset="7" extends="VkStructureType" extnumber="315" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES"/>
+ <enum bitpos="0" extends="VkEventCreateFlagBits" name="VK_EVENT_CREATE_DEVICE_ONLY_BIT"/>
+ <enum offset="0" extends="VkImageLayout" extnumber="315" name="VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL"/>
+ <enum offset="1" extends="VkImageLayout" extnumber="315" name="VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL"/>
+ <enum value="0" extends="VkPipelineStageFlagBits" name="VK_PIPELINE_STAGE_NONE"/>
+ <enum value="0" extends="VkAccessFlagBits" name="VK_ACCESS_NONE"/>
+ <type name="VkPipelineStageFlags2"/>
+ <type name="VkPipelineStageFlagBits2"/>
+ <type name="VkAccessFlags2"/>
+ <type name="VkAccessFlagBits2"/>
+ <type name="VkMemoryBarrier2"/>
+ <type name="VkBufferMemoryBarrier2"/>
+ <type name="VkImageMemoryBarrier2"/>
+ <type name="VkDependencyInfo"/>
+ <type name="VkSubmitInfo2"/>
+ <type name="VkSemaphoreSubmitInfo"/>
+ <type name="VkCommandBufferSubmitInfo"/>
+ <type name="VkSubmitFlagBits"/>
+ <type name="VkSubmitFlags"/>
+ <type name="VkPhysicalDeviceSynchronization2Features"/>
+ <command name="vkCmdSetEvent2"/>
+ <command name="vkCmdResetEvent2"/>
+ <command name="vkCmdWaitEvents2"/>
+ <command name="vkCmdPipelineBarrier2"/>
+ <command name="vkCmdWriteTimestamp2"/>
+ <command name="vkQueueSubmit2"/>
+ </require>
+ <require comment="Promoted from VK_KHR_zero_initialize_workgroup_memory (extension 326)">
+ <enum offset="0" extends="VkStructureType" extnumber="326" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES"/>
+ <type name="VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures"/>
+ </require>
+ <require comment="Promoted from VK_EXT_image_robustness (extension 336)">
+ <enum offset="0" extends="VkStructureType" extnumber="336" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES"/>
+ <type name="VkPhysicalDeviceImageRobustnessFeatures"/>
+ </require>
+ <require comment="Promoted from VK_KHR_copy_commands2 (extension 338)">
+ <enum offset="0" extends="VkStructureType" extnumber="338" name="VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2"/>
+ <enum offset="1" extends="VkStructureType" extnumber="338" name="VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2"/>
+ <enum offset="2" extends="VkStructureType" extnumber="338" name="VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2"/>
+ <enum offset="3" extends="VkStructureType" extnumber="338" name="VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2"/>
+ <enum offset="4" extends="VkStructureType" extnumber="338" name="VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2"/>
+ <enum offset="5" extends="VkStructureType" extnumber="338" name="VK_STRUCTURE_TYPE_RESOLVE_IMAGE_INFO_2"/>
+ <enum offset="6" extends="VkStructureType" extnumber="338" name="VK_STRUCTURE_TYPE_BUFFER_COPY_2"/>
+ <enum offset="7" extends="VkStructureType" extnumber="338" name="VK_STRUCTURE_TYPE_IMAGE_COPY_2"/>
+ <enum offset="8" extends="VkStructureType" extnumber="338" name="VK_STRUCTURE_TYPE_IMAGE_BLIT_2"/>
+ <enum offset="9" extends="VkStructureType" extnumber="338" name="VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2"/>
+ <enum offset="10" extends="VkStructureType" extnumber="338" name="VK_STRUCTURE_TYPE_IMAGE_RESOLVE_2"/>
+ <type name="VkCopyBufferInfo2"/>
+ <type name="VkCopyImageInfo2"/>
+ <type name="VkCopyBufferToImageInfo2"/>
+ <type name="VkCopyImageToBufferInfo2"/>
+ <type name="VkBlitImageInfo2"/>
+ <type name="VkResolveImageInfo2"/>
+ <type name="VkBufferCopy2"/>
+ <type name="VkImageCopy2"/>
+ <type name="VkImageBlit2"/>
+ <type name="VkBufferImageCopy2"/>
+ <type name="VkImageResolve2"/>
+ <command name="vkCmdCopyBuffer2"/>
+ <command name="vkCmdCopyImage2"/>
+ <command name="vkCmdCopyBufferToImage2"/>
+ <command name="vkCmdCopyImageToBuffer2"/>
+ <command name="vkCmdBlitImage2"/>
+ <command name="vkCmdResolveImage2"/>
+ </require>
+ <require comment="Promoted from VK_EXT_subgroup_size_control (STDPROMOTE/PROPLIMCHANGE) (extension 226)">
+ <type name="VkPhysicalDeviceSubgroupSizeControlFeatures"/>
+ <type name="VkPhysicalDeviceSubgroupSizeControlProperties"/>
+ <type name="VkPipelineShaderStageRequiredSubgroupSizeCreateInfo"/>
+ <enum offset="0" extends="VkStructureType" extnumber="226" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES"/>
+ <enum offset="1" extends="VkStructureType" extnumber="226" name="VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO"/>
+ <enum offset="2" extends="VkStructureType" extnumber="226" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES"/>
+ <enum bitpos="0" extends="VkPipelineShaderStageCreateFlagBits" name="VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT"/>
+ <enum bitpos="1" extends="VkPipelineShaderStageCreateFlagBits" name="VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT"/>
+ </require>
+ <require comment="Promoted from VK_EXT_inline_uniform_block (STDPROMOTE/PROPLIMCHANGE) (extension 139)">
+ <enum offset="0" extends="VkDescriptorType" extnumber="139" name="VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK"/>
+ <enum offset="0" extends="VkStructureType" extnumber="139" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES"/>
+ <enum offset="1" extends="VkStructureType" extnumber="139" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES"/>
+ <enum offset="2" extends="VkStructureType" extnumber="139" name="VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK"/>
+ <enum offset="3" extends="VkStructureType" extnumber="139" name="VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO"/>
+ <type name="VkPhysicalDeviceInlineUniformBlockFeatures"/>
+ <type name="VkPhysicalDeviceInlineUniformBlockProperties"/>
+ <type name="VkWriteDescriptorSetInlineUniformBlock"/>
+ <type name="VkDescriptorPoolInlineUniformBlockCreateInfo"/>
+ </require>
+ <require comment="Promoted from VK_EXT_ycbcr_2plane_444_formats (does not promote the Feature struct, just the formats) (extension 331)">
+ <enum offset="0" extends="VkFormat" extnumber="331" name="VK_FORMAT_G8_B8R8_2PLANE_444_UNORM"/>
+ <enum offset="1" extends="VkFormat" extnumber="331" name="VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16"/>
+ <enum offset="2" extends="VkFormat" extnumber="331" name="VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16"/>
+ <enum offset="3" extends="VkFormat" extnumber="331" name="VK_FORMAT_G16_B16R16_2PLANE_444_UNORM"/>
+ </require>
+ <require comment="Promoted from VK_EXT_4444_formats (does not promote the Feature struct, just the formats) (extension 341)">
+ <enum offset="0" extends="VkFormat" extnumber="341" name="VK_FORMAT_A4R4G4B4_UNORM_PACK16"/>
+ <enum offset="1" extends="VkFormat" extnumber="341" name="VK_FORMAT_A4B4G4R4_UNORM_PACK16"/>
+ </require>
+ <require comment="Promoted from VK_EXT_texture_compression_astc_hdr (Feature struct is promoted, but becomes optional) (extension 67)">
+ <enum offset="0" extends="VkStructureType" extnumber="67" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES"/>
+ <type name="VkPhysicalDeviceTextureCompressionASTCHDRFeatures"/>
+ <enum offset="0" extends="VkFormat" extnumber="67" name="VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK"/>
+ <enum offset="1" extends="VkFormat" extnumber="67" name="VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK"/>
+ <enum offset="2" extends="VkFormat" extnumber="67" name="VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK"/>
+ <enum offset="3" extends="VkFormat" extnumber="67" name="VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK"/>
+ <enum offset="4" extends="VkFormat" extnumber="67" name="VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK"/>
+ <enum offset="5" extends="VkFormat" extnumber="67" name="VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK"/>
+ <enum offset="6" extends="VkFormat" extnumber="67" name="VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK"/>
+ <enum offset="7" extends="VkFormat" extnumber="67" name="VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK"/>
+ <enum offset="8" extends="VkFormat" extnumber="67" name="VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK"/>
+ <enum offset="9" extends="VkFormat" extnumber="67" name="VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK"/>
+ <enum offset="10" extends="VkFormat" extnumber="67" name="VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK"/>
+ <enum offset="11" extends="VkFormat" extnumber="67" name="VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK"/>
+ <enum offset="12" extends="VkFormat" extnumber="67" name="VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK"/>
+ <enum offset="13" extends="VkFormat" extnumber="67" name="VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK"/>
+ </require>
+ <require comment="Promoted from VK_KHR_dynamic_rendering (extension 45)">
+ <command name="vkCmdBeginRendering"/>
+ <command name="vkCmdEndRendering"/>
+ <enum offset="0" extends="VkStructureType" extnumber="45" name="VK_STRUCTURE_TYPE_RENDERING_INFO"/>
+ <enum offset="1" extends="VkStructureType" extnumber="45" name="VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO"/>
+ <enum offset="2" extends="VkStructureType" extnumber="45" name="VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO"/>
+ <enum offset="3" extends="VkStructureType" extnumber="45" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES"/>
+ <enum offset="4" extends="VkStructureType" extnumber="45" name="VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDERING_INFO"/>
+ <enum offset="0" extends="VkAttachmentStoreOp" extnumber="302" name="VK_ATTACHMENT_STORE_OP_NONE"/>
+ <type name="VkRenderingInfo"/>
+ <type name="VkRenderingAttachmentInfo"/>
+ <type name="VkPipelineRenderingCreateInfo"/>
+ <type name="VkPhysicalDeviceDynamicRenderingFeatures"/>
+ <type name="VkCommandBufferInheritanceRenderingInfo"/>
+ <type name="VkRenderingFlags"/>
+ <type name="VkRenderingFlagBits"/>
+ </require>
+ <require comment="Promoted from VK_EXT_extended_dynamic_state (Feature struct is not promoted) (extension 268)">
+ <enum offset="0" extends="VkDynamicState" extnumber="268" name="VK_DYNAMIC_STATE_CULL_MODE"/>
+ <enum offset="1" extends="VkDynamicState" extnumber="268" name="VK_DYNAMIC_STATE_FRONT_FACE"/>
+ <enum offset="2" extends="VkDynamicState" extnumber="268" name="VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY"/>
+ <enum offset="3" extends="VkDynamicState" extnumber="268" name="VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT"/>
+ <enum offset="4" extends="VkDynamicState" extnumber="268" name="VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT"/>
+ <enum offset="5" extends="VkDynamicState" extnumber="268" name="VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE"/>
+ <enum offset="6" extends="VkDynamicState" extnumber="268" name="VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE"/>
+ <enum offset="7" extends="VkDynamicState" extnumber="268" name="VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE"/>
+ <enum offset="8" extends="VkDynamicState" extnumber="268" name="VK_DYNAMIC_STATE_DEPTH_COMPARE_OP"/>
+ <enum offset="9" extends="VkDynamicState" extnumber="268" name="VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE"/>
+ <enum offset="10" extends="VkDynamicState" extnumber="268" name="VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE"/>
+ <enum offset="11" extends="VkDynamicState" extnumber="268" name="VK_DYNAMIC_STATE_STENCIL_OP"/>
+ <command name="vkCmdSetCullMode"/>
+ <command name="vkCmdSetFrontFace"/>
+ <command name="vkCmdSetPrimitiveTopology"/>
+ <command name="vkCmdSetViewportWithCount"/>
+ <command name="vkCmdSetScissorWithCount"/>
+ <command name="vkCmdBindVertexBuffers2"/>
+ <command name="vkCmdSetDepthTestEnable"/>
+ <command name="vkCmdSetDepthWriteEnable"/>
+ <command name="vkCmdSetDepthCompareOp"/>
+ <command name="vkCmdSetDepthBoundsTestEnable"/>
+ <command name="vkCmdSetStencilTestEnable"/>
+ <command name="vkCmdSetStencilOp"/>
+ </require>
+ <require comment="Promoted from VK_KHR_shader_integer_dot_product (extension 281)">
+ <enum offset="0" extends="VkStructureType" extnumber="281" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES"/>
+ <enum offset="1" extends="VkStructureType" extnumber="281" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES"/>
+ <type name="VkPhysicalDeviceShaderIntegerDotProductFeatures"/>
+ <type name="VkPhysicalDeviceShaderIntegerDotProductProperties"/>
+ </require>
+ <require comment="Promoted from VK_EXT_texel_buffer_alignment (extension 282)">
+ <enum offset="1" extends="VkStructureType" extnumber="282" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES"/>
+ <type name="VkPhysicalDeviceTexelBufferAlignmentProperties"/>
+ </require>
+ <require comment="Promoted from VK_KHR_format_feature_flags2 (extension 361)">
+ <enum offset="0" extends="VkStructureType" extnumber="361" name="VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3"/>
+ <type name="VkFormatFeatureFlags2"/>
+ <type name="VkFormatFeatureFlagBits2"/>
+ <type name="VkFormatProperties3"/>
+ </require>
+ <require comment="Promoted from VK_EXT_extended_dynamic_state2 (Feature struct and optional state are not promoted) (extension 378)">
+ <enum offset="1" extends="VkDynamicState" extnumber="378" name="VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE"/>
+ <enum offset="2" extends="VkDynamicState" extnumber="378" name="VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE"/>
+ <enum offset="4" extends="VkDynamicState" extnumber="378" name="VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE"/>
+ <command name="vkCmdSetRasterizerDiscardEnable"/>
+ <command name="vkCmdSetDepthBiasEnable"/>
+ <command name="vkCmdSetPrimitiveRestartEnable"/>
+ </require>
+ <require comment="Promoted from VK_KHR_maintenance4 (extension 414)">
+ <enum offset="0" extends="VkStructureType" extnumber="414" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES"/>
+ <enum offset="1" extends="VkStructureType" extnumber="414" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES"/>
+ <enum offset="2" extends="VkStructureType" extnumber="414" name="VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS"/>
+ <enum offset="3" extends="VkStructureType" extnumber="414" name="VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS"/>
+ <enum value="0" extends="VkImageAspectFlagBits" name="VK_IMAGE_ASPECT_NONE"/>
+ <type name="VkPhysicalDeviceMaintenance4Features"/>
+ <type name="VkPhysicalDeviceMaintenance4Properties"/>
+ <type name="VkDeviceBufferMemoryRequirements"/>
+ <type name="VkDeviceImageMemoryRequirements"/>
+ <command name="vkGetDeviceBufferMemoryRequirements"/>
+ <command name="vkGetDeviceImageMemoryRequirements"/>
+ <command name="vkGetDeviceImageSparseMemoryRequirements"/>
+ </require>
+ </feature>
<extensions comment="Vulkan extension interface definitions">
<extension name="VK_KHR_surface" number="1" type="instance" author="KHR" contact="James Jones @cubanismo,Ian Elliott @ianelliottus" supported="vulkan">
@@ -10498,11 +13664,11 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_ANDROID_native_buffer" number="11" type="device" author="ANDROID" platform="android" contact="Jesse Hall @critsec" supported="disabled">
<require>
- <comment>VK_ANDROID_native_buffer is used between the Android Vulkan loader and drivers to implement the WSI extensions. It isn't exposed to applications and uses types that aren't part of Android's stable public API, so it is left disabled to keep it out of the standard Vulkan headers.</comment>
+ <comment>VK_ANDROID_native_buffer is used between the Android Vulkan loader and drivers to implement the WSI extensions. It is not exposed to applications and uses types that are not part of Android's stable public API, so it is left disabled to keep it out of the standard Vulkan headers.</comment>
<enum value="8" name="VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION"/>
<enum value="11" name="VK_ANDROID_NATIVE_BUFFER_NUMBER"/>
- <enum value="&quot;VK_ANDROID_native_buffer&quot;" name="VK_ANDROID_NATIVE_BUFFER_NAME"/>
- <enum name="VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME" alias="VK_ANDROID_NATIVE_BUFFER_NAME"/>
+ <enum value="&quot;VK_ANDROID_native_buffer&quot;" name="VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME"/>
+ <enum name="VK_ANDROID_NATIVE_BUFFER_NAME" alias="VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID"/>
<enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID"/>
<enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID"/>
@@ -10520,7 +13686,7 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_EXT_debug_report" number="12" type="instance" author="GOOGLE" contact="Courtney Goeltzenleuchter @courtney-g" specialuse="debugging" supported="vulkan" deprecatedby="VK_EXT_debug_utils">
<require>
- <enum value="9" name="VK_EXT_DEBUG_REPORT_SPEC_VERSION"/>
+ <enum value="10" name="VK_EXT_DEBUG_REPORT_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_debug_report&quot;" name="VK_EXT_DEBUG_REPORT_EXTENSION_NAME"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT"/>
<enum alias="VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT" comment="Backwards-compatible alias containing a typo"/>
@@ -10567,8 +13733,8 @@ typedef void <name>CAMetalLayer</name>;
<require>
<enum value="1" name="VK_IMG_FILTER_CUBIC_SPEC_VERSION"/>
<enum value="&quot;VK_IMG_filter_cubic&quot;" name="VK_IMG_FILTER_CUBIC_EXTENSION_NAME"/>
- <enum offset="0" extends="VkFilter" name="VK_FILTER_CUBIC_IMG"/>
- <enum bitpos="13" extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG" comment="Format can be filtered with VK_FILTER_CUBIC_IMG when being sampled"/>
+ <enum extends="VkFilter" name="VK_FILTER_CUBIC_IMG" alias="VK_FILTER_CUBIC_EXT"/>
+ <enum extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG" alias="VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_EXT" comment="Format can be filtered with VK_FILTER_CUBIC_IMG when being sampled"/>
</require>
</extension>
<extension name="VK_AMD_extension_17" number="17" author="AMD" contact="Daniel Rakos @drakos-amd" supported="disabled">
@@ -10628,43 +13794,131 @@ typedef void <name>CAMetalLayer</name>;
<command name="vkCmdDebugMarkerInsertEXT"/>
</require>
</extension>
- <extension name="VK_AMD_extension_24" number="24" author="AMD" contact="Daniel Rakos @drakos-amd" supported="disabled">
- <require>
- <enum value="0" name="VK_AMD_EXTENSION_24_SPEC_VERSION"/>
- <enum value="&quot;VK_AMD_extension_24&quot;" name="VK_AMD_EXTENSION_24_EXTENSION_NAME"/>
- <enum bitpos="6" extends="VkQueueFlagBits" name="VK_QUEUE_RESERVED_6_BIT_KHR"/>
- <enum bitpos="27" extends="VkPipelineStageFlagBits" name="VK_PIPELINE_STAGE_RESERVED_27_BIT_KHR"/>
- <enum bitpos="30" extends="VkAccessFlagBits" name="VK_ACCESS_RESERVED_30_BIT_KHR"/>
- <!-- Comment this out for now to avoid warning messages.
- The extension will probably change to avoid it.
- <enum bitpos="31" extends="VkAccessFlagBits" name="VK_ACCESS_RESERVED_31_BIT_KHR"/>
- -->
- <enum bitpos="15" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_RESERVED_15_BIT_KHR"/>
- <enum bitpos="16" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_RESERVED_16_BIT_KHR"/>
- <enum bitpos="13" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_RESERVED_13_BIT_KHR"/>
- <enum bitpos="14" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_RESERVED_14_BIT_KHR"/>
- <enum bitpos="15" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_RESERVED_15_BIT_KHR"/>
- <enum bitpos="27" extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_RESERVED_27_BIT_KHR"/>
- <enum bitpos="28" extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_RESERVED_28_BIT_KHR"/>
- <enum offset="8" extends="VkQueryType" name="VK_QUERY_TYPE_RESERVED_8"/>
- </require>
- </extension>
- <extension name="VK_AMD_extension_25" number="25" author="AMD" contact="Daniel Rakos @drakos-amd" supported="disabled">
- <require>
- <enum value="0" name="VK_AMD_EXTENSION_25_SPEC_VERSION"/>
- <enum value="&quot;VK_AMD_extension_25&quot;" name="VK_AMD_EXTENSION_25_EXTENSION_NAME"/>
- <enum bitpos="5" extends="VkQueueFlagBits" name="VK_QUEUE_RESERVED_5_BIT_KHR"/>
- <enum bitpos="26" extends="VkPipelineStageFlagBits" name="VK_PIPELINE_STAGE_RESERVED_26_BIT_KHR"/>
- <enum bitpos="28" extends="VkAccessFlagBits" name="VK_ACCESS_RESERVED_28_BIT_KHR"/>
- <enum bitpos="29" extends="VkAccessFlagBits" name="VK_ACCESS_RESERVED_29_BIT_KHR"/>
- <enum bitpos="13" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_RESERVED_13_BIT_KHR"/>
- <enum bitpos="14" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_RESERVED_14_BIT_KHR"/>
- <enum bitpos="10" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_RESERVED_10_BIT_KHR"/>
- <enum bitpos="11" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_RESERVED_11_BIT_KHR"/>
- <enum bitpos="12" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_RESERVED_12_BIT_KHR"/>
- <enum bitpos="25" extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_RESERVED_25_BIT_KHR"/>
- <enum bitpos="26" extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_RESERVED_26_BIT_KHR"/>
- <enum offset="4" extends="VkQueryType" name="VK_QUERY_TYPE_RESERVED_4"/>
+ <extension name="VK_KHR_video_queue" number="24" type="device" requires="VK_KHR_get_physical_device_properties2,VK_KHR_synchronization2" author="KHR" contact="Tony Zlatinski @tzlatinski" provisional="true" platform="provisional" supported="vulkan" requiresCore="1.1">
+ <require>
+ <enum value="4" name="VK_KHR_VIDEO_QUEUE_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_video_queue&quot;" name="VK_KHR_VIDEO_QUEUE_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_PROFILE_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_CAPABILITIES_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_GET_MEMORY_PROPERTIES_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="4" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_BIND_MEMORY_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="5" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_SESSION_CREATE_INFO_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="6" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="7" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_UPDATE_INFO_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="8" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_BEGIN_CODING_INFO_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="9" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_END_CODING_INFO_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="10" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_CODING_CONTROL_INFO_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="11" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="12" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_QUEUE_FAMILY_PROPERTIES_2_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="13" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_PROFILES_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="14" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_FORMAT_INFO_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="15" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_FORMAT_PROPERTIES_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="16" extends="VkStructureType" name="VK_STRUCTURE_TYPE_QUEUE_FAMILY_QUERY_RESULT_STATUS_PROPERTIES_2_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+
+ <enum offset="0" extends="VkObjectType" name="VK_OBJECT_TYPE_VIDEO_SESSION_KHR" comment="VkVideoSessionKHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="1" extends="VkObjectType" name="VK_OBJECT_TYPE_VIDEO_SESSION_PARAMETERS_KHR" comment="VkVideoSessionParametersKHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+
+ <enum offset="0" extends="VkQueryType" name="VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="4" extends="VkQueryResultFlagBits" name="VK_QUERY_RESULT_WITH_STATUS_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+
+ <enum offset="0" extends="VkResult" dir="-" name="VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="1" extends="VkResult" dir="-" name="VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="2" extends="VkResult" dir="-" name="VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="3" extends="VkResult" dir="-" name="VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="4" extends="VkResult" dir="-" name="VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="5" extends="VkResult" dir="-" name="VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+
+ <type name="VkVideoSessionKHR"/>
+ <type name="VkVideoSessionParametersKHR"/>
+
+ <type name="VkVideoCodecOperationFlagBitsKHR"/>
+ <type name="VkVideoCodecOperationFlagsKHR"/>
+ <type name="VkVideoChromaSubsamplingFlagBitsKHR"/>
+ <type name="VkVideoChromaSubsamplingFlagsKHR"/>
+ <type name="VkVideoComponentBitDepthFlagBitsKHR"/>
+ <type name="VkVideoComponentBitDepthFlagsKHR"/>
+ <type name="VkVideoCapabilityFlagBitsKHR"/>
+ <type name="VkVideoCapabilityFlagsKHR"/>
+ <type name="VkVideoSessionCreateFlagBitsKHR"/>
+ <type name="VkVideoSessionCreateFlagsKHR"/>
+ <type name="VkVideoBeginCodingFlagsKHR"/>
+ <type name="VkVideoEndCodingFlagsKHR"/>
+ <type name="VkVideoCodingControlFlagBitsKHR"/>
+ <type name="VkVideoCodingControlFlagsKHR"/>
+ <type name="VkVideoCodingQualityPresetFlagBitsKHR"/>
+ <type name="VkVideoCodingQualityPresetFlagsKHR"/>
+
+ <type name="VkQueueFamilyQueryResultStatusProperties2KHR"/>
+ <type name="VkQueryResultStatusKHR"/>
+
+ <type name="VkVideoQueueFamilyProperties2KHR"/>
+ <type name="VkVideoProfileKHR"/>
+ <type name="VkVideoProfilesKHR"/>
+ <type name="VkVideoCapabilitiesKHR"/>
+ <type name="VkPhysicalDeviceVideoFormatInfoKHR"/>
+ <type name="VkVideoFormatPropertiesKHR"/>
+ <type name="VkVideoPictureResourceKHR"/>
+ <type name="VkVideoReferenceSlotKHR"/>
+ <type name="VkVideoGetMemoryPropertiesKHR"/>
+ <type name="VkVideoBindMemoryKHR"/>
+ <type name="VkVideoSessionCreateInfoKHR"/>
+ <type name="VkVideoSessionParametersCreateInfoKHR"/>
+ <type name="VkVideoSessionParametersUpdateInfoKHR"/>
+ <type name="VkVideoBeginCodingInfoKHR"/>
+ <type name="VkVideoEndCodingInfoKHR"/>
+ <type name="VkVideoCodingControlInfoKHR"/>
+
+ <command name="vkGetPhysicalDeviceVideoCapabilitiesKHR"/>
+ <command name="vkGetPhysicalDeviceVideoFormatPropertiesKHR"/>
+
+ <command name="vkCreateVideoSessionKHR"/>
+ <command name="vkDestroyVideoSessionKHR"/>
+ <command name="vkGetVideoSessionMemoryRequirementsKHR"/>
+ <command name="vkBindVideoSessionMemoryKHR"/>
+ <command name="vkCreateVideoSessionParametersKHR"/>
+ <command name="vkUpdateVideoSessionParametersKHR"/>
+ <command name="vkDestroyVideoSessionParametersKHR"/>
+ <command name="vkCmdBeginVideoCodingKHR"/>
+ <command name="vkCmdEndVideoCodingKHR"/>
+ <command name="vkCmdControlVideoCodingKHR"/>
+ </require>
+ </extension>
+ <extension name="VK_KHR_video_decode_queue" number="25" type="device" requires="VK_KHR_video_queue,VK_KHR_synchronization2" author="KHR" contact="jake.beju@amd.com" provisional="true" platform="provisional" supported="vulkan">
+ <require>
+ <enum value="4" name="VK_KHR_VIDEO_DECODE_QUEUE_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_video_decode_queue&quot;" name="VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_DECODE_INFO_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_DECODE_CAPABILITIES_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="5" extends="VkQueueFlagBits" name="VK_QUEUE_VIDEO_DECODE_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <!-- VkPipelineStageFlagBits bitpos="26" is reserved by this extension, but not used -->
+ <enum bitpos="26" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="35" extends="VkAccessFlagBits2" name="VK_ACCESS_2_VIDEO_DECODE_READ_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS" />
+ <enum bitpos="36" extends="VkAccessFlagBits2" name="VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="13" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_VIDEO_DECODE_SRC_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="14" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_VIDEO_DECODE_DST_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="10" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="11" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_VIDEO_DECODE_SRC_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="12" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="25" extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_VIDEO_DECODE_OUTPUT_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="26" extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_VIDEO_DECODE_DPB_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="0" extends="VkImageLayout" name="VK_IMAGE_LAYOUT_VIDEO_DECODE_DST_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="1" extends="VkImageLayout" name="VK_IMAGE_LAYOUT_VIDEO_DECODE_SRC_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="2" extends="VkImageLayout" name="VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+
+ <type name="VkVideoDecodeCapabilityFlagBitsKHR"/>
+ <type name="VkVideoDecodeCapabilityFlagsKHR"/>
+ <type name="VkVideoDecodeCapabilitiesKHR"/>
+
+ <type name="VkVideoDecodeFlagBitsKHR"/>
+ <type name="VkVideoDecodeFlagsKHR"/>
+
+ <type name="VkVideoDecodeInfoKHR"/>
+ <command name="vkCmdDecodeVideoKHR"/>
+ </require>
+ <require extension="VK_KHR_format_feature_flags2">
+ <enum bitpos="25" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_VIDEO_DECODE_OUTPUT_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="26" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_VIDEO_DECODE_DPB_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
</require>
</extension>
<extension name="VK_AMD_gcn_shader" number="26" type="device" author="AMD" contact="Dominik Witczak @dominikwitczakamd" supported="vulkan">
@@ -10688,7 +13942,7 @@ typedef void <name>CAMetalLayer</name>;
<extension name="VK_EXT_extension_28" number="28" author="NV" contact="Piers Daniell @pdaniell-nv" supported="disabled">
<require>
<enum value="0" name="VK_EXT_EXTENSION_28_SPEC_VERSION"/>
- <enum value="&quot;VK_NV_extension_28&quot;" name="VK_EXT_EXTENSION_28_EXTENSION_NAME"/>
+ <enum value="&quot;VK_EXT_extension_28&quot;" name="VK_EXT_EXTENSION_28_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_EXT_transform_feedback" number="29" type="device" author="NV" contact="Piers Daniell @pdaniell-nv" specialuse="glemulation,d3demulation,devtools" supported="vulkan" requires="VK_KHR_get_physical_device_properties2">
@@ -10708,8 +13962,8 @@ typedef void <name>CAMetalLayer</name>;
<enum offset="4" extends="VkQueryType" name="VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT"/>
- <enum bitpos="11" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT"/>
- <enum bitpos="12" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT"/>
+ <enum bitpos="11" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT"/>
+ <enum bitpos="12" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT"/>
<enum bitpos="25" extends="VkAccessFlagBits" name="VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT"/>
<enum bitpos="26" extends="VkAccessFlagBits" name="VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT"/>
@@ -10724,13 +13978,30 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkPipelineRasterizationStateStreamCreateFlagsEXT"/>
</require>
</extension>
- <extension name="VK_NVX_extension_30" number="30" author="NVX" contact="Jeff Juliano @jjulianoatnv" supported="disabled">
+ <extension name="VK_NVX_binary_import" number="30" type="device" author="NVX" contact="Eric Werness @ewerness-nv,Liam Middlebrook @liam-middlebrook" supported="vulkan">
<require>
- <enum value="0" name="VK_NVX_EXTENSION_30_SPEC_VERSION"/>
- <enum value="&quot;VK_NVX_extension_30&quot;" name="VK_NVX_EXTENSION_30_EXTENSION_NAME"/>
+ <enum value="1" name="VK_NVX_BINARY_IMPORT_SPEC_VERSION"/>
+ <enum value="&quot;VK_NVX_binary_import&quot;" name="VK_NVX_BINARY_IMPORT_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_CU_MODULE_CREATE_INFO_NVX"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_CU_FUNCTION_CREATE_INFO_NVX"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_CU_LAUNCH_INFO_NVX"/>
+ <enum offset="0" extends="VkObjectType" name="VK_OBJECT_TYPE_CU_MODULE_NVX"/>
+ <enum offset="1" extends="VkObjectType" name="VK_OBJECT_TYPE_CU_FUNCTION_NVX"/>
+ <enum offset="0" extends="VkDebugReportObjectTypeEXT" name="VK_DEBUG_REPORT_OBJECT_TYPE_CU_MODULE_NVX_EXT"/>
+ <enum offset="1" extends="VkDebugReportObjectTypeEXT" name="VK_DEBUG_REPORT_OBJECT_TYPE_CU_FUNCTION_NVX_EXT"/>
+ <type name="VkCuModuleNVX"/>
+ <type name="VkCuFunctionNVX"/>
+ <type name="VkCuModuleCreateInfoNVX"/>
+ <type name="VkCuFunctionCreateInfoNVX"/>
+ <type name="VkCuLaunchInfoNVX"/>
+ <command name="vkCreateCuModuleNVX"/>
+ <command name="vkCreateCuFunctionNVX"/>
+ <command name="vkDestroyCuModuleNVX"/>
+ <command name="vkDestroyCuFunctionNVX"/>
+ <command name="vkCmdCuLaunchKernelNVX"/>
</require>
</extension>
- <extension name="VK_NVX_image_view_handle" number="31" type="device" author="NVX" contact="Eric Werness @ewerness" supported="vulkan">
+ <extension name="VK_NVX_image_view_handle" number="31" type="device" author="NVX" contact="Eric Werness @ewerness-nv" supported="vulkan">
<require>
<enum value="2" name="VK_NVX_IMAGE_VIEW_HANDLE_SPEC_VERSION"/>
<enum value="&quot;VK_NVX_image_view_handle&quot;" name="VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME"/>
@@ -10786,22 +14057,112 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_AMD_shader_ballot&quot;" name="VK_AMD_SHADER_BALLOT_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_AMD_extension_39" number="39" author="AMD" contact="Daniel Rakos @drakos-amd" supported="disabled">
- <require>
- <enum value="0" name="VK_AMD_EXTENSION_39_SPEC_VERSION"/>
- <enum value="&quot;VK_AMD_extension_39&quot;" name="VK_AMD_EXTENSION_39_EXTENSION_NAME"/>
- </require>
- </extension>
- <extension name="VK_AMD_extension_40" number="40" author="AMD" contact="Daniel Rakos @drakos-amd" supported="disabled">
- <require>
- <enum value="0" name="VK_AMD_EXTENSION_40_SPEC_VERSION"/>
- <enum value="&quot;VK_AMD_extension_40&quot;" name="VK_AMD_EXTENSION_40_EXTENSION_NAME"/>
- </require>
- </extension>
- <extension name="VK_AMD_extension_41" number="41" author="AMD" contact="Daniel Rakos @drakos-amd" supported="disabled">
- <require>
- <enum value="0" name="VK_AMD_EXTENSION_41_SPEC_VERSION"/>
- <enum value="&quot;VK_AMD_extension_41&quot;" name="VK_AMD_EXTENSION_41_EXTENSION_NAME"/>
+ <extension name="VK_EXT_video_encode_h264" number="39" type="device" requires="VK_KHR_video_encode_queue" author="KHR" contact="Ahmed Abdelkhalek @aabdelkh" provisional="true" platform="provisional" supported="vulkan">
+ <require>
+ <enum value="7" name="VK_EXT_VIDEO_ENCODE_H264_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_video_encode_h264&quot;" name="VK_EXT_VIDEO_ENCODE_H264_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_CAPABILITIES_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_VCL_FRAME_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="4" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_DPB_SLOT_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="5" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_NALU_SLICE_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="6" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_EMIT_PICTURE_PARAMETERS_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="7" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PROFILE_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="8" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_RATE_CONTROL_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="9" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_RATE_CONTROL_LAYER_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="10" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_REFERENCE_LISTS_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="16" extends="VkVideoCodecOperationFlagBitsKHR" name="VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+
+ <type name="VkVideoEncodeH264CapabilityFlagBitsEXT"/>
+ <type name="VkVideoEncodeH264CapabilityFlagsEXT"/>
+ <type name="VkVideoEncodeH264InputModeFlagBitsEXT"/>
+ <type name="VkVideoEncodeH264InputModeFlagsEXT"/>
+ <type name="VkVideoEncodeH264OutputModeFlagBitsEXT"/>
+ <type name="VkVideoEncodeH264OutputModeFlagsEXT"/>
+ <type name="VkVideoEncodeH264CapabilitiesEXT"/>
+ <type name="VkVideoEncodeH264SessionParametersCreateInfoEXT"/>
+ <type name="VkVideoEncodeH264SessionParametersAddInfoEXT"/>
+ <type name="VkVideoEncodeH264VclFrameInfoEXT"/>
+ <type name="VkVideoEncodeH264ReferenceListsEXT"/>
+ <type name="VkVideoEncodeH264EmitPictureParametersEXT"/>
+ <type name="VkVideoEncodeH264DpbSlotInfoEXT"/>
+ <type name="VkVideoEncodeH264NaluSliceEXT"/>
+ <type name="VkVideoEncodeH264ProfileEXT"/>
+ <type name="VkVideoEncodeH264RateControlInfoEXT"/>
+ <type name="VkVideoEncodeH264RateControlStructureFlagBitsEXT"/>
+ <type name="VkVideoEncodeH264RateControlStructureFlagsEXT"/>
+ <type name="VkVideoEncodeH264RateControlLayerInfoEXT"/>
+ <type name="VkVideoEncodeH264QpEXT"/>
+ <type name="VkVideoEncodeH264FrameSizeEXT"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_video_encode_h265" number="40" type="device" requires="VK_KHR_video_encode_queue" author="KHR" contact="Ahmed Abdelkhalek @aabdelkh" provisional="true" platform="provisional" supported="vulkan">
+ <require>
+ <enum value="7" name="VK_EXT_VIDEO_ENCODE_H265_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_video_encode_h265&quot;" name="VK_EXT_VIDEO_ENCODE_H265_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_CAPABILITIES_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_SESSION_PARAMETERS_CREATE_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_SESSION_PARAMETERS_ADD_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_VCL_FRAME_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="4" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_DPB_SLOT_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="5" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_NALU_SLICE_SEGMENT_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="6" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_EMIT_PICTURE_PARAMETERS_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="7" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_PROFILE_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="8" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_REFERENCE_LISTS_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="9" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_RATE_CONTROL_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="10" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_RATE_CONTROL_LAYER_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="17" extends="VkVideoCodecOperationFlagBitsKHR" name="VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+
+ <type name="VkVideoEncodeH265CapabilityFlagBitsEXT"/>
+ <type name="VkVideoEncodeH265CapabilityFlagsEXT"/>
+ <type name="VkVideoEncodeH265InputModeFlagBitsEXT"/>
+ <type name="VkVideoEncodeH265InputModeFlagsEXT"/>
+ <type name="VkVideoEncodeH265OutputModeFlagBitsEXT"/>
+ <type name="VkVideoEncodeH265OutputModeFlagsEXT"/>
+
+ <type name="VkVideoEncodeH265CtbSizeFlagBitsEXT"/>
+ <type name="VkVideoEncodeH265CtbSizeFlagsEXT"/>
+ <type name="VkVideoEncodeH265TransformBlockSizeFlagBitsEXT"/>
+ <type name="VkVideoEncodeH265TransformBlockSizeFlagsEXT"/>
+ <type name="VkVideoEncodeH265CapabilitiesEXT"/>
+ <type name="VkVideoEncodeH265SessionParametersCreateInfoEXT"/>
+ <type name="VkVideoEncodeH265SessionParametersAddInfoEXT"/>
+ <type name="VkVideoEncodeH265VclFrameInfoEXT"/>
+ <type name="VkVideoEncodeH265EmitPictureParametersEXT"/>
+ <type name="VkVideoEncodeH265DpbSlotInfoEXT"/>
+ <type name="VkVideoEncodeH265NaluSliceSegmentEXT"/>
+ <type name="VkVideoEncodeH265ProfileEXT"/>
+ <type name="VkVideoEncodeH265ReferenceListsEXT"/>
+ <type name="VkVideoEncodeH265RateControlInfoEXT"/>
+ <type name="VkVideoEncodeH265RateControlStructureFlagBitsEXT"/>
+ <type name="VkVideoEncodeH265RateControlStructureFlagsEXT"/>
+ <type name="VkVideoEncodeH265RateControlLayerInfoEXT"/>
+ <type name="VkVideoEncodeH265QpEXT"/>
+ <type name="VkVideoEncodeH265FrameSizeEXT"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_video_decode_h264" number="41" type="device" requires="VK_KHR_video_decode_queue" author="KHR" contact="peter.fang@amd.com" provisional="true" platform="provisional" supported="vulkan">
+ <require>
+ <enum value="5" name="VK_EXT_VIDEO_DECODE_H264_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_video_decode_h264&quot;" name="VK_EXT_VIDEO_DECODE_H264_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_CAPABILITIES_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PICTURE_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_MVC_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PROFILE_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="4" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="5" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="6" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_DPB_SLOT_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="0" extends="VkVideoCodecOperationFlagBitsKHR" name="VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <type name="VkVideoDecodeH264PictureLayoutFlagBitsEXT"/>
+ <type name="VkVideoDecodeH264PictureLayoutFlagsEXT"/>
+ <type name="VkVideoDecodeH264ProfileEXT"/>
+ <type name="VkVideoDecodeH264CapabilitiesEXT"/>
+ <type name="VkVideoDecodeH264SessionParametersCreateInfoEXT"/>
+ <type name="VkVideoDecodeH264SessionParametersAddInfoEXT"/>
+ <type name="VkVideoDecodeH264PictureInfoEXT"/>
+ <type name="VkVideoDecodeH264MvcEXT"/>
+ <type name="VkVideoDecodeH264DpbSlotInfoEXT"/>
</require>
</extension>
<extension name="VK_AMD_texture_gather_bias_lod" number="42" author="AMD" contact="Rex Xu @amdrexu" supported="vulkan" type="device" requires="VK_KHR_get_physical_device_properties2">
@@ -10828,10 +14189,49 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_AMD_extension_44&quot;" name="VK_AMD_EXTENSION_44_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_AMD_extension_45" number="45" author="AMD" contact="Daniel Rakos @drakos-amd" supported="disabled">
- <require>
- <enum value="0" name="VK_AMD_EXTENSION_45_SPEC_VERSION"/>
- <enum value="&quot;VK_AMD_extension_45&quot;" name="VK_AMD_EXTENSION_45_EXTENSION_NAME"/>
+ <extension name="VK_KHR_dynamic_rendering" number="45" author="KHR" type="device" requires="VK_KHR_depth_stencil_resolve,VK_KHR_get_physical_device_properties2" contact="Tobias Hector @tobski" supported="vulkan" promotedto="VK_VERSION_1_3">
+ <require>
+ <enum value="1" name="VK_KHR_DYNAMIC_RENDERING_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_dynamic_rendering&quot;" name="VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME"/>
+ <command name="vkCmdBeginRenderingKHR"/>
+ <command name="vkCmdEndRenderingKHR"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_RENDERING_INFO_KHR" alias="VK_STRUCTURE_TYPE_RENDERING_INFO"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR" alias="VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR" alias="VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDERING_INFO_KHR" alias="VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDERING_INFO"/>
+ <enum extends="VkAttachmentStoreOp" name="VK_ATTACHMENT_STORE_OP_NONE_KHR" alias="VK_ATTACHMENT_STORE_OP_NONE"/>
+ <type name="VkRenderingInfoKHR"/>
+ <type name="VkRenderingAttachmentInfoKHR"/>
+ <type name="VkPipelineRenderingCreateInfoKHR"/>
+ <type name="VkPhysicalDeviceDynamicRenderingFeaturesKHR"/>
+ <type name="VkCommandBufferInheritanceRenderingInfoKHR"/>
+ <type name="VkRenderingFlagsKHR"/>
+ <type name="VkRenderingFlagBitsKHR"/>
+ </require>
+ <require extension="VK_KHR_fragment_shading_rate">
+ <enum bitpos="21" extends="VkPipelineCreateFlagBits" name="VK_PIPELINE_CREATE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR"/>
+ <enum alias="VK_PIPELINE_CREATE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR" extends="VkPipelineCreateFlagBits" name="VK_PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR" comment="Backwards-compatible alias containing a typo"/>
+ <enum offset="6" extends="VkStructureType" extnumber="45" name="VK_STRUCTURE_TYPE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR"/>
+ <type name="VkRenderingFragmentShadingRateAttachmentInfoKHR"/>
+ </require>
+ <require extension="VK_EXT_fragment_density_map">
+ <enum bitpos="22" extends="VkPipelineCreateFlagBits" name="VK_PIPELINE_CREATE_RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT"/>
+ <enum alias="VK_PIPELINE_CREATE_RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT" extends="VkPipelineCreateFlagBits" name="VK_PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT" comment="Backwards-compatible alias containing a typo"/>
+ <enum offset="7" extends="VkStructureType" extnumber="45" name="VK_STRUCTURE_TYPE_RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_INFO_EXT"/>
+ <type name="VkRenderingFragmentDensityMapAttachmentInfoEXT"/>
+ </require>
+ <require extension="VK_AMD_mixed_attachment_samples">
+ <enum offset="8" extends="VkStructureType" extnumber="45" name="VK_STRUCTURE_TYPE_ATTACHMENT_SAMPLE_COUNT_INFO_AMD"/>
+ <type name="VkAttachmentSampleCountInfoAMD"/>
+ </require>
+ <require extension="VK_NV_framebuffer_mixed_samples">
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_ATTACHMENT_SAMPLE_COUNT_INFO_NV" alias="VK_STRUCTURE_TYPE_ATTACHMENT_SAMPLE_COUNT_INFO_AMD"/>
+ <type name="VkAttachmentSampleCountInfoNV"/>
+ </require>
+ <require extension="VK_NVX_multiview_per_view_attributes">
+ <enum offset="9" extends="VkStructureType" extnumber="45" name="VK_STRUCTURE_TYPE_MULTIVIEW_PER_VIEW_ATTRIBUTES_INFO_NVX"/>
+ <type name="VkMultiviewPerViewAttributesInfoNVX"/>
</require>
</extension>
<extension name="VK_AMD_extension_46" number="46" author="AMD" contact="Daniel Rakos @drakos-amd" supported="disabled">
@@ -11088,48 +14488,58 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_EXT_shader_subgroup_vote&quot;" name="VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_EXT_texture_compression_astc_hdr" number="67" type="device" author="ARM" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" requires="VK_KHR_get_physical_device_properties2" supported="vulkan">
+ <extension name="VK_EXT_texture_compression_astc_hdr" number="67" type="device" author="ARM" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" requires="VK_KHR_get_physical_device_properties2" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
- <enum value="1" name="VK_EXT_TEXTURE_COMPRESSION_ASTC_HDR_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_texture_compression_astc_hdr&quot;" name="VK_EXT_TEXTURE_COMPRESSION_ASTC_HDR_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES_EXT"/>
+ <enum value="1" name="VK_EXT_TEXTURE_COMPRESSION_ASTC_HDR_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_texture_compression_astc_hdr&quot;" name="VK_EXT_TEXTURE_COMPRESSION_ASTC_HDR_EXTENSION_NAME"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES_EXT" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES"/>
<type name="VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT"/>
- <enum extends="VkFormat" extnumber="67" offset="0" name="VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT"/>
- <enum extends="VkFormat" extnumber="67" offset="1" name="VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT"/>
- <enum extends="VkFormat" extnumber="67" offset="2" name="VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT"/>
- <enum extends="VkFormat" extnumber="67" offset="3" name="VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT"/>
- <enum extends="VkFormat" extnumber="67" offset="4" name="VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT"/>
- <enum extends="VkFormat" extnumber="67" offset="5" name="VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT"/>
- <enum extends="VkFormat" extnumber="67" offset="6" name="VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT"/>
- <enum extends="VkFormat" extnumber="67" offset="7" name="VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT"/>
- <enum extends="VkFormat" extnumber="67" offset="8" name="VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT"/>
- <enum extends="VkFormat" extnumber="67" offset="9" name="VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT"/>
- <enum extends="VkFormat" extnumber="67" offset="10" name="VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT"/>
- <enum extends="VkFormat" extnumber="67" offset="11" name="VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT"/>
- <enum extends="VkFormat" extnumber="67" offset="12" name="VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT"/>
- <enum extends="VkFormat" extnumber="67" offset="13" name="VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT"/>
+ <enum extends="VkFormat" name="VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT" alias="VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK"/>
+ <enum extends="VkFormat" name="VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT" alias="VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK"/>
+ <enum extends="VkFormat" name="VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT" alias="VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK"/>
+ <enum extends="VkFormat" name="VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT" alias="VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK"/>
+ <enum extends="VkFormat" name="VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT" alias="VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK"/>
+ <enum extends="VkFormat" name="VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT" alias="VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK"/>
+ <enum extends="VkFormat" name="VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT" alias="VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK"/>
+ <enum extends="VkFormat" name="VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT" alias="VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK"/>
+ <enum extends="VkFormat" name="VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT" alias="VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK"/>
+ <enum extends="VkFormat" name="VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT" alias="VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK"/>
+ <enum extends="VkFormat" name="VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT" alias="VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK"/>
+ <enum extends="VkFormat" name="VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT" alias="VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK"/>
+ <enum extends="VkFormat" name="VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT" alias="VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK"/>
+ <enum extends="VkFormat" name="VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT" alias="VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK"/>
</require>
</extension>
<extension name="VK_EXT_astc_decode_mode" number="68" type="device" author="ARM" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" requires="VK_KHR_get_physical_device_properties2" supported="vulkan">
<require>
- <enum value="1" name="VK_EXT_ASTC_DECODE_MODE_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_astc_decode_mode&quot;" name="VK_EXT_ASTC_DECODE_MODE_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_VIEW_ASTC_DECODE_MODE_EXT"/>
- <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ASTC_DECODE_FEATURES_EXT"/>
+ <enum value="1" name="VK_EXT_ASTC_DECODE_MODE_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_astc_decode_mode&quot;" name="VK_EXT_ASTC_DECODE_MODE_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_VIEW_ASTC_DECODE_MODE_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ASTC_DECODE_FEATURES_EXT"/>
<type name="VkImageViewASTCDecodeModeEXT"/>
<type name="VkPhysicalDeviceASTCDecodeFeaturesEXT"/>
</require>
</extension>
- <extension name="VK_IMG_extension_69" number="69" type="device" author="IMG" contact="Tobias Hector @tobski" supported="disabled">
+ <extension name="VK_EXT_pipeline_robustness" requires="VK_KHR_get_physical_device_properties2" number="69" type="device" author="IMG" contact="Jarred Davies" supported="vulkan">
<require>
- <enum value="0" name="VK_IMG_EXTENSION_69_SPEC_VERSION"/>
- <enum value="&quot;VK_IMG_extension_69&quot;" name="VK_IMG_EXTENSION_69_EXTENSION_NAME"/>
+ <enum value="1" name="VK_EXT_PIPELINE_ROBUSTNESS_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_pipeline_robustness&quot;" name="VK_EXT_PIPELINE_ROBUSTNESS_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PIPELINE_ROBUSTNESS_CREATE_INFO_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_FEATURES_EXT"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_PROPERTIES_EXT"/>
+ <type name="VkPhysicalDevicePipelineRobustnessFeaturesEXT"/>
+ <type name="VkPhysicalDevicePipelineRobustnessPropertiesEXT"/>
+ <type name="VkPipelineRobustnessCreateInfoEXT"/>
+ <type name="VkPipelineRobustnessBufferBehaviorEXT"/>
+ <type name="VkPipelineRobustnessImageBehaviorEXT"/>
</require>
</extension>
<extension name="VK_KHR_maintenance1" number="70" type="device" author="KHR" contact="Piers Daniell @pdaniell-nv" supported="vulkan" promotedto="VK_VERSION_1_1">
<require>
- <enum value="2" name="VK_KHR_MAINTENANCE1_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_maintenance1&quot;" name="VK_KHR_MAINTENANCE1_EXTENSION_NAME"/>
+ <enum value="2" name="VK_KHR_MAINTENANCE_1_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_maintenance1&quot;" name="VK_KHR_MAINTENANCE_1_EXTENSION_NAME"/>
+ <enum alias="VK_KHR_MAINTENANCE_1_SPEC_VERSION" name="VK_KHR_MAINTENANCE1_SPEC_VERSION" comment="Backwards-compatible alias containing a typo"/>
+ <enum alias="VK_KHR_MAINTENANCE_1_EXTENSION_NAME" name="VK_KHR_MAINTENANCE1_EXTENSION_NAME" comment="Backwards-compatible alias containing a typo"/>
<enum extends="VkResult" name="VK_ERROR_OUT_OF_POOL_MEMORY_KHR" alias="VK_ERROR_OUT_OF_POOL_MEMORY"/>
<enum extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR" alias="VK_FORMAT_FEATURE_TRANSFER_SRC_BIT"/>
<enum extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR" alias="VK_FORMAT_FEATURE_TRANSFER_DST_BIT"/>
@@ -11339,10 +14749,10 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_KHR_shader_float16_int8" number="83" type="device" requires="VK_KHR_get_physical_device_properties2" author="KHR" contact="Alexander Galazin @alegal-arm" supported="vulkan" promotedto="VK_VERSION_1_2">
<require>
- <enum value="1" name="VK_KHR_SHADER_FLOAT16_INT8_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_shader_float16_int8&quot;" name="VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME"/>
- <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES"/>
- <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES"/>
+ <enum value="1" name="VK_KHR_SHADER_FLOAT16_INT8_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_shader_float16_int8&quot;" name="VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES"/>
<type name="VkPhysicalDeviceShaderFloat16Int8FeaturesKHR"/>
<type name="VkPhysicalDeviceFloat16Int8FeaturesKHR"/>
</require>
@@ -11357,7 +14767,7 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_KHR_incremental_present" number="85" type="device" author="KHR" requires="VK_KHR_swapchain" contact="Ian Elliott @ianelliottus" supported="vulkan">
<require>
- <enum value="1" name="VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION"/>
+ <enum value="2" name="VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION"/>
<enum value="&quot;VK_KHR_incremental_present&quot;" name="VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR"/>
<type name="VkPresentRegionsKHR"/>
@@ -11467,7 +14877,11 @@ typedef void <name>CAMetalLayer</name>;
<command name="vkGetPastPresentationTimingGOOGLE"/>
</require>
</extension>
- <extension name="RESERVED_DO_NOT_USE_94" number="94" supported="disabled" comment="Used for functionality subsumed into Vulkan 1.1 and not published as an extension">
+ <extension name="VK_RESERVED_do_not_use_94" number="94" supported="disabled" comment="Used for functionality subsumed into Vulkan 1.1 and not published as an extension">
+ <require>
+ <enum value="1" name="VK_RESERVED_DO_NOT_USE_94_SPEC_VERSION"/>
+ <enum value="&quot;VK_RESERVED_do_not_use_94&quot;" name="VK_RESERVED_DO_NOT_USE_94_EXTENSION_NAME"/>
+ </require>
</extension>
<extension name="VK_NV_sample_mask_override_coverage" number="95" type="device" author="NV" contact="Piers Daniell @pdaniell-nv" supported="vulkan">
<require>
@@ -11488,8 +14902,10 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_NV_viewport_array2" number="97" type="device" author="NV" contact="Daniel Koch @dgkoch" supported="vulkan">
<require>
- <enum value="1" name="VK_NV_VIEWPORT_ARRAY2_SPEC_VERSION"/>
- <enum value="&quot;VK_NV_viewport_array2&quot;" name="VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME"/>
+ <enum value="1" name="VK_NV_VIEWPORT_ARRAY_2_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_viewport_array2&quot;" name="VK_NV_VIEWPORT_ARRAY_2_EXTENSION_NAME"/>
+ <enum alias="VK_NV_VIEWPORT_ARRAY_2_SPEC_VERSION" name="VK_NV_VIEWPORT_ARRAY2_SPEC_VERSION" comment="Backwards-compatible alias containing a typo"/>
+ <enum alias="VK_NV_VIEWPORT_ARRAY_2_EXTENSION_NAME" name="VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME" comment="Backwards-compatible alias containing a typo"/>
</require>
</extension>
<extension name="VK_NVX_multiview_per_view_attributes" number="98" type="device" requires="VK_KHR_multiview" author="NVX" contact="Jeff Bolz @jeffbolznv" supported="vulkan">
@@ -11580,7 +14996,7 @@ typedef void <name>CAMetalLayer</name>;
<enum offset="12" extends="VkColorSpaceKHR" name="VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT"/>
<enum offset="13" extends="VkColorSpaceKHR" name="VK_COLOR_SPACE_PASS_THROUGH_EXT"/>
<enum offset="14" extends="VkColorSpaceKHR" name="VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT"/>
- <enum extends="VkColorSpaceKHR" name="VK_COLOR_SPACE_DCI_P3_LINEAR_EXT" alias="VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT" comment="Deprecated name for backwards compatibility"/>
+ <enum extends="VkColorSpaceKHR" name="VK_COLOR_SPACE_DCI_P3_LINEAR_EXT" alias="VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT" comment="Backwards-compatible alias containing a typo"/>
</require>
</extension>
<extension name="VK_EXT_hdr_metadata" number="106" type="device" requires="VK_KHR_swapchain" author="GOOGLE" contact="Courtney Goeltzenleuchter @courtney-g" supported="vulkan">
@@ -11620,7 +15036,7 @@ typedef void <name>CAMetalLayer</name>;
<enum extends="VkFramebufferCreateFlagBits" name="VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT_KHR" alias="VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT"/>
</require>
</extension>
- <extension name="VK_KHR_create_renderpass2" requires="VK_KHR_multiview,VK_KHR_maintenance2" number="110" contact="Tobias Hector @tobias" type="device" supported="vulkan" promotedto="VK_VERSION_1_2">
+ <extension name="VK_KHR_create_renderpass2" requires="VK_KHR_multiview,VK_KHR_maintenance2" number="110" author="KHR" contact="Tobias Hector @tobias" type="device" supported="vulkan" promotedto="VK_VERSION_1_2">
<require>
<enum value="1" name="VK_KHR_CREATE_RENDERPASS_2_SPEC_VERSION"/>
<enum value="&quot;VK_KHR_create_renderpass2&quot;" name="VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME"/>
@@ -11758,8 +15174,10 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_KHR_maintenance2" number="118" type="device" author="KHR" contact="Michael Worcester @michaelworcester" supported="vulkan" promotedto="VK_VERSION_1_1">
<require>
- <enum value="1" name="VK_KHR_MAINTENANCE2_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_maintenance2&quot;" name="VK_KHR_MAINTENANCE2_EXTENSION_NAME"/>
+ <enum value="1" name="VK_KHR_MAINTENANCE_2_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_maintenance2&quot;" name="VK_KHR_MAINTENANCE_2_EXTENSION_NAME"/>
+ <enum alias="VK_KHR_MAINTENANCE_2_SPEC_VERSION" name="VK_KHR_MAINTENANCE2_SPEC_VERSION" comment="Backwards-compatible alias containing a typo"/>
+ <enum alias="VK_KHR_MAINTENANCE_2_EXTENSION_NAME" name="VK_KHR_MAINTENANCE2_EXTENSION_NAME" comment="Backwards-compatible alias containing a typo"/>
<enum extends="VkImageCreateFlagBits" name="VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR" alias="VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT"/>
<enum extends="VkImageCreateFlagBits" name="VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR" alias="VK_IMAGE_CREATE_EXTENDED_USAGE_BIT"/>
<enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES_KHR" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES"/>
@@ -11813,13 +15231,13 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_KHR_get_display_properties2" number="122" type="instance" requires="VK_KHR_display" author="KHR" contact="James Jones @cubanismo" supported="vulkan">
<require>
- <enum value="1" name="VK_KHR_GET_DISPLAY_PROPERTIES_2_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_get_display_properties2&quot;" name="VK_KHR_GET_DISPLAY_PROPERTIES_2_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR"/>
- <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR"/>
- <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR"/>
- <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DISPLAY_PLANE_INFO_2_KHR"/>
- <enum offset="4" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DISPLAY_PLANE_CAPABILITIES_2_KHR"/>
+ <enum value="1" name="VK_KHR_GET_DISPLAY_PROPERTIES_2_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_get_display_properties2&quot;" name="VK_KHR_GET_DISPLAY_PROPERTIES_2_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR"/>
+ <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DISPLAY_PLANE_INFO_2_KHR"/>
+ <enum offset="4" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DISPLAY_PLANE_CAPABILITIES_2_KHR"/>
<type name="VkDisplayProperties2KHR"/>
<type name="VkDisplayPlaneProperties2KHR"/>
<type name="VkDisplayModeProperties2KHR"/>
@@ -11919,7 +15337,7 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_ANDROID_external_memory_android_hardware_buffer" number="130" type="device" author="ANDROID" requires="VK_KHR_sampler_ycbcr_conversion,VK_KHR_external_memory,VK_EXT_queue_family_foreign,VK_KHR_dedicated_allocation" platform="android" contact="Jesse Hall @critsec" supported="vulkan">
<require>
- <enum value="3" name="VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION"/>
+ <enum value="5" name="VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION"/>
<enum value="&quot;VK_ANDROID_external_memory_android_hardware_buffer&quot;" name="VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME"/>
<enum bitpos="10" extends="VkExternalMemoryHandleTypeFlagBits" name="VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID"/>
@@ -11938,6 +15356,10 @@ typedef void <name>CAMetalLayer</name>;
<command name="vkGetMemoryAndroidHardwareBufferANDROID"/>
<type name="AHardwareBuffer"/>
</require>
+ <require extension="VK_KHR_format_feature_flags2">
+ <type name="VkAndroidHardwareBufferFormatProperties2ANDROID"/>
+ <enum offset="6" extends="VkStructureType" name="VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_2_ANDROID"/>
+ </require>
</extension>
<extension name="VK_EXT_sampler_filter_minmax" number="131" type="device" author="NV" requires="VK_KHR_get_physical_device_properties2" contact="Jeff Bolz @jeffbolznv" supported="vulkan" promotedto="VK_VERSION_1_2">
<require>
@@ -11976,6 +15398,7 @@ typedef void <name>CAMetalLayer</name>;
<require>
<enum value="0" name="VK_AMD_EXTENSION_135_SPEC_VERSION"/>
<enum value="&quot;VK_AMD_extension_135&quot;" name="VK_AMD_EXTENSION_135_EXTENSION_NAME"/>
+ <enum bitpos="25" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_RESERVED_25_BIT_AMD"/>
</require>
</extension>
<extension name="VK_AMD_extension_136" number="136" author="AMD" contact="Mais Alnasser @malnasse" supported="disabled">
@@ -11996,15 +15419,15 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_AMD_shader_fragment_mask&quot;" name="VK_AMD_SHADER_FRAGMENT_MASK_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_EXT_inline_uniform_block" number="139" type="device" author="EXT" requires="VK_KHR_get_physical_device_properties2,VK_KHR_maintenance1" contact="Daniel Rakos @aqnuep" supported="vulkan">
+ <extension name="VK_EXT_inline_uniform_block" number="139" type="device" author="EXT" requires="VK_KHR_get_physical_device_properties2,VK_KHR_maintenance1" contact="Daniel Rakos @aqnuep" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
- <enum value="1" name="VK_EXT_INLINE_UNIFORM_BLOCK_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_inline_uniform_block&quot;" name="VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME"/>
- <enum offset="0" extends="VkDescriptorType" name="VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT"/>
- <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT"/>
- <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT"/>
- <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO_EXT"/>
+ <enum value="1" name="VK_EXT_INLINE_UNIFORM_BLOCK_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_inline_uniform_block&quot;" name="VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME"/>
+ <enum extends="VkDescriptorType" name="VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT" alias="VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT" alias="VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO_EXT" alias="VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO"/>
<type name="VkPhysicalDeviceInlineUniformBlockFeaturesEXT"/>
<type name="VkPhysicalDeviceInlineUniformBlockPropertiesEXT"/>
<type name="VkWriteDescriptorSetInlineUniformBlockEXT"/>
@@ -12064,7 +15487,11 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_KHR_relaxed_block_layout&quot;" name="VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="RESERVED_DO_NOT_USE_146" number="146" supported="disabled" comment="Used for functionality subsumed into Vulkan 1.1 and not published as an extension">
+ <extension name="VK_RESERVED_do_not_use_146" number="146" supported="disabled" comment="Used for functionality subsumed into Vulkan 1.1 and not published as an extension">
+ <require>
+ <enum value="1" name="VK_RESERVED_DO_NOT_USE_146_SPEC_VERSION"/>
+ <enum value="&quot;VK_RESERVED_do_not_use_146&quot;" name="VK_RESERVED_DO_NOT_USE_146_EXTENSION_NAME"/>
+ </require>
</extension>
<extension name="VK_KHR_get_memory_requirements2" number="147" type="device" author="KHR" contact="Jason Ekstrand @jekstrand" supported="vulkan" promotedto="VK_VERSION_1_1">
<require>
@@ -12093,7 +15520,7 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkImageFormatListCreateInfoKHR"/>
</require>
</extension>
- <extension name="VK_EXT_blend_operation_advanced" number="149" type="device" author="NV" contact="Jeff Bolz @jeffbolznv" supported="vulkan">
+ <extension name="VK_EXT_blend_operation_advanced" number="149" type="device" author="NV" contact="Jeff Bolz @jeffbolznv" supported="vulkan" requires="VK_KHR_get_physical_device_properties2">
<require>
<enum value="2" name="VK_EXT_BLEND_OPERATION_ADVANCED_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_blend_operation_advanced&quot;" name="VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME"/>
@@ -12164,7 +15591,7 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_KHR_acceleration_structure" number="151" type="device" requiresCore="1.1" requires="VK_EXT_descriptor_indexing,VK_KHR_buffer_device_address,VK_KHR_deferred_host_operations" author="KHR" contact="Daniel Koch @dgkoch" supported="vulkan" sortorder="1">
<require>
- <enum value="11" name="VK_KHR_ACCELERATION_STRUCTURE_SPEC_VERSION"/>
+ <enum value="13" name="VK_KHR_ACCELERATION_STRUCTURE_SPEC_VERSION"/>
<enum value="&quot;VK_KHR_acceleration_structure&quot;" name="VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME"/>
<enum offset="7" extends="VkStructureType" name="VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR"/>
@@ -12247,6 +15674,9 @@ typedef void <name>CAMetalLayer</name>;
<command name="vkGetDeviceAccelerationStructureCompatibilityKHR"/>
<command name="vkGetAccelerationStructureBuildSizesKHR"/>
</require>
+ <require extension="VK_KHR_format_feature_flags2">
+ <enum bitpos="29" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_ACCELERATION_STRUCTURE_VERTEX_BUFFER_BIT_KHR"/>
+ </require>
</extension>
<extension name="VK_KHR_ray_tracing_pipeline" number="348" type="device" requiresCore="1.1" requires="VK_KHR_spirv_1_4,VK_KHR_acceleration_structure" author="KHR" contact="Daniel Koch @dgkoch" supported="vulkan" sortorder="1">
<require>
@@ -12438,33 +15868,32 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_EXT_image_drm_format_modifier" number="159" type="device" requires="VK_KHR_bind_memory2,VK_KHR_get_physical_device_properties2,VK_KHR_image_format_list,VK_KHR_sampler_ycbcr_conversion" author="EXT" contact="Chad Versace @chadversary" supported="vulkan">
<require>
- <enum value="1" name="VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_SPEC_VERSION"/>
+ <enum value="2" name="VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_image_drm_format_modifier&quot;" name="VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME"/>
-
- <enum offset="0" dir="-" extends="VkResult" name="VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT"/>
-
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT"/>
- <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT"/>
- <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT"/>
- <enum offset="4" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT"/>
- <enum offset="5" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT"/>
-
- <enum offset="0" extends="VkImageTiling" name="VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT"/>
-
- <enum bitpos="7" extends="VkImageAspectFlagBits" name="VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT"/>
- <enum bitpos="8" extends="VkImageAspectFlagBits" name="VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT"/>
- <enum bitpos="9" extends="VkImageAspectFlagBits" name="VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT"/>
- <enum bitpos="10" extends="VkImageAspectFlagBits" name="VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT"/>
-
+ <enum offset="0" dir="-" extends="VkResult" name="VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT"/>
+ <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT"/>
+ <enum offset="4" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT"/>
+ <enum offset="5" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT"/>
+ <enum offset="0" extends="VkImageTiling" name="VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT"/>
+ <enum bitpos="7" extends="VkImageAspectFlagBits" name="VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT"/>
+ <enum bitpos="8" extends="VkImageAspectFlagBits" name="VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT"/>
+ <enum bitpos="9" extends="VkImageAspectFlagBits" name="VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT"/>
+ <enum bitpos="10" extends="VkImageAspectFlagBits" name="VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT"/>
<type name="VkDrmFormatModifierPropertiesListEXT"/>
<type name="VkDrmFormatModifierPropertiesEXT"/>
<type name="VkPhysicalDeviceImageDrmFormatModifierInfoEXT"/>
<type name="VkImageDrmFormatModifierListCreateInfoEXT"/>
<type name="VkImageDrmFormatModifierExplicitCreateInfoEXT"/>
<type name="VkImageDrmFormatModifierPropertiesEXT"/>
-
<command name="vkGetImageDrmFormatModifierPropertiesEXT"/>
</require>
+ <require extension="VK_KHR_format_feature_flags2">
+ <type name="VkDrmFormatModifierPropertiesList2EXT"/>
+ <type name="VkDrmFormatModifierProperties2EXT"/>
+ <enum offset="6" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_2_EXT"/>
+ </require>
</extension>
<extension name="VK_EXT_extension_160" number="160" author="EXT" contact="Mark Young @marky-lunarg" supported="disabled">
<require>
@@ -12525,8 +15954,8 @@ typedef void <name>CAMetalLayer</name>;
<require>
<enum value="1" name="VK_KHR_PORTABILITY_SUBSET_SPEC_VERSION"/>
<enum value="&quot;VK_KHR_portability_subset&quot;" name="VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR"/>
- <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
<type name="VkPhysicalDevicePortabilitySubsetFeaturesKHR"/>
<type name="VkPhysicalDevicePortabilitySubsetPropertiesKHR"/>
</require>
@@ -12538,11 +15967,11 @@ typedef void <name>CAMetalLayer</name>;
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SHADING_RATE_IMAGE_STATE_CREATE_INFO_NV"/>
<enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_FEATURES_NV"/>
<enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_PROPERTIES_NV"/>
- <enum offset="3" extends="VkImageLayout" name="VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV"/>
+ <enum extends="VkImageLayout" name="VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV" alias="VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR"/>
<enum offset="4" extends="VkDynamicState" name="VK_DYNAMIC_STATE_VIEWPORT_SHADING_RATE_PALETTE_NV"/>
- <enum bitpos="23" extends="VkAccessFlagBits" name="VK_ACCESS_SHADING_RATE_IMAGE_READ_BIT_NV"/>
- <enum bitpos="8" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV"/>
- <enum bitpos="22" extends="VkPipelineStageFlagBits" name="VK_PIPELINE_STAGE_SHADING_RATE_IMAGE_BIT_NV"/>
+ <enum extends="VkAccessFlagBits" name="VK_ACCESS_SHADING_RATE_IMAGE_READ_BIT_NV" alias="VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR"/>
+ <enum extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV" alias="VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR"/>
+ <enum extends="VkPipelineStageFlagBits" name="VK_PIPELINE_STAGE_SHADING_RATE_IMAGE_BIT_NV" alias="VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR"/>
<enum offset="5" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_COARSE_SAMPLE_ORDER_STATE_CREATE_INFO_NV"/>
<enum offset="6" extends="VkDynamicState" name="VK_DYNAMIC_STATE_VIEWPORT_COARSE_SAMPLE_ORDER_NV"/>
<type name="VkShadingRatePaletteEntryNV"/>
@@ -12559,7 +15988,7 @@ typedef void <name>CAMetalLayer</name>;
<command name="vkCmdSetCoarseSampleOrderNV"/>
</require>
</extension>
- <extension name="VK_NV_ray_tracing" number="166" type="device" requires="VK_KHR_get_physical_device_properties2,VK_KHR_get_memory_requirements2" author="NV" contact="Eric Werness @ewerness" supported="vulkan">
+ <extension name="VK_NV_ray_tracing" number="166" type="device" requires="VK_KHR_get_physical_device_properties2,VK_KHR_get_memory_requirements2" author="NV" contact="Eric Werness @ewerness-nv" supported="vulkan">
<require>
<enum value="3" name="VK_NV_RAY_TRACING_SPEC_VERSION"/>
<enum value="&quot;VK_NV_ray_tracing&quot;" name="VK_NV_RAY_TRACING_EXTENSION_NAME"/>
@@ -12667,14 +16096,16 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_NV_extension_168" number="168" author="NV" contact="Daniel Koch @dgkoch" supported="disabled">
<require>
- <enum value="0" name="VK_EXT_EXTENSION_168_SPEC_VERSION"/>
- <enum value="&quot;VK_NV_extension_168&quot;" name="VK_EXT_EXTENSION_168_EXTENSION_NAME"/>
+ <enum value="0" name="VK_NV_EXTENSION_168_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_extension_168&quot;" name="VK_NV_EXTENSION_168_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_KHR_maintenance3" number="169" type="device" requires="VK_KHR_get_physical_device_properties2" author="KHR" contact="Jeff Bolz @jeffbolznv" supported="vulkan" promotedto="VK_VERSION_1_1">
<require>
- <enum value="1" name="VK_KHR_MAINTENANCE3_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_maintenance3&quot;" name="VK_KHR_MAINTENANCE3_EXTENSION_NAME"/>
+ <enum value="1" name="VK_KHR_MAINTENANCE_3_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_maintenance3&quot;" name="VK_KHR_MAINTENANCE_3_EXTENSION_NAME"/>
+ <enum alias="VK_KHR_MAINTENANCE_3_SPEC_VERSION" name="VK_KHR_MAINTENANCE3_SPEC_VERSION" comment="Backwards-compatible alias containing a typo"/>
+ <enum alias="VK_KHR_MAINTENANCE_3_EXTENSION_NAME" name="VK_KHR_MAINTENANCE3_EXTENSION_NAME" comment="Backwards-compatible alias containing a typo"/>
<enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES_KHR" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES"/>
<enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT_KHR" alias="VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT"/>
<type name="VkPhysicalDeviceMaintenance3PropertiesKHR"/>
@@ -12694,8 +16125,8 @@ typedef void <name>CAMetalLayer</name>;
<require>
<enum value="3" name="VK_EXT_FILTER_CUBIC_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_filter_cubic&quot;" name="VK_EXT_FILTER_CUBIC_EXTENSION_NAME"/>
- <enum extends="VkFilter" name="VK_FILTER_CUBIC_EXT" alias="VK_FILTER_CUBIC_IMG"/>
- <enum extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_EXT" alias="VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG"/>
+ <enum offset="0" extends="VkFilter" extnumber="16" name="VK_FILTER_CUBIC_EXT"/>
+ <enum bitpos="13" extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_EXT"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_IMAGE_FORMAT_INFO_EXT"/>
<enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_FILTER_CUBIC_IMAGE_VIEW_IMAGE_FORMAT_PROPERTIES_EXT"/>
<type name="VkPhysicalDeviceImageViewImageFormatInfoEXT"/>
@@ -12712,8 +16143,8 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_QCOM_extension_173" number="173" author="QCOM" contact="Bill Licea-Kane @wwlk" supported="disabled">
<require>
- <enum value="0" name="VK_QCOM_extension_173_SPEC_VERSION"/>
- <enum value="&quot;VK_QCOM_extension_173&quot;" name="VK_QCOM_extension_173_EXTENSION_NAME"/>
+ <enum value="0" name="VK_QCOM_EXTENSION_173_SPEC_VERSION"/>
+ <enum value="&quot;VK_QCOM_extension_173&quot;" name="VK_QCOM_EXTENSION_173_EXTENSION_NAME"/>
<enum bitpos="18" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_RESERVED_18_BIT_QCOM"/>
<enum bitpos="16" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_RESERVED_16_BIT_QCOM"/>
<enum bitpos="17" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_RESERVED_17_BIT_QCOM"/>
@@ -12721,16 +16152,16 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_QCOM_extension_174" number="174" author="QCOM" contact="Bill Licea-Kane @wwlk" supported="disabled">
<require>
- <enum value="0" name="VK_QCOM_extension_174_SPEC_VERSION"/>
- <enum value="&quot;VK_QCOM_extension_174&quot;" name="VK_QCOM_extension_174_EXTENSION_NAME"/>
+ <enum value="0" name="VK_QCOM_EXTENSION_174_SPEC_VERSION"/>
+ <enum value="&quot;VK_QCOM_extension_174&quot;" name="VK_QCOM_EXTENSION_174_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_EXT_global_priority" number="175" type="device" author="EXT" contact="Andres Rodriguez @lostgoat" supported="vulkan">
+ <extension name="VK_EXT_global_priority" number="175" type="device" author="EXT" contact="Andres Rodriguez @lostgoat" supported="vulkan" promotedto="VK_KHR_global_priority">
<require>
<enum value="2" name="VK_EXT_GLOBAL_PRIORITY_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_global_priority&quot;" name="VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT"/>
- <enum offset="1" dir="-" extends="VkResult" name="VK_ERROR_NOT_PERMITTED_EXT"/>
+ <enum extends="VkStructureType" alias="VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR" name="VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT"/>
+ <enum extends="VkResult" alias="VK_ERROR_NOT_PERMITTED_KHR" name="VK_ERROR_NOT_PERMITTED_EXT"/>
<type name="VkDeviceQueueGlobalPriorityCreateInfoEXT"/>
<type name="VkQueueGlobalPriorityEXT"/>
</require>
@@ -12745,8 +16176,8 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_EXT_extension_177" number="177" author="EXT" contact="Neil Henning @sheredom" supported="disabled">
<require>
- <enum value="0" name="VK_KHR_EXTENSION_177_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_extension_177&quot;" name="VK_KHR_EXTENSION_177_EXTENSION_NAME"/>
+ <enum value="0" name="VK_EXT_EXTENSION_177_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_177&quot;" name="VK_EXT_EXTENSION_177_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_KHR_8bit_storage" number="178" type="device" requires="VK_KHR_get_physical_device_properties2,VK_KHR_storage_buffer_storage_class" author="KHR" contact="Alexander Galazin @alegal-arm" supported="vulkan" promotedto="VK_VERSION_1_2">
@@ -12797,8 +16228,8 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_AMD_extension_183" number="183" author="AMD" contact="Daniel Rakos @drakos-amd" supported="disabled">
<require>
- <enum value="0" name="VK_KHR_EXTENSION_183_SPEC_VERSION"/>
- <enum value="&quot;VK_AMD_extension_183&quot;" name="VK_KHR_EXTENSION_183_EXTENSION_NAME"/>
+ <enum value="0" name="VK_AMD_EXTENSION_183_SPEC_VERSION"/>
+ <enum value="&quot;VK_AMD_extension_183&quot;" name="VK_AMD_EXTENSION_183_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_AMD_pipeline_compiler_control" number="184" type="device" author="AMD" contact="Matthaeus G. Chajdas @anteru" supported="vulkan">
@@ -12811,9 +16242,9 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkPipelineCompilerControlCreateInfoAMD"/>
</require>
</extension>
- <extension name="VK_EXT_calibrated_timestamps" number="185" type="device" author="EXT" contact="Daniel Rakos @drakos-amd" supported="vulkan">
+ <extension name="VK_EXT_calibrated_timestamps" number="185" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Daniel Rakos @drakos-amd" supported="vulkan">
<require>
- <enum value="1" name="VK_EXT_CALIBRATED_TIMESTAMPS_SPEC_VERSION"/>
+ <enum value="2" name="VK_EXT_CALIBRATED_TIMESTAMPS_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_calibrated_timestamps&quot;" name="VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT"/>
<type name="VkTimeDomainEXT"/>
@@ -12832,20 +16263,44 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_AMD_extension_187" number="187" author="AMD" contact="Daniel Rakos @drakos-amd" supported="disabled">
<require>
- <enum value="0" name="VK_KHR_EXTENSION_187_SPEC_VERSION"/>
- <enum value="&quot;VK_AMD_extension_187&quot;" name="VK_KHR_EXTENSION_187_EXTENSION_NAME"/>
+ <enum value="0" name="VK_AMD_EXTENSION_187_SPEC_VERSION"/>
+ <enum value="&quot;VK_AMD_extension_187&quot;" name="VK_AMD_EXTENSION_187_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_AMD_extension_188" number="188" author="AMD" contact="Daniel Rakos @drakos-amd" supported="disabled">
+ <extension name="VK_EXT_video_decode_h265" number="188" type="device" requires="VK_KHR_video_decode_queue" author="KHR" contact="peter.fang@amd.com" provisional="true" platform="provisional" supported="vulkan">
<require>
- <enum value="0" name="VK_KHR_EXTENSION_188_SPEC_VERSION"/>
- <enum value="&quot;VK_AMD_extension_188&quot;" name="VK_KHR_EXTENSION_188_EXTENSION_NAME"/>
+ <enum value="3" name="VK_EXT_VIDEO_DECODE_H265_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_video_decode_h265&quot;" name="VK_EXT_VIDEO_DECODE_H265_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_CAPABILITIES_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PROFILE_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="4" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PICTURE_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="5" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_DPB_SLOT_INFO_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="1" extends="VkVideoCodecOperationFlagBitsKHR" name="VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_EXT" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+
+ <type name="VkVideoDecodeH265ProfileEXT"/>
+ <type name="VkVideoDecodeH265CapabilitiesEXT"/>
+
+ <type name="VkVideoDecodeH265SessionParametersCreateInfoEXT"/>
+ <type name="VkVideoDecodeH265SessionParametersAddInfoEXT"/>
+ <type name="VkVideoDecodeH265PictureInfoEXT"/>
+ <type name="VkVideoDecodeH265DpbSlotInfoEXT"/>
</require>
</extension>
- <extension name="VK_AMD_extension_189" number="189" author="AMD" contact="Daniel Rakos @drakos-amd" supported="disabled">
+ <extension name="VK_KHR_global_priority" number="189" type="device" author="KHR" contact="Tobias Hector @tobski" supported="vulkan">
<require>
- <enum value="0" name="VK_KHR_EXTENSION_189_SPEC_VERSION"/>
- <enum value="&quot;VK_AMD_extension_189&quot;" name="VK_KHR_EXTENSION_189_EXTENSION_NAME"/>
+ <enum value="1" name="VK_KHR_GLOBAL_PRIORITY_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_global_priority&quot;" name="VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" extnumber="175" name="VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR"/>
+ <enum offset="0" extends="VkStructureType" extnumber="389" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_KHR"/>
+ <enum offset="1" extends="VkStructureType" extnumber="389" name="VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_KHR"/>
+ <enum extends="VkResult" extnumber="175" offset="1" dir="-" name="VK_ERROR_NOT_PERMITTED_KHR"/>
+ <enum name="VK_MAX_GLOBAL_PRIORITY_SIZE_KHR"/>
+ <type name="VkDeviceQueueGlobalPriorityCreateInfoKHR"/>
+ <type name="VkQueueGlobalPriorityKHR"/>
+ <type name="VkPhysicalDeviceGlobalPriorityQueryFeaturesKHR"/>
+ <type name="VkQueueFamilyGlobalPriorityPropertiesKHR"/>
</require>
</extension>
<extension name="VK_AMD_memory_overallocation_behavior" number="190" type="device" author="AMD" contact="Martin Dinkov @mdinkov" supported="vulkan">
@@ -12878,11 +16333,11 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkPresentFrameTokenGGP"/>
</require>
</extension>
- <extension name="VK_EXT_pipeline_creation_feedback" number="193" type="device" author="GOOGLE" contact="Jean-Francois Roy @jfroy" specialuse="devtools" supported="vulkan">
+ <extension name="VK_EXT_pipeline_creation_feedback" number="193" type="device" author="GOOGLE" contact="Jean-Francois Roy @jfroy" specialuse="devtools" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
- <enum value="1" name="VK_EXT_PIPELINE_CREATION_FEEDBACK_SPEC_VERSION"/>
+ <enum value="1" name="VK_EXT_PIPELINE_CREATION_FEEDBACK_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_pipeline_creation_feedback&quot;" name="VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO_EXT"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO_EXT" alias="VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO"/>
<type name="VkPipelineCreationFeedbackFlagBitsEXT"/>
<type name="VkPipelineCreationFeedbackFlagsEXT"/>
<type name="VkPipelineCreationFeedbackCreateInfoEXT"/>
@@ -13002,11 +16457,11 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkDrawMeshTasksIndirectCommandNV"/>
</require>
</extension>
- <extension name="VK_NV_fragment_shader_barycentric" number="204" type="device" requires="VK_KHR_get_physical_device_properties2" author="NV" contact="Pat Brown @nvpbrown" supported="vulkan">
+ <extension name="VK_NV_fragment_shader_barycentric" number="204" type="device" requires="VK_KHR_get_physical_device_properties2" author="NV" contact="Pat Brown @nvpbrown" supported="vulkan" promotedto="VK_KHR_fragment_shader_barycentric">
<require>
<enum value="1" name="VK_NV_FRAGMENT_SHADER_BARYCENTRIC_SPEC_VERSION"/>
<enum value="&quot;VK_NV_fragment_shader_barycentric&quot;" name="VK_NV_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_NV"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_NV" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR"/>
<type name="VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV"/>
</require>
</extension>
@@ -13088,7 +16543,7 @@ typedef void <name>CAMetalLayer</name>;
<enum value="2" name="VK_INTEL_PERFORMANCE_QUERY_SPEC_VERSION"/>
<enum value="&quot;VK_INTEL_performance_query&quot;" name="VK_INTEL_PERFORMANCE_QUERY_EXTENSION_NAME"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_QUERY_POOL_PERFORMANCE_QUERY_CREATE_INFO_INTEL"/>
- <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO_INTEL" alias="VK_STRUCTURE_TYPE_QUERY_POOL_PERFORMANCE_QUERY_CREATE_INFO_INTEL" comment="Backwards-compatible alias"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO_INTEL" alias="VK_STRUCTURE_TYPE_QUERY_POOL_PERFORMANCE_QUERY_CREATE_INFO_INTEL" comment="Backwards-compatible alias containing a typo"/>
<enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_INITIALIZE_PERFORMANCE_API_INFO_INTEL"/>
<enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PERFORMANCE_MARKER_INFO_INTEL"/>
<enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PERFORMANCE_STREAM_MARKER_INFO_INTEL"/>
@@ -13160,18 +16615,18 @@ typedef void <name>CAMetalLayer</name>;
<command name="vkCreateImagePipeSurfaceFUCHSIA"/>
</require>
</extension>
- <extension name="VK_KHR_shader_terminate_invocation" number="216" type="device" author="KHR" requires="VK_KHR_get_physical_device_properties2" contact="Jesse Hall @critsec" supported="vulkan">
+ <extension name="VK_KHR_shader_terminate_invocation" number="216" type="device" author="KHR" requires="VK_KHR_get_physical_device_properties2" contact="Jesse Hall @critsec" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
<enum value="1" name="VK_KHR_SHADER_TERMINATE_INVOCATION_SPEC_VERSION"/>
<enum value="&quot;VK_KHR_shader_terminate_invocation&quot;" name="VK_KHR_SHADER_TERMINATE_INVOCATION_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES_KHR"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES_KHR" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES"/>
<type name="VkPhysicalDeviceShaderTerminateInvocationFeaturesKHR"/>
</require>
</extension>
<extension name="VK_GOOGLE_extension_217" number="217" author="GOOGLE" contact="Jesse Hall @critsec" supported="disabled">
<require>
- <enum value="0" name="VK_KHR_EXTENSION_217_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_extension_217&quot;" name="VK_KHR_EXTENSION_217_EXTENSION_NAME"/>
+ <enum value="0" name="VK_GOOGLE_EXTENSION_217_SPEC_VERSION"/>
+ <enum value="&quot;VK_GOOGLE_extension_217&quot;" name="VK_GOOGLE_EXTENSION_217_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_EXT_metal_surface" number="218" type="instance" requires="VK_KHR_surface" platform="metal" supported="vulkan" author="EXT" contact="Dzmitry Malyshau @kvark">
@@ -13187,7 +16642,7 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_EXT_fragment_density_map" number="219" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Matthew Netsch @mnetsch" supported="vulkan">
<require>
- <enum value="1" name="VK_EXT_FRAGMENT_DENSITY_MAP_SPEC_VERSION"/>
+ <enum value="2" name="VK_EXT_FRAGMENT_DENSITY_MAP_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_fragment_density_map&quot;" name="VK_EXT_FRAGMENT_DENSITY_MAP_EXTENSION_NAME"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT"/>
<enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT"/>
@@ -13205,6 +16660,9 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkPhysicalDeviceFragmentDensityMapPropertiesEXT"/>
<type name="VkRenderPassFragmentDensityMapCreateInfoEXT"/>
</require>
+ <require extension="VK_KHR_format_feature_flags2">
+ <enum bitpos="24" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_FRAGMENT_DENSITY_MAP_BIT_EXT"/>
+ </require>
</extension>
<extension name="VK_EXT_extension_220" number="220" author="EXT" contact="Dzmitry Malyshau @kvark" supported="disabled">
<require>
@@ -13235,8 +16693,10 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_GOOGLE_hlsl_functionality1" number="224" type="device" author="GOOGLE" contact="Hai Nguyen @chaoticbob" supported="vulkan">
<require>
- <enum value="1" name="VK_GOOGLE_HLSL_FUNCTIONALITY1_SPEC_VERSION"/>
- <enum value="&quot;VK_GOOGLE_hlsl_functionality1&quot;" name="VK_GOOGLE_HLSL_FUNCTIONALITY1_EXTENSION_NAME"/>
+ <enum value="1" name="VK_GOOGLE_HLSL_FUNCTIONALITY_1_SPEC_VERSION"/>
+ <enum value="&quot;VK_GOOGLE_hlsl_functionality1&quot;" name="VK_GOOGLE_HLSL_FUNCTIONALITY_1_EXTENSION_NAME"/>
+ <enum alias="VK_GOOGLE_HLSL_FUNCTIONALITY_1_SPEC_VERSION" name="VK_GOOGLE_HLSL_FUNCTIONALITY1_SPEC_VERSION" comment="Backwards-compatible alias containing a typo"/>
+ <enum alias="VK_GOOGLE_HLSL_FUNCTIONALITY_1_EXTENSION_NAME" name="VK_GOOGLE_HLSL_FUNCTIONALITY1_EXTENSION_NAME" comment="Backwards-compatible alias containing a typo"/>
</require>
</extension>
<extension name="VK_GOOGLE_decorate_string" number="225" type="device" author="GOOGLE" contact="Hai Nguyen @chaoticbob" supported="vulkan">
@@ -13245,23 +16705,23 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_GOOGLE_decorate_string&quot;" name="VK_GOOGLE_DECORATE_STRING_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_EXT_subgroup_size_control" number="226" type="device" requiresCore="1.1" author="EXT" contact="Neil Henning @sheredom" supported="vulkan">
+ <extension name="VK_EXT_subgroup_size_control" number="226" type="device" requiresCore="1.1" author="EXT" contact="Neil Henning @sheredom" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
- <enum value="2" name="VK_EXT_SUBGROUP_SIZE_CONTROL_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_subgroup_size_control&quot;" name="VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME"/>
- <type name="VkPhysicalDeviceSubgroupSizeControlFeaturesEXT"/>
- <type name="VkPhysicalDeviceSubgroupSizeControlPropertiesEXT"/>
- <type name="VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT"/>
- <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT"/>
- <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT"/>
- <enum bitpos="0" extends="VkPipelineShaderStageCreateFlagBits" name="VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT"/>
- <enum bitpos="1" extends="VkPipelineShaderStageCreateFlagBits" name="VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT"/>
+ <enum value="2" name="VK_EXT_SUBGROUP_SIZE_CONTROL_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_subgroup_size_control&quot;" name="VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME"/>
+ <type name="VkPhysicalDeviceSubgroupSizeControlFeaturesEXT"/>
+ <type name="VkPhysicalDeviceSubgroupSizeControlPropertiesEXT"/>
+ <type name="VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT" alias="VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES"/>
+ <enum extends="VkPipelineShaderStageCreateFlagBits" name="VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT" alias="VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT"/>
+ <enum extends="VkPipelineShaderStageCreateFlagBits" name="VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT" alias="VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT"/>
</require>
</extension>
<extension name="VK_KHR_fragment_shading_rate" number="227" type="device" requires="VK_KHR_create_renderpass2,VK_KHR_get_physical_device_properties2" author="KHR" contact="Tobias Hector @tobski" supported="vulkan">
<require>
- <enum value="1" name="VK_KHR_FRAGMENT_SHADING_RATE_SPEC_VERSION"/>
+ <enum value="2" name="VK_KHR_FRAGMENT_SHADING_RATE_SPEC_VERSION"/>
<enum value="&quot;VK_KHR_fragment_shading_rate&quot;" name="VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME"/>
<type name="VkFragmentShadingRateCombinerOpKHR"/>
<type name="VkFragmentShadingRateAttachmentInfoKHR"/>
@@ -13271,17 +16731,20 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkPhysicalDeviceFragmentShadingRateKHR"/>
<command name="vkGetPhysicalDeviceFragmentShadingRatesKHR"/>
<command name="vkCmdSetFragmentShadingRateKHR"/>
- <enum extends="VkImageLayout" name="VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR" alias="VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV"/>
+ <enum offset="3" extends="VkImageLayout" extnumber="165" name="VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR"/>
<enum offset="0" extends="VkDynamicState" name="VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR"/>
<enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_SHADING_RATE_STATE_CREATE_INFO_KHR"/>
<enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR"/>
<enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR"/>
<enum offset="4" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_KHR"/>
- <enum extends="VkAccessFlagBits" name="VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR" alias="VK_ACCESS_SHADING_RATE_IMAGE_READ_BIT_NV"/>
- <enum extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR" alias="VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV"/>
- <enum extends="VkPipelineStageFlagBits" name="VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR" alias="VK_PIPELINE_STAGE_SHADING_RATE_IMAGE_BIT_NV"/>
- <enum bitpos="30" extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR"/>
+ <enum bitpos="23" extends="VkAccessFlagBits" name="VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR"/>
+ <enum bitpos="8" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR"/>
+ <enum bitpos="22" extends="VkPipelineStageFlagBits" name="VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR"/>
+ <enum bitpos="30" extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR"/>
+ </require>
+ <require extension="VK_KHR_format_feature_flags2">
+ <enum bitpos="30" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR"/>
</require>
</extension>
<extension name="VK_AMD_shader_core_properties2" number="228" type="device" author="AMD" contact="Matthaeus G. Chajdas @anteru" supported="vulkan" requires="VK_AMD_shader_core_properties">
@@ -13434,25 +16897,25 @@ typedef void <name>CAMetalLayer</name>;
<command name="vkGetBufferDeviceAddressEXT"/>
</require>
</extension>
- <extension name="VK_EXT_tooling_info" number="246" type="device" author="EXT" contact="Tobias Hector @tobski" supported="vulkan">
+ <extension name="VK_EXT_tooling_info" number="246" type="device" author="EXT" contact="Tobias Hector @tobski" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
<enum value="1" name="VK_EXT_TOOLING_INFO_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_tooling_info&quot;" name="VK_EXT_TOOLING_INFO_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TOOL_PROPERTIES_EXT"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TOOL_PROPERTIES_EXT" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TOOL_PROPERTIES"/>
<type name="VkToolPurposeFlagBitsEXT"/>
<type name="VkToolPurposeFlagsEXT"/>
<type name="VkPhysicalDeviceToolPropertiesEXT"/>
<command name="vkGetPhysicalDeviceToolPropertiesEXT"/>
</require>
<require extension="VK_EXT_debug_report">
- <enum bitpos="5" extends="VkToolPurposeFlagBitsEXT" name="VK_TOOL_PURPOSE_DEBUG_REPORTING_BIT_EXT"/>
+ <enum bitpos="5" extends="VkToolPurposeFlagBits" name="VK_TOOL_PURPOSE_DEBUG_REPORTING_BIT_EXT"/>
</require>
<require extension="VK_EXT_debug_marker">
- <enum bitpos="6" extends="VkToolPurposeFlagBitsEXT" name="VK_TOOL_PURPOSE_DEBUG_MARKERS_BIT_EXT"/>
+ <enum bitpos="6" extends="VkToolPurposeFlagBits" name="VK_TOOL_PURPOSE_DEBUG_MARKERS_BIT_EXT"/>
</require>
<require extension="VK_EXT_debug_utils">
- <enum bitpos="5" extends="VkToolPurposeFlagBitsEXT" name="VK_TOOL_PURPOSE_DEBUG_REPORTING_BIT_EXT"/>
- <enum bitpos="6" extends="VkToolPurposeFlagBitsEXT" name="VK_TOOL_PURPOSE_DEBUG_MARKERS_BIT_EXT"/>
+ <enum bitpos="5" extends="VkToolPurposeFlagBits" name="VK_TOOL_PURPOSE_DEBUG_REPORTING_BIT_EXT"/>
+ <enum bitpos="6" extends="VkToolPurposeFlagBits" name="VK_TOOL_PURPOSE_DEBUG_MARKERS_BIT_EXT"/>
</require>
</extension>
<extension name="VK_EXT_separate_stencil_usage" number="247" type="device" author="EXT" contact="Daniel Rakos @drakos-amd" supported="vulkan" promotedto="VK_VERSION_1_2">
@@ -13465,7 +16928,7 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_EXT_validation_features" number="248" type="instance" author="LUNARG" contact="Karl Schultz @karl-lunarg" specialuse="debugging" supported="vulkan">
<require>
- <enum value="4" name="VK_EXT_VALIDATION_FEATURES_SPEC_VERSION"/>
+ <enum value="5" name="VK_EXT_VALIDATION_FEATURES_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_validation_features&quot;" name="VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT"/>
<type name="VkValidationFeaturesEXT"/>
@@ -13473,10 +16936,13 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkValidationFeatureDisableEXT"/>
</require>
</extension>
- <extension name="VK_KHR_extension_249" number="249" author="KHR" contact="Keith Packard @keithp" supported="disabled">
+ <extension name="VK_KHR_present_wait" number="249" type="device" requires="VK_KHR_swapchain,VK_KHR_present_id" author="KHR" contact="Keith Packard @keithp" supported="vulkan">
<require>
- <enum value="0" name="VK_KHR_EXTENSION_249_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_extension_249&quot;" name="VK_KHR_EXTENSION_249_EXTENSION_NAME"/>
+ <enum value="1" name="VK_KHR_PRESENT_WAIT_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_present_wait&quot;" name="VK_KHR_PRESENT_WAIT_EXTENSION_NAME"/>
+ <command name="vkWaitForPresentKHR"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR"/>
+ <type name="VkPhysicalDevicePresentWaitFeaturesKHR"/>
</require>
</extension>
<extension name="VK_NV_cooperative_matrix" number="250" type="device" requires="VK_KHR_get_physical_device_properties2" author="NV" contact="Jeff Bolz @jeffbolznv" supported="vulkan">
@@ -13533,10 +16999,17 @@ typedef void <name>CAMetalLayer</name>;
<enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES"/>
</require>
</extension>
- <extension name="VK_EXT_extension_255" number="255" author="EXT" contact="Jesse Hall @jessehall" supported="disabled">
+ <extension name="VK_EXT_provoking_vertex" number="255" type="device" author="EXT" requires="VK_KHR_get_physical_device_properties2" contact="Jesse Hall @jessehall" specialuse="glemulation" supported="vulkan">
<require>
- <enum value="0" name="VK_EXT_EXTENSION_255_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_extension_255&quot;" name="VK_EXT_EXTENSION_255_EXTENSION_NAME"/>
+ <enum value="1" name="VK_EXT_PROVOKING_VERTEX_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_provoking_vertex&quot;" name="VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_PROPERTIES_EXT"/>
+ <type name="VkPhysicalDeviceProvokingVertexFeaturesEXT"/>
+ <type name="VkPhysicalDeviceProvokingVertexPropertiesEXT"/>
+ <type name="VkPipelineRasterizationProvokingVertexStateCreateInfoEXT"/>
+ <type name="VkProvokingVertexModeEXT"/>
</require>
</extension>
<extension name="VK_EXT_full_screen_exclusive" number="256" type="device" author="EXT" requires="VK_KHR_get_physical_device_properties2,VK_KHR_surface,VK_KHR_get_surface_capabilities2,VK_KHR_swapchain" platform="win32" contact="James Jones @cubanismo" supported="vulkan">
@@ -13602,6 +17075,10 @@ typedef void <name>CAMetalLayer</name>;
<require>
<enum value="0" name="VK_EXT_EXTENSION_259_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_extension_259&quot;" name="VK_EXT_EXTENSION_259_EXTENSION_NAME"/>
+ <enum bitpos="9" extends="VkQueueFlagBits" name="VK_QUEUE_RESERVED_9_BIT_EXT"/>
+ <enum bitpos="44" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_RESERVED_44_BIT_EXT"/>
+ <enum bitpos="45" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_RESERVED_45_BIT_EXT"/>
+ <enum bitpos="19" extends="VkImageCreateFlagBits" name="VK_IMAGE_CREATE_RESERVED_19_BIT_EXT"/>
</require>
</extension>
<extension name="VK_EXT_line_rasterization" number="260" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Jeff Bolz @jeffbolznv" specialuse="cadsupport" supported="vulkan">
@@ -13638,8 +17115,8 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_GGP_extension_263" number="263" author="GGP" contact="Jean-Francois Roy @jfroy" supported="disabled">
<require>
- <enum value="0" name="VK_GOOGLE_EXTENSION_263_SPEC_VERSION"/>
- <enum value="&quot;VK_GGP_extension_263&quot;" name="VK_GOOGLE_EXTENSION_263_EXTENSION_NAME"/>
+ <enum value="0" name="VK_GGP_EXTENSION_263_SPEC_VERSION"/>
+ <enum value="&quot;VK_GGP_extension_263&quot;" name="VK_GGP_EXTENSION_263_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_BRCM_extension_264" number="264" author="BRCM" contact="Graeme Leese @gnl21" supported="disabled">
@@ -13666,27 +17143,27 @@ typedef void <name>CAMetalLayer</name>;
<extension name="VK_EXT_extension_267" number="267" type="device" author="EXT" contact="Piers Daniell @pdaniell-nv" supported="disabled">
<require>
<enum value="0" name="VK_EXT_EXTENSION_267_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_extension_267&quot;" name="VK_EXT_extension_267"/>
+ <enum value="&quot;VK_EXT_extension_267&quot;" name="VK_EXT_EXTENSION_267_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_EXT_extended_dynamic_state" number="268" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Piers Daniell @pdaniell-nv" supported="vulkan">
+ <extension name="VK_EXT_extended_dynamic_state" number="268" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Piers Daniell @pdaniell-nv" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
<enum value="1" name="VK_EXT_EXTENDED_DYNAMIC_STATE_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_extended_dynamic_state&quot;" name="VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT"/>
- <enum offset="0" extends="VkDynamicState" name="VK_DYNAMIC_STATE_CULL_MODE_EXT"/>
- <enum offset="1" extends="VkDynamicState" name="VK_DYNAMIC_STATE_FRONT_FACE_EXT"/>
- <enum offset="2" extends="VkDynamicState" name="VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT"/>
- <enum offset="3" extends="VkDynamicState" name="VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT"/>
- <enum offset="4" extends="VkDynamicState" name="VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT"/>
- <enum offset="5" extends="VkDynamicState" name="VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT"/>
- <enum offset="6" extends="VkDynamicState" name="VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT"/>
- <enum offset="7" extends="VkDynamicState" name="VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT"/>
- <enum offset="8" extends="VkDynamicState" name="VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT"/>
- <enum offset="9" extends="VkDynamicState" name="VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT"/>
- <enum offset="10" extends="VkDynamicState" name="VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT"/>
- <enum offset="11" extends="VkDynamicState" name="VK_DYNAMIC_STATE_STENCIL_OP_EXT"/>
- <type name="VkPhysicalDeviceExtendedDynamicStateFeaturesEXT"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT" comment="Not promoted to 1.3"/>
+ <enum extends="VkDynamicState" name="VK_DYNAMIC_STATE_CULL_MODE_EXT" alias="VK_DYNAMIC_STATE_CULL_MODE"/>
+ <enum extends="VkDynamicState" name="VK_DYNAMIC_STATE_FRONT_FACE_EXT" alias="VK_DYNAMIC_STATE_FRONT_FACE"/>
+ <enum extends="VkDynamicState" name="VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT" alias="VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY"/>
+ <enum extends="VkDynamicState" name="VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT" alias="VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT"/>
+ <enum extends="VkDynamicState" name="VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT" alias="VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT"/>
+ <enum extends="VkDynamicState" name="VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT" alias="VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE"/>
+ <enum extends="VkDynamicState" name="VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT" alias="VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE"/>
+ <enum extends="VkDynamicState" name="VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT" alias="VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE"/>
+ <enum extends="VkDynamicState" name="VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT" alias="VK_DYNAMIC_STATE_DEPTH_COMPARE_OP"/>
+ <enum extends="VkDynamicState" name="VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT" alias="VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE"/>
+ <enum extends="VkDynamicState" name="VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT" alias="VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE"/>
+ <enum extends="VkDynamicState" name="VK_DYNAMIC_STATE_STENCIL_OP_EXT" alias="VK_DYNAMIC_STATE_STENCIL_OP"/>
+ <type name="VkPhysicalDeviceExtendedDynamicStateFeaturesEXT" comment="Not promoted to 1.3"/>
<command name="vkCmdSetCullModeEXT"/>
<command name="vkCmdSetFrontFaceEXT"/>
<command name="vkCmdSetPrimitiveTopologyEXT"/>
@@ -13699,8 +17176,8 @@ typedef void <name>CAMetalLayer</name>;
<command name="vkCmdSetDepthBoundsTestEnableEXT"/>
<command name="vkCmdSetStencilTestEnableEXT"/>
<command name="vkCmdSetStencilOpEXT"/>
- </require>
- </extension>
+ </require>
+ </extension>
<extension name="VK_KHR_deferred_host_operations" number="269" type="device" author="KHR" contact="Josh Barczak @jbarczak" supported="vulkan">
<require>
<enum value="4" name="VK_KHR_DEFERRED_HOST_OPERATIONS_SPEC_VERSION"/>
@@ -13746,49 +17223,56 @@ typedef void <name>CAMetalLayer</name>;
<extension name="VK_INTEL_extension_271" number="271" type="device" author="INTEL" contact="Jason Ekstrand @jekstrand" supported="disabled">
<require>
<enum value="0" name="VK_INTEL_EXTENSION_271_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_extension_271&quot;" name="VK_INTEL_extension_271"/>
+ <enum value="&quot;VK_INTEL_extension_271&quot;" name="VK_INTEL_EXTENSION_271_EXTENSION_NAME"/>
+ <enum bitpos="22" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_RESERVED_22_BIT_EXT"/>
</require>
</extension>
<extension name="VK_INTEL_extension_272" number="272" type="device" author="INTEL" contact="Jason Ekstrand @jekstrand" supported="disabled">
<require>
<enum value="0" name="VK_INTEL_EXTENSION_272_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_extension_272&quot;" name="VK_INTEL_extension_272"/>
+ <enum value="&quot;VK_INTEL_extension_272&quot;" name="VK_INTEL_EXTENSION_272_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_INTEL_extension_273" number="273" type="device" author="INTEL" contact="Jason Ekstrand @jekstrand" supported="disabled">
<require>
<enum value="0" name="VK_INTEL_EXTENSION_273_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_extension_273&quot;" name="VK_INTEL_extension_273"/>
+ <enum value="&quot;VK_INTEL_extension_273&quot;" name="VK_INTEL_EXTENSION_273_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_INTEL_extension_274" number="274" type="device" author="INTEL" contact="Jason Ekstrand @jekstrand" supported="disabled">
+ <extension name="VK_EXT_shader_atomic_float2" number="274" type="device" requires="VK_EXT_shader_atomic_float" author="EXT" contact="Jason Ekstrand @jekstrand" supported="vulkan">
<require>
- <enum value="0" name="VK_INTEL_EXTENSION_274_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_extension_274&quot;" name="VK_INTEL_extension_274"/>
+ <enum value="1" name="VK_EXT_SHADER_ATOMIC_FLOAT_2_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_shader_atomic_float2&quot;" name="VK_EXT_SHADER_ATOMIC_FLOAT_2_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_2_FEATURES_EXT"/>
+ <type name="VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT"/>
</require>
</extension>
<extension name="VK_KHR_extension_275" number="275" type="instance" author="KHR" contact="Lionel Landwerlin @llandwerlin" supported="disabled">
<require>
<enum value="0" name="VK_KHR_EXTENSION_275_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_extension_275&quot;" name="VK_KHR_extension_275"/>
+ <enum value="&quot;VK_KHR_extension_275&quot;" name="VK_KHR_EXTENSION_275_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_KHR_extension_276" number="276" type="device" author="KHR" contact="James Jones @cubanismo" supported="disabled">
<require>
<enum value="0" name="VK_KHR_EXTENSION_276_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_extension_276&quot;" name="VK_KHR_extension_276"/>
+ <enum value="&quot;VK_KHR_extension_276&quot;" name="VK_KHR_EXTENSION_276_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_EXT_shader_demote_to_helper_invocation" number="277" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Jeff Bolz @jeffbolznv" supported="vulkan">
+ <extension name="VK_EXT_shader_demote_to_helper_invocation" number="277" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Jeff Bolz @jeffbolznv" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
<enum value="1" name="VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_shader_demote_to_helper_invocation&quot;" name="VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES"/>
<type name="VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT"/>
</require>
</extension>
- <extension name="VK_NV_device_generated_commands" number="278" type="device" requiresCore="1.1" author="NV" contact="Christoph Kubisch @pixeljetstream" supported="vulkan">
+ <extension name="VK_NV_device_generated_commands" number="278" type="device" requiresCore="1.1" requires="VK_KHR_buffer_device_address" author="NV" contact="Christoph Kubisch @pixeljetstream" supported="vulkan">
<require>
+ <comment>
+ This extension requires buffer_device_address functionality.
+ VK_EXT_buffer_device_address is also acceptable, but since it is deprecated the KHR version is preferred.
+ </comment>
<enum value="3" name="VK_NV_DEVICE_GENERATED_COMMANDS_SPEC_VERSION"/>
<enum value="&quot;VK_NV_device_generated_commands&quot;" name="VK_NV_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_PROPERTIES_NV"/>
@@ -13831,37 +17315,45 @@ typedef void <name>CAMetalLayer</name>;
<command name="vkDestroyIndirectCommandsLayoutNV"/>
</require>
</extension>
- <extension name="VK_NV_extension_279" number="279" type="device" author="NV" contact="Christoph Kubisch @pixeljetstream" supported="disabled">
+ <extension name="VK_NV_inherited_viewport_scissor" number="279" type="device" author="NV" contact="David Zhao Akeley @akeley98" supported="vulkan">
<require>
- <enum value="0" name="VK_NV_EXTENSION_279_SPEC_VERSION"/>
- <enum value="&quot;VK_NV_extension_279&quot;" name="VK_NV_extension_279"/>
+ <enum value="1" name="VK_NV_INHERITED_VIEWPORT_SCISSOR_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_inherited_viewport_scissor&quot;" name="VK_NV_INHERITED_VIEWPORT_SCISSOR_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INHERITED_VIEWPORT_SCISSOR_FEATURES_NV"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_VIEWPORT_SCISSOR_INFO_NV"/>
+ <type name="VkPhysicalDeviceInheritedViewportScissorFeaturesNV"/>
+ <type name="VkCommandBufferInheritanceViewportScissorInfoNV"/>
</require>
</extension>
<extension name="VK_KHR_extension_280" number="280" type="device" author="KHR" contact="Kevin Petit @kevinpetit" supported="disabled">
<require>
<enum value="0" name="VK_KHR_EXTENSION_280_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_extension_280&quot;" name="VK_KHR_extension_280"/>
+ <enum value="&quot;VK_KHR_extension_280&quot;" name="VK_KHR_EXTENSION_280_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_ARM_extension_281" number="281" type="device" author="ARM" contact="Kevin Petit @kevinpetit" supported="disabled">
+ <extension name="VK_KHR_shader_integer_dot_product" number="281" type="device" author="KHR" requires="VK_KHR_get_physical_device_properties2" contact="Kevin Petit @kevinpetit" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
- <enum value="0" name="VK_ARM_EXTENSION_281_SPEC_VERSION"/>
- <enum value="&quot;VK_ARM_extension_281&quot;" name="VK_ARM_extension_281"/>
+ <enum value="1" name="VK_KHR_SHADER_INTEGER_DOT_PRODUCT_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_shader_integer_dot_product&quot;" name="VK_KHR_SHADER_INTEGER_DOT_PRODUCT_EXTENSION_NAME"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES_KHR" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES_KHR" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES"/>
+ <type name="VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR"/>
+ <type name="VkPhysicalDeviceShaderIntegerDotProductPropertiesKHR"/>
</require>
</extension>
- <extension name="VK_EXT_texel_buffer_alignment" number="282" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Jeff Bolz @jeffbolznv" supported="vulkan">
+ <extension name="VK_EXT_texel_buffer_alignment" number="282" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Jeff Bolz @jeffbolznv" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
<enum value="1" name="VK_EXT_TEXEL_BUFFER_ALIGNMENT_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_texel_buffer_alignment&quot;" name="VK_EXT_TEXEL_BUFFER_ALIGNMENT_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT"/>
- <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT"/>
- <type name="VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT" comment="Not promoted to 1.3"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES"/>
+ <type name="VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT" comment="Not promoted to 1.3"/>
<type name="VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT"/>
</require>
</extension>
<extension name="VK_QCOM_render_pass_transform" number="283" type="device" requires="VK_KHR_swapchain,VK_KHR_surface" author="QCOM" contact="Jeff Leger @jackohound" supported="vulkan">
<require>
- <enum value="1" name="VK_QCOM_RENDER_PASS_TRANSFORM_SPEC_VERSION"/>
+ <enum value="3" name="VK_QCOM_RENDER_PASS_TRANSFORM_SPEC_VERSION"/>
<enum value="&quot;VK_QCOM_render_pass_transform&quot;" name="VK_QCOM_RENDER_PASS_TRANSFORM_EXTENSION_NAME"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDER_PASS_TRANSFORM_INFO_QCOM"/>
<enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_RENDER_PASS_TRANSFORM_BEGIN_INFO_QCOM"/>
@@ -13873,12 +17365,12 @@ typedef void <name>CAMetalLayer</name>;
<extension name="VK_EXT_extension_284" number="284" type="device" author="EXT" contact="Samuel Pitoiset @hakzsam" supported="disabled">
<require>
<enum value="0" name="VK_EXT_EXTENSION_284_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_extension_284&quot;" name="VK_EXT_extension_284"/>
+ <enum value="&quot;VK_EXT_extension_284&quot;" name="VK_EXT_EXTENSION_284_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_EXT_device_memory_report" number="285" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Yiwei Zhang @zhangyiwei" specialuse="devtools" supported="vulkan">
<require>
- <enum value="1" name="VK_EXT_DEVICE_MEMORY_REPORT_SPEC_VERSION"/>
+ <enum value="2" name="VK_EXT_DEVICE_MEMORY_REPORT_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_device_memory_report&quot;" name="VK_EXT_DEVICE_MEMORY_REPORT_EXTENSION_NAME"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_MEMORY_REPORT_FEATURES_EXT"/>
<enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DEVICE_DEVICE_MEMORY_REPORT_CREATE_INFO_EXT"/>
@@ -13891,10 +17383,12 @@ typedef void <name>CAMetalLayer</name>;
<type name="PFN_vkDeviceMemoryReportCallbackEXT"/>
</require>
</extension>
- <extension name="VK_EXT_extension_286" number="286" type="instance" author="EXT" contact="Drew DeVault sir@cmpwn.com" supported="disabled">
+ <extension name="VK_EXT_acquire_drm_display" number="286" type="instance" requires="VK_EXT_direct_mode_display" author="EXT" contact="Drew DeVault sir@cmpwn.com" supported="vulkan">
<require>
- <enum value="0" name="VK_EXT_EXTENSION_286_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_extension_286&quot;" name="VK_EXT_extension_286"/>
+ <enum value="1" name="VK_EXT_ACQUIRE_DRM_DISPLAY_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_acquire_drm_display&quot;" name="VK_EXT_ACQUIRE_DRM_DISPLAY_EXTENSION_NAME"/>
+ <command name="vkAcquireDrmDisplayEXT"/>
+ <command name="vkGetDrmDisplayEXT"/>
</require>
</extension>
<extension name="VK_EXT_robustness2" number="287" type="device" author="EXT" contact="Liam Middlebrook @liam-middlebrook" supported="vulkan">
@@ -13907,7 +17401,7 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkPhysicalDeviceRobustness2PropertiesEXT"/>
</require>
</extension>
- <extension name="VK_EXT_custom_border_color" number="288" type="device" author="EXT" contact="Liam Middlebrook @liam-middlebrook" supported="vulkan">
+ <extension name="VK_EXT_custom_border_color" number="288" type="device" author="EXT" contact="Liam Middlebrook @liam-middlebrook" specialuse="glemulation,d3demulation" supported="vulkan">
<require>
<enum value="12" name="VK_EXT_CUSTOM_BORDER_COLOR_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_custom_border_color&quot;" name="VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME"/>
@@ -13989,32 +17483,35 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_NV_extension_293&quot;" name="VK_NV_EXTENSION_293_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_KHR_shader_non_semantic_info" number="294" type="device" author="KHR" contact="Baldur Karlsson @baldurk" supported="vulkan">
+ <extension name="VK_KHR_shader_non_semantic_info" number="294" type="device" author="KHR" contact="Baldur Karlsson @baldurk" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
<enum value="1" name="VK_KHR_SHADER_NON_SEMANTIC_INFO_SPEC_VERSION"/>
<enum value="&quot;VK_KHR_shader_non_semantic_info&quot;" name="VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_KHR_extension_295" number="295" author="KHR" contact="Keith Packard @keithp" supported="disabled">
+ <extension name="VK_KHR_present_id" number="295" type="device" requires="VK_KHR_swapchain" author="KHR" contact="Keith Packard @keithp" supported="vulkan">
<require>
- <enum value="0" name="VK_KHR_EXTENSION_295_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_extension_295&quot;" name="VK_KHR_EXTENSION_295_EXTENSION_NAME"/>
+ <enum value="1" name="VK_KHR_PRESENT_ID_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_present_id&quot;" name="VK_KHR_PRESENT_ID_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PRESENT_ID_KHR"/>
+ <type name="VkPresentIdKHR"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR"/>
+ <type name="VkPhysicalDevicePresentIdFeaturesKHR"/>
</require>
</extension>
- <extension name="VK_EXT_private_data" number="296" type="device" author="NV" contact="Matthew Rusch @mattruschnv" supported="vulkan">
+ <extension name="VK_EXT_private_data" number="296" type="device" author="NV" contact="Matthew Rusch @mattruschnv" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
<enum value="1" name="VK_EXT_PRIVATE_DATA_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_private_data&quot;" name="VK_EXT_PRIVATE_DATA_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES_EXT"/>
- <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO_EXT"/>
- <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PRIVATE_DATA_SLOT_CREATE_INFO_EXT"/>
- <enum offset="0" extends="VkObjectType" name="VK_OBJECT_TYPE_PRIVATE_DATA_SLOT_EXT"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES_EXT" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO_EXT" alias="VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PRIVATE_DATA_SLOT_CREATE_INFO_EXT" alias="VK_STRUCTURE_TYPE_PRIVATE_DATA_SLOT_CREATE_INFO"/>
+ <enum extends="VkObjectType" name="VK_OBJECT_TYPE_PRIVATE_DATA_SLOT_EXT" alias="VK_OBJECT_TYPE_PRIVATE_DATA_SLOT"/>
<type name="VkPhysicalDevicePrivateDataFeaturesEXT"/>
<type name="VkDevicePrivateDataCreateInfoEXT"/>
<type name="VkPrivateDataSlotCreateInfoEXT"/>
<type name="VkPrivateDataSlotEXT"/>
- <type name="VkPrivateDataSlotCreateFlagsEXT"/>
- <type name="VkPrivateDataSlotCreateFlagBitsEXT"/>
+ <type name="VkPrivateDataSlotCreateFlagsEXT" comment="Will add VkPrivateDataSlotCreateFlagBits when bits are defined in the future"/>
<command name="vkCreatePrivateDataSlotEXT"/>
<command name="vkDestroyPrivateDataSlotEXT"/>
<command name="vkSetPrivateDataEXT"/>
@@ -14028,39 +17525,79 @@ typedef void <name>CAMetalLayer</name>;
<enum bitpos="3" extends="VkPipelineShaderStageCreateFlagBits" name="VK_PIPELINE_SHADER_STAGE_CREATE_RESERVED_3_BIT_KHR"/>
</require>
</extension>
- <extension name="VK_EXT_pipeline_creation_cache_control" number="298" type="device" author="AMD" contact="Gregory Grebe @grgrebe_amd" supported="vulkan">
+ <extension name="VK_EXT_pipeline_creation_cache_control" number="298" type="device" author="AMD" contact="Gregory Grebe @grgrebe_amd" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
- <enum value="3" name="VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_pipeline_creation_cache_control&quot;"
- name="VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType"
- name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES_EXT"/>
+ <enum value="3" name="VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_pipeline_creation_cache_control&quot;" name="VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES_EXT" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES"/>
<type name="VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT"/>
- <enum bitpos="8" extends="VkPipelineCreateFlagBits"
- name="VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT"/>
- <enum bitpos="9" extends="VkPipelineCreateFlagBits"
- name="VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT_EXT"/>
- <enum extends="VkResult" offset="0" name="VK_PIPELINE_COMPILE_REQUIRED_EXT"/>
- <enum extends="VkResult" name="VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT" alias="VK_PIPELINE_COMPILE_REQUIRED_EXT"/>
- <enum bitpos="0" extends="VkPipelineCacheCreateFlagBits"
- name="VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT_EXT"/>
+ <enum extends="VkPipelineCreateFlagBits" name="VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT" alias="VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT"/>
+ <enum extends="VkPipelineCreateFlagBits" name="VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT_EXT" alias="VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT"/>
+ <enum extends="VkResult" name="VK_PIPELINE_COMPILE_REQUIRED_EXT" alias="VK_PIPELINE_COMPILE_REQUIRED"/>
+ <enum extends="VkResult" name="VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT" alias="VK_PIPELINE_COMPILE_REQUIRED"/>
+ <enum extends="VkPipelineCacheCreateFlagBits" name="VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT_EXT" alias="VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT"/>
+ <type name="VkPipelineCacheCreateFlagBits" comment="This is a temporary workaround for processors not recognizing that VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT_EXT above also requires this type"/>
</require>
</extension>
<extension name="VK_KHR_extension_299" number="299" author="KHR" contact="Mark Bellamy @mark.bellamy_arm" supported="disabled">
<require>
<enum value="0" name="VK_KHR_EXTENSION_299_SPEC_VERSION"/>
<enum value="&quot;VK_KHR_extension_299&quot;" name="VK_KHR_EXTENSION_299_EXTENSION_NAME"/>
+ <enum bitpos="2" extends="VkMemoryHeapFlagBits" name="VK_MEMORY_HEAP_RESERVED_2_BIT_KHR"/>
+ <enum extends="VkPipelineCacheCreateFlagBits" name="VK_PIPELINE_CACHE_CREATE_RESERVED_1_BIT_KHR" alias="VK_PIPELINE_CACHE_CREATE_RESERVED_1_BIT_EXT"/>
+ <enum bitpos="2" extends="VkPipelineCacheCreateFlagBits" name="VK_PIPELINE_CACHE_CREATE_RESERVED_2_BIT_KHR"/>
+ </require>
+ </extension>
+ <extension name="VK_KHR_video_encode_queue" number="300" type="device" requires="VK_KHR_video_queue,VK_KHR_synchronization2" author="KHR" contact="Ahmed Abdelkhalek @aabdelkh" provisional="true" platform="provisional" supported="vulkan">
+ <require>
+ <enum value="5" name="VK_KHR_VIDEO_ENCODE_QUEUE_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_video_encode_queue&quot;" name="VK_KHR_VIDEO_ENCODE_QUEUE_EXTENSION_NAME"/>
+ <!-- VkPipelineStageFlagBits bitpos="27" is reserved by this extension, but not used -->
+ <enum bitpos="27" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS" />
+ <enum bitpos="37" extends="VkAccessFlagBits2" name="VK_ACCESS_2_VIDEO_ENCODE_READ_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS" />
+ <enum bitpos="38" extends="VkAccessFlagBits2" name="VK_ACCESS_2_VIDEO_ENCODE_WRITE_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_INFO_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_RATE_CONTROL_INFO_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_RATE_CONTROL_LAYER_INFO_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VIDEO_ENCODE_CAPABILITIES_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="6" extends="VkQueueFlagBits" name="VK_QUEUE_VIDEO_ENCODE_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="15" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_VIDEO_ENCODE_DST_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="16" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_VIDEO_ENCODE_SRC_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="13" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_VIDEO_ENCODE_DST_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="14" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="15" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="27" extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_VIDEO_ENCODE_INPUT_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="28" extends="VkFormatFeatureFlagBits" name="VK_FORMAT_FEATURE_VIDEO_ENCODE_DPB_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="0" extends="VkImageLayout" name="VK_IMAGE_LAYOUT_VIDEO_ENCODE_DST_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="1" extends="VkImageLayout" name="VK_IMAGE_LAYOUT_VIDEO_ENCODE_SRC_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="2" extends="VkImageLayout" name="VK_IMAGE_LAYOUT_VIDEO_ENCODE_DPB_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum offset="0" extends="VkQueryType" name="VK_QUERY_TYPE_VIDEO_ENCODE_BITSTREAM_BUFFER_RANGE_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+
+ <type name="VkVideoEncodeFlagBitsKHR"/>
+ <type name="VkVideoEncodeFlagsKHR"/>
+ <type name="VkVideoEncodeInfoKHR"/>
+
+ <type name="VkVideoEncodeCapabilityFlagBitsKHR"/>
+ <type name="VkVideoEncodeCapabilityFlagsKHR"/>
+ <type name="VkVideoEncodeCapabilitiesKHR"/>
+
+ <type name="VkVideoEncodeRateControlFlagBitsKHR"/>
+ <type name="VkVideoEncodeRateControlFlagsKHR"/>
+ <type name="VkVideoEncodeRateControlModeFlagBitsKHR"/>
+ <type name="VkVideoEncodeRateControlModeFlagsKHR"/>
+ <type name="VkVideoEncodeRateControlInfoKHR"/>
+ <type name="VkVideoEncodeRateControlLayerInfoKHR"/>
+
+ <command name="vkCmdEncodeVideoKHR"/>
</require>
- </extension>
- <extension name="VK_KHR_extension_300" number="300" author="KHR" contact="Aidan Fabius @afabius" supported="disabled">
- <require>
- <enum value="0" name="VK_KHR_EXTENSION_300_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_extension_300&quot;" name="VK_KHR_EXTENSION_300_EXTENSION_NAME"/>
+ <require extension="VK_KHR_format_feature_flags2">
+ <enum bitpos="27" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_VIDEO_ENCODE_INPUT_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
+ <enum bitpos="28" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_VIDEO_ENCODE_DPB_BIT_KHR" protect="VK_ENABLE_BETA_EXTENSIONS"/>
</require>
</extension>
<extension name="VK_NV_device_diagnostics_config" number="301" type="device" requires="VK_KHR_get_physical_device_properties2" author="NV" contact="Kedarnath Thangudu @kthangudu" supported="vulkan">
<require>
- <enum value="1" name="VK_NV_DEVICE_DIAGNOSTICS_CONFIG_SPEC_VERSION"/>
+ <enum value="2" name="VK_NV_DEVICE_DIAGNOSTICS_CONFIG_SPEC_VERSION"/>
<enum value="&quot;VK_NV_device_diagnostics_config&quot;" name="VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DIAGNOSTICS_CONFIG_FEATURES_NV"/>
<enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV"/>
@@ -14072,39 +17609,39 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_QCOM_render_pass_store_ops" number="302" type="device" author="QCOM" contact="Bill Licea-Kane @wwlk" supported="vulkan">
<require>
- <enum value="2" name="VK_QCOM_render_pass_store_ops_SPEC_VERSION"/>
- <enum value="&quot;VK_QCOM_render_pass_store_ops&quot;" name="VK_QCOM_render_pass_store_ops_EXTENSION_NAME"/>
- <enum offset="0" extends="VkAttachmentStoreOp" name="VK_ATTACHMENT_STORE_OP_NONE_QCOM"/>
+ <enum value="2" name="VK_QCOM_RENDER_PASS_STORE_OPS_SPEC_VERSION"/>
+ <enum value="&quot;VK_QCOM_render_pass_store_ops&quot;" name="VK_QCOM_RENDER_PASS_STORE_OPS_EXTENSION_NAME"/>
+ <enum extends="VkAttachmentStoreOp" name="VK_ATTACHMENT_STORE_OP_NONE_QCOM" alias="VK_ATTACHMENT_STORE_OP_NONE"/>
</require>
</extension>
<extension name="VK_QCOM_extension_303" number="303" author="QCOM" contact="Bill Licea-Kane @wwlk" supported="disabled">
<require>
- <enum value="0" name="VK_QCOM_extension_303_SPEC_VERSION"/>
- <enum value="&quot;VK_QCOM_extension_303&quot;" name="VK_QCOM_extension_303_EXTENSION_NAME"/>
+ <enum value="0" name="VK_QCOM_EXTENSION_303_SPEC_VERSION"/>
+ <enum value="&quot;VK_QCOM_extension_303&quot;" name="VK_QCOM_EXTENSION_303_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_QCOM_extension_304" number="304" author="QCOM" contact="Bill Licea-Kane @wwlk" supported="disabled">
<require>
- <enum value="0" name="VK_QCOM_extension_304_SPEC_VERSION"/>
- <enum value="&quot;VK_QCOM_extension_304&quot;" name="VK_QCOM_extension_304_EXTENSION_NAME"/>
+ <enum value="0" name="VK_QCOM_EXTENSION_304_SPEC_VERSION"/>
+ <enum value="&quot;VK_QCOM_extension_304&quot;" name="VK_QCOM_EXTENSION_304_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_QCOM_extension_305" number="305" author="QCOM" contact="Bill Licea-Kane @wwlk" supported="disabled">
<require>
- <enum value="0" name="VK_QCOM_extension_305_SPEC_VERSION"/>
- <enum value="&quot;VK_QCOM_extension_305&quot;" name="VK_QCOM_extension_305_EXTENSION_NAME"/>
+ <enum value="0" name="VK_QCOM_EXTENSION_305_SPEC_VERSION"/>
+ <enum value="&quot;VK_QCOM_extension_305&quot;" name="VK_QCOM_EXTENSION_305_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_QCOM_extension_306" number="306" author="QCOM" contact="Bill Licea-Kane @wwlk" supported="disabled">
<require>
- <enum value="0" name="VK_QCOM_extension_306_SPEC_VERSION"/>
- <enum value="&quot;VK_QCOM_extension_306&quot;" name="VK_QCOM_extension_306_EXTENSION_NAME"/>
+ <enum value="0" name="VK_QCOM_EXTENSION_306_SPEC_VERSION"/>
+ <enum value="&quot;VK_QCOM_extension_306&quot;" name="VK_QCOM_EXTENSION_306_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_QCOM_extension_307" number="307" author="QCOM" contact="Bill Licea-Kane @wwlk" supported="disabled">
<require>
- <enum value="0" name="VK_QCOM_extension_307_SPEC_VERSION"/>
- <enum value="&quot;VK_QCOM_extension_307&quot;" name="VK_QCOM_extension_307_EXTENSION_NAME"/>
+ <enum value="0" name="VK_QCOM_EXTENSION_307_SPEC_VERSION"/>
+ <enum value="&quot;VK_QCOM_extension_307&quot;" name="VK_QCOM_EXTENSION_307_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_NV_extension_308" number="308" type="device" author="NV" contact="Tristan Lorach @tlorach" supported="disabled">
@@ -14117,13 +17654,12 @@ typedef void <name>CAMetalLayer</name>;
<require>
<enum value="0" name="VK_KHR_EXTENSION_309_SPEC_VERSION"/>
<enum value="&quot;VK_KHR_extension_309&quot;" name="VK_KHR_EXTENSION_309_EXTENSION_NAME"/>
- <enum bitpos="2" extends="VkMemoryHeapFlagBits" name="VK_MEMORY_HEAP_RESERVED_2_BIT_KHR"/>
</require>
</extension>
<extension name="VK_QCOM_extension_310" number="310" author="QCOM" contact="Jeff Leger @jackohound" supported="disabled">
<require>
- <enum value="0" name="VK_QCOM_extension_310_SPEC_VERSION"/>
- <enum value="&quot;VK_QCOM_extension_310&quot;" name="VK_QCOM_extension_310_EXTENSION_NAME"/>
+ <enum value="0" name="VK_QCOM_EXTENSION_310_SPEC_VERSION"/>
+ <enum value="&quot;VK_QCOM_extension_310&quot;" name="VK_QCOM_EXTENSION_310_EXTENSION_NAME"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_RESERVED_QCOM"/>
</require>
</extension>
@@ -14133,10 +17669,43 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_NV_extension_311&quot;" name="VK_NV_EXTENSION_311_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_EXT_extension_312" number="312" author="MVK" contact="Bill Hollings @billhollings" supported="disabled">
- <require>
- <enum value="0" name="VK_EXT_EXTENSION_312_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_extension_312&quot;" name="VK_EXT_EXTENSION_312_EXTENSION_NAME"/>
+ <extension name="VK_EXT_metal_objects" number="312" type="device" platform="metal" supported="vulkan" author="EXT" contact="Bill Hollings @billhollings">
+ <require>
+ <enum value="1" name="VK_EXT_METAL_OBJECTS_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_metal_objects&quot;" name="VK_EXT_METAL_OBJECTS_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_EXPORT_METAL_OBJECT_CREATE_INFO_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_EXPORT_METAL_OBJECTS_INFO_EXT"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_EXPORT_METAL_DEVICE_INFO_EXT"/>
+ <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_EXPORT_METAL_COMMAND_QUEUE_INFO_EXT"/>
+ <enum offset="4" extends="VkStructureType" name="VK_STRUCTURE_TYPE_EXPORT_METAL_BUFFER_INFO_EXT"/>
+ <enum offset="5" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMPORT_METAL_BUFFER_INFO_EXT"/>
+ <enum offset="6" extends="VkStructureType" name="VK_STRUCTURE_TYPE_EXPORT_METAL_TEXTURE_INFO_EXT"/>
+ <enum offset="7" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMPORT_METAL_TEXTURE_INFO_EXT"/>
+ <enum offset="8" extends="VkStructureType" name="VK_STRUCTURE_TYPE_EXPORT_METAL_IO_SURFACE_INFO_EXT"/>
+ <enum offset="9" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMPORT_METAL_IO_SURFACE_INFO_EXT"/>
+ <enum offset="10" extends="VkStructureType" name="VK_STRUCTURE_TYPE_EXPORT_METAL_SHARED_EVENT_INFO_EXT"/>
+ <enum offset="11" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMPORT_METAL_SHARED_EVENT_INFO_EXT"/>
+ <type name="VkExportMetalObjectTypeFlagBitsEXT"/>
+ <type name="VkExportMetalObjectTypeFlagsEXT"/>
+ <type name="VkExportMetalObjectCreateInfoEXT"/>
+ <type name="VkExportMetalObjectsInfoEXT"/>
+ <type name="VkExportMetalDeviceInfoEXT"/>
+ <type name="VkExportMetalCommandQueueInfoEXT"/>
+ <type name="VkExportMetalBufferInfoEXT"/>
+ <type name="VkImportMetalBufferInfoEXT"/>
+ <type name="VkExportMetalTextureInfoEXT"/>
+ <type name="VkImportMetalTextureInfoEXT"/>
+ <type name="VkExportMetalIOSurfaceInfoEXT"/>
+ <type name="VkImportMetalIOSurfaceInfoEXT"/>
+ <type name="VkExportMetalSharedEventInfoEXT"/>
+ <type name="VkImportMetalSharedEventInfoEXT"/>
+ <type name="MTLDevice_id"/>
+ <type name="MTLCommandQueue_id"/>
+ <type name="MTLBuffer_id"/>
+ <type name="MTLTexture_id"/>
+ <type name="MTLSharedEvent_id"/>
+ <type name="IOSurfaceRef"/>
+ <command name="vkExportMetalObjectsEXT"/>
</require>
</extension>
<extension name="VK_EXT_extension_313" number="313" author="MVK" contact="Bill Hollings @billhollings" supported="disabled">
@@ -14151,10 +17720,102 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_AMD_extension_314&quot;" name="VK_AMD_EXTENSION_314_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_AMD_extension_315" number="315" author="AMD" contact="Martin Dinkov @mdinkov" supported="disabled">
- <require>
- <enum value="0" name="VK_AMD_EXTENSION_315_SPEC_VERSION"/>
- <enum value="&quot;VK_AMD_extension_315&quot;" name="VK_AMD_EXTENSION_315_EXTENSION_NAME"/>
+ <extension name="VK_KHR_synchronization2" number="315" type="device" author="KHR" requires="VK_KHR_get_physical_device_properties2" contact="Tobias Hector @tobski" supported="vulkan" promotedto="VK_VERSION_1_3">
+ <require>
+ <enum value="1" name="VK_KHR_SYNCHRONIZATION_2_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_synchronization2&quot;" name="VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_MEMORY_BARRIER_2_KHR" alias="VK_STRUCTURE_TYPE_MEMORY_BARRIER_2"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2_KHR" alias="VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2_KHR" alias="VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_DEPENDENCY_INFO_KHR" alias="VK_STRUCTURE_TYPE_DEPENDENCY_INFO"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR" alias="VK_STRUCTURE_TYPE_SUBMIT_INFO_2"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR" alias="VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO_KHR" alias="VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES"/>
+ <enum extends="VkEventCreateFlagBits" name="VK_EVENT_CREATE_DEVICE_ONLY_BIT_KHR" alias="VK_EVENT_CREATE_DEVICE_ONLY_BIT"/>
+ <enum extends="VkImageLayout" name="VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR" alias="VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL"/>
+ <enum extends="VkImageLayout" name="VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR" alias="VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL"/>
+ <enum extends="VkPipelineStageFlagBits" name="VK_PIPELINE_STAGE_NONE_KHR" alias="VK_PIPELINE_STAGE_NONE"/>
+ <enum extends="VkAccessFlagBits" name="VK_ACCESS_NONE_KHR" alias="VK_ACCESS_NONE"/>
+ <type name="VkFlags64"/>
+ <type name="VkPipelineStageFlags2KHR"/>
+ <type name="VkPipelineStageFlagBits2KHR"/>
+ <type name="VkAccessFlags2KHR"/>
+ <type name="VkAccessFlagBits2KHR"/>
+ <type name="VkMemoryBarrier2KHR"/>
+ <type name="VkBufferMemoryBarrier2KHR"/>
+ <type name="VkImageMemoryBarrier2KHR"/>
+ <type name="VkDependencyInfoKHR"/>
+ <type name="VkSubmitInfo2KHR"/>
+ <type name="VkSemaphoreSubmitInfoKHR"/>
+ <type name="VkCommandBufferSubmitInfoKHR"/>
+ <type name="VkSubmitFlagBitsKHR"/>
+ <type name="VkSubmitFlagsKHR"/>
+ <type name="VkPhysicalDeviceSynchronization2FeaturesKHR"/>
+ <command name="vkCmdSetEvent2KHR"/>
+ <command name="vkCmdResetEvent2KHR"/>
+ <command name="vkCmdWaitEvents2KHR"/>
+ <command name="vkCmdPipelineBarrier2KHR"/>
+ <command name="vkCmdWriteTimestamp2KHR"/>
+ <command name="vkQueueSubmit2KHR"/>
+ </require>
+ <require extension="VK_EXT_transform_feedback">
+ <enum bitpos="24" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT"/>
+ <enum bitpos="25" extends="VkAccessFlagBits2" name="VK_ACCESS_2_TRANSFORM_FEEDBACK_WRITE_BIT_EXT"/>
+ <enum bitpos="26" extends="VkAccessFlagBits2" name="VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT"/>
+ <enum bitpos="27" extends="VkAccessFlagBits2" name="VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT"/>
+ </require>
+ <require extension="VK_EXT_conditional_rendering">
+ <enum bitpos="18" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_CONDITIONAL_RENDERING_BIT_EXT" comment="A pipeline stage for conditional rendering predicate fetch"/>
+ <enum bitpos="20" extends="VkAccessFlagBits2" name="VK_ACCESS_2_CONDITIONAL_RENDERING_READ_BIT_EXT" comment="read access flag for reading conditional rendering predicate"/>
+ </require>
+ <require extension="VK_NV_device_generated_commands">
+ <enum bitpos="17" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_COMMAND_PREPROCESS_BIT_NV"/>
+ <enum bitpos="17" extends="VkAccessFlagBits2" name="VK_ACCESS_2_COMMAND_PREPROCESS_READ_BIT_NV"/>
+ <enum bitpos="18" extends="VkAccessFlagBits2" name="VK_ACCESS_2_COMMAND_PREPROCESS_WRITE_BIT_NV"/>
+ </require>
+ <require extension="VK_KHR_fragment_shading_rate">
+ <enum bitpos="22" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR"/>
+ <enum bitpos="23" extends="VkAccessFlagBits2" name="VK_ACCESS_2_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR"/>
+ </require>
+ <require extension="VK_NV_shading_rate_image">
+ <enum extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_SHADING_RATE_IMAGE_BIT_NV" alias="VK_PIPELINE_STAGE_2_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR"/>
+ <enum extends="VkAccessFlagBits2" name="VK_ACCESS_2_SHADING_RATE_IMAGE_READ_BIT_NV" alias="VK_ACCESS_2_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR"/>
+ </require>
+ <require extension="VK_KHR_acceleration_structure">
+ <enum bitpos="25" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR"/>
+ <enum bitpos="21" extends="VkAccessFlagBits2" name="VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR"/>
+ <enum bitpos="22" extends="VkAccessFlagBits2" name="VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR"/>
+ </require>
+ <require extension="VK_KHR_ray_tracing_pipeline">
+ <enum bitpos="21" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR"/>
+ </require>
+ <require extension="VK_NV_ray_tracing">
+ <enum extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_NV" alias="VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR"/>
+ <enum extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_NV" alias="VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR"/>
+ <enum extends="VkAccessFlagBits2" name="VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_NV" alias="VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR"/>
+ <enum extends="VkAccessFlagBits2" name="VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_NV" alias="VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR"/>
+ </require>
+ <require extension="VK_EXT_fragment_density_map">
+ <enum bitpos="23" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_FRAGMENT_DENSITY_PROCESS_BIT_EXT"/>
+ <enum bitpos="24" extends="VkAccessFlagBits2" name="VK_ACCESS_2_FRAGMENT_DENSITY_MAP_READ_BIT_EXT"/>
+ </require>
+ <require extension="VK_EXT_blend_operation_advanced">
+ <enum bitpos="19" extends="VkAccessFlagBits2" name="VK_ACCESS_2_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT"/>
+ </require>
+ <require extension="VK_NV_mesh_shader">
+ <enum bitpos="19" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_TASK_SHADER_BIT_NV"/>
+ <enum bitpos="20" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_MESH_SHADER_BIT_NV"/>
+ </require>
+ <require extension="VK_AMD_buffer_marker">
+ <command name="vkCmdWriteBufferMarker2AMD"/>
+ </require>
+ <require extension="VK_NV_device_diagnostic_checkpoints">
+ <type name="VkQueueFamilyCheckpointProperties2NV"/>
+ <type name="VkCheckpointData2NV"/>
+ <command name="vkGetQueueCheckpointData2NV"/>
+ <enum offset="8" extends="VkStructureType" name="VK_STRUCTURE_TYPE_QUEUE_FAMILY_CHECKPOINT_PROPERTIES_2_NV"/>
+ <enum offset="9" extends="VkStructureType" name="VK_STRUCTURE_TYPE_CHECKPOINT_DATA_2_NV"/>
</require>
</extension>
<extension name="VK_AMD_extension_316" number="316" author="AMD" contact="Martin Dinkov @mdinkov" supported="disabled">
@@ -14167,6 +17828,16 @@ typedef void <name>CAMetalLayer</name>;
<require>
<enum value="0" name="VK_AMD_EXTENSION_317_SPEC_VERSION"/>
<enum value="&quot;VK_AMD_extension_317&quot;" name="VK_AMD_EXTENSION_317_EXTENSION_NAME"/>
+ <enum bitpos="4" extends="VkDescriptorSetLayoutCreateFlagBits" name="VK_DESCRIPTOR_SET_LAYOUT_CREATE_RESERVED_4_BIT_AMD"/>
+ <enum bitpos="5" extends="VkDescriptorSetLayoutCreateFlagBits" name="VK_DESCRIPTOR_SET_LAYOUT_CREATE_RESERVED_5_BIT_AMD"/>
+ <enum bitpos="21" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_RESERVED_21_BIT_AMD"/>
+ <enum bitpos="22" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_RESERVED_22_BIT_AMD"/>
+ <enum bitpos="5" extends="VkBufferCreateFlagBits" name="VK_BUFFER_CREATE_RESERVED_5_BIT_AMD"/>
+ <enum bitpos="16" extends="VkImageCreateFlagBits" name="VK_IMAGE_CREATE_RESERVED_16_BIT_AMD"/>
+ <enum bitpos="3" extends="VkSamplerCreateFlagBits" name="VK_SAMPLER_CREATE_RESERVED_3_BIT_AMD"/>
+ <enum bitpos="41" extends="VkAccessFlagBits2" name="VK_ACCESS_2_RESERVED_41_BIT_AMD"/>
+ <enum bitpos="2" extends="VkImageViewCreateFlagBits" name="VK_IMAGE_VIEW_CREATE_RESERVED_2_BIT_AMD"/>
+ <enum bitpos="3" extends="VkAccelerationStructureCreateFlagBitsKHR" name="VK_ACCELERATION_STRUCTURE_CREATE_RESERVED_3_BIT_AMD"/>
</require>
</extension>
<extension name="VK_AMD_extension_318" number="318" author="AMD" contact="Martin Dinkov @mdinkov" supported="disabled">
@@ -14179,6 +17850,8 @@ typedef void <name>CAMetalLayer</name>;
<require>
<enum value="0" name="VK_AMD_EXTENSION_319_SPEC_VERSION"/>
<enum value="&quot;VK_AMD_extension_319&quot;" name="VK_AMD_EXTENSION_319_EXTENSION_NAME"/>
+ <enum bitpos="3" extends="VkDescriptorSetLayoutCreateFlagBits" name="VK_DESCRIPTOR_SET_LAYOUT_CREATE_RESERVED_3_BIT_AMD"/>
+ <enum bitpos="0" extends="VkPipelineLayoutCreateFlagBits" name="VK_PIPELINE_LAYOUT_CREATE_RESERVED_0_BIT_AMD"/>
</require>
</extension>
<extension name="VK_AMD_extension_320" number="320" author="AMD" contact="Martin Dinkov @mdinkov" supported="disabled">
@@ -14187,28 +17860,48 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_AMD_extension_320&quot;" name="VK_AMD_EXTENSION_320_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_AMD_extension_321" number="321" author="AMD" contact="Martin Dinkov @mdinkov" supported="disabled">
+ <extension name="VK_EXT_graphics_pipeline_library" number="321" type="device" requires="VK_KHR_get_physical_device_properties2,VK_KHR_pipeline_library" author="AMD" contact="Tobias Hector @tobski" supported="vulkan">
<require>
- <enum value="0" name="VK_AMD_EXTENSION_321_SPEC_VERSION"/>
- <enum value="&quot;VK_AMD_extension_321&quot;" name="VK_AMD_EXTENSION_321_EXTENSION_NAME"/>
+ <enum value="1" name="VK_EXT_GRAPHICS_PIPELINE_LIBRARY_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_graphics_pipeline_library&quot;" name="VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME"/>
+ <type name="VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT"/>
+ <type name="VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT"/>
+ <type name="VkGraphicsPipelineLibraryCreateInfoEXT"/>
+ <type name="VkGraphicsPipelineLibraryFlagBitsEXT"/>
+ <type name="VkGraphicsPipelineLibraryFlagsEXT"/>
+ <type name="VkPipelineLayoutCreateFlagBits"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT"/>
+ <enum bitpos="23" extends="VkPipelineCreateFlagBits" name="VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT"/>
+ <enum bitpos="10" extends="VkPipelineCreateFlagBits" name="VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT"/>
+ <enum bitpos="1" extends="VkPipelineLayoutCreateFlagBits" name="VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT"/>
</require>
</extension>
- <extension name="VK_AMD_extension_322" number="322" author="AMD" contact="Martin Dinkov @mdinkov" supported="disabled">
+ <extension name="VK_AMD_shader_early_and_late_fragment_tests" number="322" author="EXT" contact="Tobias Hector @tobski" type="device" supported="vulkan">
<require>
- <enum value="0" name="VK_AMD_EXTENSION_322_SPEC_VERSION"/>
- <enum value="&quot;VK_AMD_extension_322&quot;" name="VK_AMD_EXTENSION_322_EXTENSION_NAME"/>
+ <enum value="1" name="VK_AMD_SHADER_EARLY_AND_LATE_FRAGMENT_TESTS_SPEC_VERSION"/>
+ <enum value="&quot;VK_AMD_shader_early_and_late_fragment_tests&quot;" name="VK_AMD_SHADER_EARLY_AND_LATE_FRAGMENT_TESTS_EXTENSION_NAME"/>
+ <type name="VkPhysicalDeviceShaderEarlyAndLateFragmentTestsFeaturesAMD"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_EARLY_AND_LATE_FRAGMENT_TESTS_FEATURES_AMD"/>
</require>
</extension>
- <extension name="VK_AMD_extension_323" number="323" author="AMD" contact="Martin Dinkov @mdinkov" supported="disabled">
+ <extension name="VK_KHR_fragment_shader_barycentric" number="323" type="device" requires="VK_KHR_get_physical_device_properties2" author="KHR" contact="Stu Smith" supported="vulkan">
<require>
- <enum value="0" name="VK_AMD_EXTENSION_323_SPEC_VERSION"/>
- <enum value="&quot;VK_AMD_extension_323&quot;" name="VK_AMD_EXTENSION_323_EXTENSION_NAME"/>
+ <enum value="1" name="VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_fragment_shader_barycentric&quot;" name="VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" extnumber="204" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_PROPERTIES_KHR"/>
+ <type name="VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR"/>
+ <type name="VkPhysicalDeviceFragmentShaderBarycentricPropertiesKHR"/>
</require>
</extension>
- <extension name="VK_KHR_extension_324" number="324" author="KHR" contact="Alan Baker @alan-baker" supported="disabled">
+ <extension name="VK_KHR_shader_subgroup_uniform_control_flow" number="324" type="device" requiresCore="1.1" author="KHR" contact="Alan Baker @alan-baker" supported="vulkan">
<require>
- <enum value="0" name="VK_KHR_EXTENSION_324_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_extension_324&quot;" name="VK_KHR_EXTENSION_324_EXTENSION_NAME"/>
+ <enum value="1" name="VK_KHR_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_shader_subgroup_uniform_control_flow&quot;" name="VK_KHR_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_FEATURES_KHR"/>
+ <type name="VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR"/>
</require>
</extension>
<extension name="VK_KHR_extension_325" number="325" author="KHR" contact="Ralph Potter gitlab:@r_potter" supported="disabled">
@@ -14217,10 +17910,12 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_KHR_extension_325&quot;" name="VK_KHR_EXTENSION_325_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_KHR_extension_326" number="326" author="KHR" contact="Alan Baker @alan-baker" supported="disabled">
+ <extension name="VK_KHR_zero_initialize_workgroup_memory" number="326" type="device" requires="VK_KHR_get_physical_device_properties2" author="KHR" contact="Alan Baker @alan-baker" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
- <enum value="0" name="VK_KHR_EXTENSION_326_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_extension_326&quot;" name="VK_KHR_EXTENSION_326_EXTENSION_NAME"/>
+ <enum value="1" name="VK_KHR_ZERO_INITIALIZE_WORKGROUP_MEMORY_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_zero_initialize_workgroup_memory&quot;" name="VK_KHR_ZERO_INITIALIZE_WORKGROUP_MEMORY_EXTENSION_NAME"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES_KHR" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES"/>
+ <type name="VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeaturesKHR"/>
</require>
</extension>
<extension name="VK_NV_fragment_shading_rate_enums" number="327" type="device" requires="VK_KHR_fragment_shading_rate" author="NV" contact="Pat Brown @nvpbrown" supported="vulkan">
@@ -14238,10 +17933,27 @@ typedef void <name>CAMetalLayer</name>;
<command name="vkCmdSetFragmentShadingRateEnumNV"/>
</require>
</extension>
- <extension name="VK_NV_extension_328" number="328" author="NV" contact="Pat Brown @nvpbrown" supported="disabled">
- <require>
- <enum value="0" name="VK_NV_EXTENSION_328_SPEC_VERSION"/>
- <enum value="&quot;VK_NV_extension_328&quot;" name="VK_NV_EXTENSION_328_EXTENSION_NAME"/>
+ <extension name="VK_NV_ray_tracing_motion_blur" number="328" type="device" requires="VK_KHR_ray_tracing_pipeline" author="NV" contact="Eric Werness" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_NV_RAY_TRACING_MOTION_BLUR_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_ray_tracing_motion_blur&quot;" name="VK_NV_RAY_TRACING_MOTION_BLUR_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_MOTION_TRIANGLES_DATA_NV"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_MOTION_BLUR_FEATURES_NV"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MOTION_INFO_NV"/>
+ <enum bitpos="5" extends="VkBuildAccelerationStructureFlagBitsKHR" name="VK_BUILD_ACCELERATION_STRUCTURE_MOTION_BIT_NV"/>
+ <enum bitpos="2" extends="VkAccelerationStructureCreateFlagBitsKHR" name="VK_ACCELERATION_STRUCTURE_CREATE_MOTION_BIT_NV"/>
+ <enum bitpos="20" extends="VkPipelineCreateFlagBits" name="VK_PIPELINE_CREATE_RAY_TRACING_ALLOW_MOTION_BIT_NV"/>
+ <type name="VkAccelerationStructureGeometryMotionTrianglesDataNV"/>
+ <type name="VkAccelerationStructureMotionInfoNV"/>
+ <type name="VkAccelerationStructureMotionInstanceNV"/>
+ <type name="VkAccelerationStructureMotionInstanceDataNV"/>
+ <type name="VkAccelerationStructureMatrixMotionInstanceNV"/>
+ <type name="VkAccelerationStructureSRTMotionInstanceNV"/>
+ <type name="VkSRTDataNV"/>
+ <type name="VkAccelerationStructureMotionInstanceTypeNV"/>
+ <type name="VkPhysicalDeviceRayTracingMotionBlurFeaturesNV"/>
+ <type name="VkAccelerationStructureMotionInfoFlagsNV"/>
+ <type name="VkAccelerationStructureMotionInstanceFlagsNV"/>
</require>
</extension>
<extension name="VK_NV_extension_329" number="329" author="NV" contact="Pat Brown @nvpbrown" supported="disabled">
@@ -14256,10 +17968,21 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_NV_extension_330&quot;" name="VK_NV_EXTENSION_330_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_NV_extension_331" number="331" author="NV" contact="Tony Zlatinski @tzlatinski" supported="disabled">
+ <extension name="VK_EXT_ycbcr_2plane_444_formats" number="331" type="device" requires="VK_KHR_sampler_ycbcr_conversion" author="EXT" contact="Tony Zlatinski @tzlatinski" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
- <enum value="0" name="VK_NV_EXTENSION_331_SPEC_VERSION"/>
- <enum value="&quot;VK_NV_extension_331&quot;" name="VK_NV_EXTENSION_331_EXTENSION_NAME"/>
+ <comment>
+ VkPhysicalDeviceYcbcr2Plane444FormatsFeaturesEXT and
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_YCBCR_2_PLANE_444_FORMATS_FEATURES_EXT
+ were not promoted to Vulkan 1.3.
+ </comment>
+ <enum value="1" name="VK_EXT_YCBCR_2PLANE_444_FORMATS_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_ycbcr_2plane_444_formats&quot;" name="VK_EXT_YCBCR_2PLANE_444_FORMATS_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_YCBCR_2_PLANE_444_FORMATS_FEATURES_EXT"/>
+ <enum extends="VkFormat" name="VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT" alias="VK_FORMAT_G8_B8R8_2PLANE_444_UNORM"/>
+ <enum extends="VkFormat" name="VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT" alias="VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16"/>
+ <enum extends="VkFormat" name="VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT" alias="VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16"/>
+ <enum extends="VkFormat" name="VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT" alias="VK_FORMAT_G16_B16R16_2PLANE_444_UNORM"/>
+ <type name="VkPhysicalDeviceYcbcr2Plane444FormatsFeaturesEXT"/>
</require>
</extension>
<extension name="VK_NV_extension_332" number="332" author="NV" contact="Tony Zlatinski @tzlatinski" supported="disabled">
@@ -14281,8 +18004,8 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_QCOM_rotated_copy_commands" number="334" type="device" requires="VK_KHR_swapchain,VK_KHR_copy_commands2" author="QCOM" contact="Jeff Leger @jackohound" supported="vulkan">
<require>
- <enum value="0" name="VK_QCOM_rotated_copy_commands_SPEC_VERSION"/>
- <enum value="&quot;VK_QCOM_rotated_copy_commands&quot;" name="VK_QCOM_rotated_copy_commands_EXTENSION_NAME"/>
+ <enum value="1" name="VK_QCOM_ROTATED_COPY_COMMANDS_SPEC_VERSION"/>
+ <enum value="&quot;VK_QCOM_rotated_copy_commands&quot;" name="VK_QCOM_ROTATED_COPY_COMMANDS_EXTENSION_NAME"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_COPY_COMMAND_TRANSFORM_INFO_QCOM"/>
<type name="VkCopyCommandTransformInfoQCOM"/>
</require>
@@ -14293,35 +18016,37 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_KHR_extension_335&quot;" name="VK_KHR_EXTENSION_335_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_EXT_image_robustness" number="336" type="device" author="EXT" contact="Graeme Leese @gnl21" supported="vulkan" requires="VK_KHR_get_physical_device_properties2">
+ <extension name="VK_EXT_image_robustness" number="336" type="device" author="EXT" contact="Graeme Leese @gnl21" supported="vulkan" requires="VK_KHR_get_physical_device_properties2" promotedto="VK_VERSION_1_3">
<require>
<enum value="1" name="VK_EXT_IMAGE_ROBUSTNESS_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_image_robustness&quot;" name="VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES_EXT"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES_EXT" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES"/>
<type name="VkPhysicalDeviceImageRobustnessFeaturesEXT"/>
</require>
</extension>
- <extension name="VK_KHR_extension_337" number="337" type="device" author="KHR" contact="Caio Marcelo de Oliveira Filho @cmarcelo" supported="disabled">
+ <extension name="VK_KHR_workgroup_memory_explicit_layout" number="337" type="device" requires="VK_KHR_get_physical_device_properties2" author="KHR" contact="Caio Marcelo de Oliveira Filho @cmarcelo" supported="vulkan">
<require>
- <enum value="0" name="VK_KHR_EXTENSION_337_SPEC_VERSION"/>
- <enum value="&quot;VK_KHR_extension_337&quot;" name="VK_KHR_EXTENSION_337_EXTENSION_NAME"/>
+ <enum value="1" name="VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_workgroup_memory_explicit_layout&quot;" name="VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR"/>
+ <type name="VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR"/>
</require>
</extension>
- <extension name="VK_KHR_copy_commands2" number="338" author="KHR" type="device" contact="Jeff Leger @jackohound" supported="vulkan">
+ <extension name="VK_KHR_copy_commands2" number="338" author="KHR" type="device" contact="Jeff Leger @jackohound" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
<enum value="1" name="VK_KHR_COPY_COMMANDS_2_SPEC_VERSION"/>
<enum value="&quot;VK_KHR_copy_commands2&quot;" name="VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME"/>
- <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2_KHR"/>
- <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2_KHR"/>
- <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2_KHR"/>
- <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2_KHR"/>
- <enum offset="4" extends="VkStructureType" name="VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2_KHR"/>
- <enum offset="5" extends="VkStructureType" name="VK_STRUCTURE_TYPE_RESOLVE_IMAGE_INFO_2_KHR"/>
- <enum offset="6" extends="VkStructureType" name="VK_STRUCTURE_TYPE_BUFFER_COPY_2_KHR"/>
- <enum offset="7" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_COPY_2_KHR"/>
- <enum offset="8" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_BLIT_2_KHR"/>
- <enum offset="9" extends="VkStructureType" name="VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2_KHR"/>
- <enum offset="10" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_RESOLVE_2_KHR"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2_KHR" alias="VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2_KHR" alias="VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2_KHR" alias="VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2_KHR" alias="VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2_KHR" alias="VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_RESOLVE_IMAGE_INFO_2_KHR" alias="VK_STRUCTURE_TYPE_RESOLVE_IMAGE_INFO_2"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_BUFFER_COPY_2_KHR" alias="VK_STRUCTURE_TYPE_BUFFER_COPY_2"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_COPY_2_KHR" alias="VK_STRUCTURE_TYPE_IMAGE_COPY_2"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_BLIT_2_KHR" alias="VK_STRUCTURE_TYPE_IMAGE_BLIT_2"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2_KHR" alias="VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_RESOLVE_2_KHR" alias="VK_STRUCTURE_TYPE_IMAGE_RESOLVE_2"/>
<type name="VkCopyBufferInfo2KHR"/>
<type name="VkCopyImageInfo2KHR"/>
<type name="VkCopyBufferToImageInfo2KHR"/>
@@ -14341,25 +18066,50 @@ typedef void <name>CAMetalLayer</name>;
<command name="vkCmdResolveImage2KHR"/>
</require>
</extension>
- <extension name="VK_ARM_extension_339" number="339" author="ARM" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" supported="disabled">
- <require>
- <enum value="0" name="VK_ARM_EXTENSION_339_SPEC_VERSION"/>
- <enum value="&quot;VK_ARM_extension_339&quot;" name="VK_ARM_EXTENSION_339_EXTENSION_NAME"/>
+ <extension name="VK_EXT_image_compression_control" number="339" type="device" author="EXT" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_EXT_IMAGE_COMPRESSION_CONTROL_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_image_compression_control&quot;" name="VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT"/>
+ <type name="VkPhysicalDeviceImageCompressionControlFeaturesEXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT"/>
+ <type name="VkImageCompressionControlEXT"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_SUBRESOURCE_LAYOUT_2_EXT"/>
+ <type name="VkSubresourceLayout2EXT"/>
+ <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_SUBRESOURCE_2_EXT"/>
+ <type name="VkImageSubresource2EXT"/>
+ <enum offset="4" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT"/>
+ <type name="VkImageCompressionPropertiesEXT"/>
+ <type name="VkImageCompressionFlagBitsEXT"/>
+ <type name="VkImageCompressionFlagsEXT"/>
+ <type name="VkImageCompressionFixedRateFlagBitsEXT"/>
+ <type name="VkImageCompressionFixedRateFlagsEXT"/>
+ <enum offset="0" dir="-" extends="VkResult" name="VK_ERROR_COMPRESSION_EXHAUSTED_EXT"/>
+ <command name="vkGetImageSubresourceLayout2EXT"/>
</require>
</extension>
<extension name="VK_EXT_extension_340" number="340" author="EXT" contact="Joshua Ashton @Joshua-Ashton" supported="disabled">
<require>
<enum value="0" name="VK_EXT_EXTENSION_340_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_extension_340&quot;" name="VK_EXT_EXTENSION_340_EXTENSION_NAME"/>
+ <enum bitpos="19" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_RESERVED_19_BIT_EXT"/>
+ <enum bitpos="25" extends="VkPipelineCreateFlagBits" name="VK_PIPELINE_CREATE_RESERVED_25_BIT_EXT"/>
+ <enum bitpos="26" extends="VkPipelineCreateFlagBits" name="VK_PIPELINE_CREATE_RESERVED_26_BIT_EXT"/>
+ <enum bitpos="3" extends="VkDependencyFlagBits" name="VK_DEPENDENCY_RESERVED_3_BIT_EXT"/>
</require>
</extension>
- <extension name="VK_EXT_4444_formats" number="341" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Joshua Ashton @Joshua-Ashton" supported="vulkan">
+ <extension name="VK_EXT_4444_formats" number="341" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Joshua Ashton @Joshua-Ashton" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
+ <comment>
+ VkPhysicalDevice4444FormatsFeaturesEXT and
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT
+ were not promoted to Vulkan 1.3.
+ </comment>
<enum value="1" name="VK_EXT_4444_FORMATS_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_4444_formats&quot;" name="VK_EXT_4444_FORMATS_EXTENSION_NAME"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT"/>
- <enum offset="0" extends="VkFormat" name="VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT"/>
- <enum offset="1" extends="VkFormat" name="VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT"/>
+ <enum extends="VkFormat" name="VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT" alias="VK_FORMAT_A4R4G4B4_UNORM_PACK16"/>
+ <enum extends="VkFormat" name="VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT" alias="VK_FORMAT_A4B4G4R4_UNORM_PACK16"/>
<type name="VkPhysicalDevice4444FormatsFeaturesEXT"/>
</require>
</extension>
@@ -14369,10 +18119,20 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_EXT_extension_342&quot;" name="VK_EXT_EXTENSION_342_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_ARM_extension_343" number="343" author="ARM" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" supported="disabled">
+ <extension name="VK_ARM_rasterization_order_attachment_access" number="343" type="device" requires="VK_KHR_get_physical_device_properties2" author="ARM" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" supported="vulkan">
<require>
- <enum value="0" name="VK_ARM_EXTENSION_343_SPEC_VERSION"/>
- <enum value="&quot;VK_ARM_extension_343&quot;" name="VK_ARM_EXTENSION_343_EXTENSION_NAME"/>
+ <enum value="1" name="VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_SPEC_VERSION"/>
+ <enum value="&quot;VK_ARM_rasterization_order_attachment_access&quot;" name="VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_ARM"/>
+ <type name="VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesARM"/>
+ <type name="VkPipelineColorBlendStateCreateFlagBits"/>
+ <type name="VkPipelineDepthStencilStateCreateFlagBits"/>
+ <enum bitpos="0" extends="VkPipelineColorBlendStateCreateFlagBits" name="VK_PIPELINE_COLOR_BLEND_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_BIT_ARM"/>
+ <enum bitpos="0" extends="VkPipelineDepthStencilStateCreateFlagBits" name="VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_DEPTH_ACCESS_BIT_ARM"/>
+ <enum bitpos="1" extends="VkPipelineDepthStencilStateCreateFlagBits" name="VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_STENCIL_ACCESS_BIT_ARM"/>
+ <enum bitpos="4" extends="VkSubpassDescriptionFlagBits" name="VK_SUBPASS_DESCRIPTION_RASTERIZATION_ORDER_ATTACHMENT_COLOR_ACCESS_BIT_ARM"/>
+ <enum bitpos="5" extends="VkSubpassDescriptionFlagBits" name="VK_SUBPASS_DESCRIPTION_RASTERIZATION_ORDER_ATTACHMENT_DEPTH_ACCESS_BIT_ARM"/>
+ <enum bitpos="6" extends="VkSubpassDescriptionFlagBits" name="VK_SUBPASS_DESCRIPTION_RASTERIZATION_ORDER_ATTACHMENT_STENCIL_ACCESS_BIT_ARM"/>
</require>
</extension>
<extension name="VK_ARM_extension_344" number="344" author="ARM" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" supported="disabled">
@@ -14381,10 +18141,12 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_ARM_extension_344&quot;" name="VK_ARM_EXTENSION_344_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_ARM_extension_345" number="345" author="ARM" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" supported="disabled">
- <require>
- <enum value="0" name="VK_ARM_EXTENSION_345_SPEC_VERSION"/>
- <enum value="&quot;VK_ARM_extension_345&quot;" name="VK_ARM_EXTENSION_345_EXTENSION_NAME"/>
+ <extension name="VK_EXT_rgba10x6_formats" number="345" type="device" requires="VK_KHR_sampler_ycbcr_conversion" author="EXT" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_EXT_RGBA10X6_FORMATS_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_rgba10x6_formats&quot;" name="VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RGBA10X6_FORMATS_FEATURES_EXT"/>
+ <type name="VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT"/>
</require>
</extension>
<extension name="VK_NV_acquire_winrt_display" number="346" type="device" requires="VK_EXT_direct_mode_display" author="NV" contact="Jeff Juliano @jjuliano" platform="win32" supported="vulkan">
@@ -14410,7 +18172,6 @@ typedef void <name>CAMetalLayer</name>;
<require>
<enum value="0" name="VK_KHR_EXTENSION_350_SPEC_VERSION"/>
<enum value="&quot;VK_KHR_extension_350&quot;" name="VK_KHR_EXTENSION_350_EXTENSION_NAME"/>
- <enum bitpos="2" extends="VkPipelineCacheCreateFlagBits" name="VK_PIPELINE_CACHE_CREATE_RESERVED_2_BIT_EXT"/>
</require>
</extension>
<extension name="VK_NV_extension_351" number="351" author="NV" contact="Liam Middlebrook @liam-middlebrook" supported="disabled">
@@ -14433,16 +18194,28 @@ typedef void <name>CAMetalLayer</name>;
<type name="VkMutableDescriptorTypeCreateInfoVALVE"/>
</require>
</extension>
- <extension name="VK_EXT_extension_353" number="353" author="EXT" contact="Piers Daniell @pdaniell-nv" supported="disabled">
+ <extension name="VK_EXT_vertex_input_dynamic_state" number="353" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Piers Daniell @pdaniell-nv" supported="vulkan">
<require>
- <enum value="0" name="VK_EXT_EXTENSION_353_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_extension_353&quot;" name="VK_EXT_EXTENSION_353_EXTENSION_NAME"/>
+ <enum value="2" name="VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_vertex_input_dynamic_state&quot;" name="VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT"/>
+ <enum offset="0" extends="VkDynamicState" name="VK_DYNAMIC_STATE_VERTEX_INPUT_EXT"/>
+ <type name="VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT"/>
+ <type name="VkVertexInputBindingDescription2EXT"/>
+ <type name="VkVertexInputAttributeDescription2EXT"/>
+ <command name="vkCmdSetVertexInputEXT"/>
</require>
</extension>
- <extension name="VK_EXT_extension_354" number="354" author="EXT" contact="Simon Ser @emersion" supported="disabled">
+ <extension name="VK_EXT_physical_device_drm" number="354" author="EXT" type="device" contact="Simon Ser @emersion" supported="vulkan" requires="VK_KHR_get_physical_device_properties2">
<require>
- <enum value="0" name="VK_EXT_EXTENSION_354_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_extension_354&quot;" name="VK_EXT_EXTENSION_354_EXTENSION_NAME"/>
+ <enum value="1" name="VK_EXT_PHYSICAL_DEVICE_DRM_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_physical_device_drm&quot;" name="VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME"/>
+
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT"/>
+
+ <type name="VkPhysicalDeviceDrmPropertiesEXT"/>
</require>
</extension>
<extension name="VK_EXT_extension_355" number="355" author="EXT" contact="Ralph Potter gitlab:@r_potter" supported="disabled">
@@ -14451,16 +18224,22 @@ typedef void <name>CAMetalLayer</name>;
<enum value="&quot;VK_EXT_extension_355&quot;" name="VK_EXT_EXTENSION_355_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_EXT_vertex_attribute_aliasing" number="356" type="device" author="EXT" contact="Shahbaz Youssefi @syoussefi" supported="disabled" specialuse="glemulation">
+ <extension name="VK_EXT_depth_clip_control" number="356" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Shahbaz Youssefi @syoussefi" supported="vulkan" specialuse="glemulation">
<require>
- <enum value="0" name="VK_EXT_VERTEX_ATTRIBUTE_ALIASING_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_vertex_attribute_aliasing&quot;" name="VK_EXT_VERTEX_ATTRIBUTE_ALIASING_EXTENSION_NAME"/>
+ <enum value="1" name="VK_EXT_DEPTH_CLIP_CONTROL_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_depth_clip_control&quot;" name="VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT"/>
+ <type name="VkPhysicalDeviceDepthClipControlFeaturesEXT"/>
+ <type name="VkPipelineViewportDepthClipControlCreateInfoEXT"/>
</require>
</extension>
- <extension name="VK_EXT_extension_357" number="357" author="EXT" contact="Courtney Goeltzenleuchter @courtney-g" supported="disabled" specialuse="glemulation">
+ <extension name="VK_EXT_primitive_topology_list_restart" number="357" type="device" author="EXT" contact="Shahbaz Youssefi @syoussefi" supported="vulkan" specialuse="glemulation">
<require>
- <enum value="0" name="VK_EXT_EXTENSION_357"/>
- <enum value="&quot;VK_EXT_extension_357&quot;" name="VK_EXT_EXTENSION_357"/>
+ <enum value="1" name="VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_primitive_topology_list_restart&quot;" name="VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT"/>
+ <type name="VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT"/>
</require>
</extension>
<extension name="VK_KHR_extension_358" number="358" author="KHR" contact="Jeff Bolz @jeffbolznv" supported="disabled">
@@ -14471,20 +18250,24 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_EXT_extension_359" number="359" author="EXT" contact="Bill Hollings @billhollings" supported="disabled" specialuse="glemulation">
<require>
- <enum value="0" name="VK_EXT_EXTENSION_359"/>
- <enum value="&quot;VK_EXT_extension_359&quot;" name="VK_EXT_EXTENSION_359"/>
+ <enum value="0" name="VK_EXT_EXTENSION_359_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_359&quot;" name="VK_EXT_EXTENSION_359_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_EXT_extension_360" number="360" author="EXT" contact="Bill Hollings @billhollings" supported="disabled" specialuse="glemulation">
<require>
- <enum value="0" name="VK_EXT_EXTENSION_360"/>
- <enum value="&quot;VK_EXT_extension_360&quot;" name="VK_EXT_EXTENSION_360"/>
+ <enum value="0" name="VK_EXT_EXTENSION_360_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_360&quot;" name="VK_EXT_EXTENSION_360_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_KHR_extension_361" number="361" author="KHR" contact="Lionel Landwerlin @llandwerlin" supported="disabled">
+ <extension name="VK_KHR_format_feature_flags2" number="361" author="KHR" type="device" requires="VK_KHR_get_physical_device_properties2" contact="Lionel Landwerlin @llandwerlin" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
- <enum value="0" name="VK_EXT_EXTENSION_361"/>
- <enum value="&quot;VK_EXT_extension_361&quot;" name="VK_EXT_EXTENSION_361"/>
+ <enum value="1" name="VK_KHR_FORMAT_FEATURE_FLAGS_2_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_format_feature_flags2&quot;" name="VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR" alias="VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3"/>
+ <type name="VkFormatFeatureFlags2KHR"/>
+ <type name="VkFormatFeatureFlagBits2KHR"/>
+ <type name="VkFormatProperties3KHR"/>
</require>
</extension>
<extension name="VK_EXT_extension_362" number="362" author="EXT" contact="Lionel Duc @nvlduc" supported="disabled">
@@ -14501,32 +18284,79 @@ typedef void <name>CAMetalLayer</name>;
</extension>
<extension name="VK_FUCHSIA_extension_364" number="364" author="FUCHSIA" contact="Craig Stout @cdotstout" supported="disabled">
<require>
- <enum value="0" name="VK_EXT_EXTENSION_364_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_extension_364&quot;" name="VK_EXT_EXTENSION_364_EXTENSION_NAME"/>
- </require>
- </extension>
- <extension name="VK_FUCHSIA_extension_365" number="365" author="FUCHSIA" contact="Craig Stout @cdotstout" supported="disabled">
- <require>
- <enum value="0" name="VK_EXT_EXTENSION_365_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_extension_365&quot;" name="VK_EXT_EXTENSION_365_EXTENSION_NAME"/>
- </require>
- </extension>
- <extension name="VK_FUCHSIA_extension_366" number="366" author="FUCHSIA" contact="Craig Stout @cdotstout" supported="disabled">
- <require>
- <enum value="0" name="VK_EXT_EXTENSION_366_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_extension_366&quot;" name="VK_EXT_EXTENSION_366_EXTENSION_NAME"/>
- </require>
- </extension>
- <extension name="VK_FUCHSIA_extension_367" number="367" author="FUCHSIA" contact="Craig Stout @cdotstout" supported="disabled">
- <require>
- <enum value="0" name="VK_EXT_EXTENSION_367_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_extension_367&quot;" name="VK_EXT_EXTENSION_367_EXTENSION_NAME"/>
+ <enum value="0" name="VK_FUCHSIA_EXTENSION_364_SPEC_VERSION"/>
+ <enum value="&quot;VK_FUCHSIA_extension_364&quot;" name="VK_FUCHSIA_EXTENSION_364_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_FUCHSIA_external_memory" number="365" type="device" requires="VK_KHR_external_memory_capabilities,VK_KHR_external_memory" author="FUCHSIA" contact="John Rosasco @rosasco" platform="fuchsia" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_FUCHSIA_EXTERNAL_MEMORY_SPEC_VERSION"/>
+ <enum value="&quot;VK_FUCHSIA_external_memory&quot;" name="VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMPORT_MEMORY_ZIRCON_HANDLE_INFO_FUCHSIA"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_MEMORY_ZIRCON_HANDLE_PROPERTIES_FUCHSIA"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_MEMORY_GET_ZIRCON_HANDLE_INFO_FUCHSIA"/>
+ <enum bitpos="11" extends="VkExternalMemoryHandleTypeFlagBits" name="VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA"/>
+ <type name="VkImportMemoryZirconHandleInfoFUCHSIA"/>
+ <type name="VkMemoryZirconHandlePropertiesFUCHSIA"/>
+ <type name="VkMemoryGetZirconHandleInfoFUCHSIA"/>
+ <command name="vkGetMemoryZirconHandleFUCHSIA"/>
+ <command name="vkGetMemoryZirconHandlePropertiesFUCHSIA"/>
+ </require>
+ </extension>
+ <extension name="VK_FUCHSIA_external_semaphore" number="366" type="device" requires="VK_KHR_external_semaphore_capabilities,VK_KHR_external_semaphore" author="FUCHSIA" contact="John Rosasco @rosasco" platform="fuchsia" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_FUCHSIA_EXTERNAL_SEMAPHORE_SPEC_VERSION"/>
+ <enum value="&quot;VK_FUCHSIA_external_semaphore&quot;" name="VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_SEMAPHORE_GET_ZIRCON_HANDLE_INFO_FUCHSIA"/>
+ <enum bitpos="7" extends="VkExternalSemaphoreHandleTypeFlagBits" name="VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA"/>
+ <type name="VkImportSemaphoreZirconHandleInfoFUCHSIA"/>
+ <type name="VkSemaphoreGetZirconHandleInfoFUCHSIA"/>
+ <command name="vkImportSemaphoreZirconHandleFUCHSIA"/>
+ <command name="vkGetSemaphoreZirconHandleFUCHSIA"/>
+ </require>
+ </extension>
+ <extension name="VK_FUCHSIA_buffer_collection" number="367" type="device" requires="VK_FUCHSIA_external_memory,VK_KHR_sampler_ycbcr_conversion" author="FUCHSIA" contact="John Rosasco @rosasco" supported="vulkan" platform="fuchsia">
+ <require>
+ <enum value="2" name="VK_FUCHSIA_BUFFER_COLLECTION_SPEC_VERSION"/>
+ <enum value="&quot;VK_FUCHSIA_buffer_collection&quot;" name="VK_FUCHSIA_BUFFER_COLLECTION_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CREATE_INFO_FUCHSIA"/>
+ <enum offset="0" extends="VkObjectType" name="VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA" comment="VkBufferCollectionFUCHSIA"/>
+ <enum offset="0" extends="VkDebugReportObjectTypeEXT" name="VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMPORT_MEMORY_BUFFER_COLLECTION_FUCHSIA"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_BUFFER_COLLECTION_IMAGE_CREATE_INFO_FUCHSIA"/>
+ <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_BUFFER_COLLECTION_PROPERTIES_FUCHSIA"/>
+ <enum offset="4" extends="VkStructureType" name="VK_STRUCTURE_TYPE_BUFFER_CONSTRAINTS_INFO_FUCHSIA"/>
+ <enum offset="5" extends="VkStructureType" name="VK_STRUCTURE_TYPE_BUFFER_COLLECTION_BUFFER_CREATE_INFO_FUCHSIA"/>
+ <enum offset="6" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_CONSTRAINTS_INFO_FUCHSIA"/>
+ <enum offset="7" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_FORMAT_CONSTRAINTS_INFO_FUCHSIA"/>
+ <enum offset="8" extends="VkStructureType" name="VK_STRUCTURE_TYPE_SYSMEM_COLOR_SPACE_FUCHSIA"/>
+ <enum offset="9" extends="VkStructureType" name="VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CONSTRAINTS_INFO_FUCHSIA"/>
+ <type name="VkBufferCollectionFUCHSIA"/>
+ <type name="VkBufferCollectionCreateInfoFUCHSIA"/>
+ <type name="VkImportMemoryBufferCollectionFUCHSIA"/>
+ <type name="VkBufferCollectionImageCreateInfoFUCHSIA"/>
+ <type name="VkBufferConstraintsInfoFUCHSIA"/>
+ <type name="VkBufferCollectionBufferCreateInfoFUCHSIA"/>
+ <type name="VkBufferCollectionPropertiesFUCHSIA"/>
+ <type name="VkImageFormatConstraintsFlagsFUCHSIA" comment="Will add VkImageFormatConstraintsFlagBitsFUCHSIA when bits are defined in the future"/>
+ <type name="VkSysmemColorSpaceFUCHSIA"/>
+ <type name="VkImageConstraintsInfoFlagBitsFUCHSIA"/>
+ <type name="VkImageConstraintsInfoFlagsFUCHSIA"/>
+ <type name="VkImageConstraintsInfoFUCHSIA"/>
+ <type name="VkImageFormatConstraintsInfoFUCHSIA"/>
+ <type name="VkBufferCollectionConstraintsInfoFUCHSIA"/>
+ <command name="vkCreateBufferCollectionFUCHSIA"/>
+ <command name="vkSetBufferCollectionImageConstraintsFUCHSIA"/>
+ <command name="vkSetBufferCollectionBufferConstraintsFUCHSIA"/>
+ <command name="vkDestroyBufferCollectionFUCHSIA"/>
+ <command name="vkGetBufferCollectionPropertiesFUCHSIA"/>
</require>
</extension>
<extension name="VK_FUCHSIA_extension_368" number="368" author="FUCHSIA" contact="Craig Stout @cdotstout" supported="disabled">
<require>
- <enum value="0" name="VK_EXT_EXTENSION_368_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_extension_368&quot;" name="VK_EXT_EXTENSION_368_EXTENSION_NAME"/>
+ <enum value="0" name="VK_FUCHSIA_EXTENSION_368_SPEC_VERSION"/>
+ <enum value="&quot;VK_FUCHSIA_extension_368&quot;" name="VK_FUCHSIA_EXTENSION_368_EXTENSION_NAME"/>
</require>
</extension>
<extension name="VK_QCOM_extension_369" number="369" author="QCOM" contact="Matthew Netsch @mnetsch" supported="disabled">
@@ -14536,63 +18366,116 @@ typedef void <name>CAMetalLayer</name>;
<enum bitpos="4" extends="VkDescriptorBindingFlagBits" name="VK_DESCRIPTOR_BINDING_RESERVED_4_BIT_QCOM"/>
</require>
</extension>
- <extension name="VK_HUAWEI_extension_370" number="370" author="HUAWEI" contact="Hueilong Wang @wyvernathuawei" supported="disabled">
+ <extension name="VK_HUAWEI_subpass_shading" number="370" type="device" author="HUAWEI" contact="Pan Gao @PanGao-h" requires="VK_KHR_create_renderpass2,VK_KHR_synchronization2" supported="vulkan">
<require>
- <enum value="0" name="VK_HUAWEI_EXTENSION_370_SPEC_VERSION"/>
- <enum value="&quot;VK_HUAWEI_extension_370&quot;" name="VK_HUAWEI_EXTENSION_370_EXTENSION_NAME"/>
+ <enum value="2" name="VK_HUAWEI_SUBPASS_SHADING_SPEC_VERSION"/>
+ <enum value="&quot;VK_HUAWEI_subpass_shading&quot;" name="VK_HUAWEI_SUBPASS_SHADING_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_SUBPASS_SHADING_PIPELINE_CREATE_INFO_HUAWEI"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_SHADING_FEATURES_HUAWEI"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_SHADING_PROPERTIES_HUAWEI"/>
+ <enum offset="3" extends="VkPipelineBindPoint" extnumber="370" name="VK_PIPELINE_BIND_POINT_SUBPASS_SHADING_HUAWEI"/>
+ <enum bitpos="39" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_SUBPASS_SHADING_BIT_HUAWEI"/>
+ <enum bitpos="14" extends="VkShaderStageFlagBits" name="VK_SHADER_STAGE_SUBPASS_SHADING_BIT_HUAWEI"/>
+ <type name="VkSubpassShadingPipelineCreateInfoHUAWEI"/>
+ <type name="VkPhysicalDeviceSubpassShadingFeaturesHUAWEI"/>
+ <type name="VkPhysicalDeviceSubpassShadingPropertiesHUAWEI"/>
+ <command name="vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI"/>
+ <command name="vkCmdSubpassShadingHUAWEI"/>
</require>
</extension>
- <extension name="VK_HUAWEI_extension_371" number="371" author="HUAWEI" contact="Hueilong Wang @wyvernathuawei" supported="disabled">
+ <extension name="VK_HUAWEI_invocation_mask" number="371" type="device" requires="VK_KHR_ray_tracing_pipeline,VK_KHR_synchronization2" author="Huawei" contact="Pan Gao @PanGao-h" supported="vulkan">
<require>
- <enum value="0" name="VK_HUAWEI_EXTENSION_371_SPEC_VERSION"/>
- <enum value="&quot;VK_HUAWEI_extension_371&quot;" name="VK_HUAWEI_EXTENSION_371_EXTENSION_NAME"/>
+ <enum value="1" name="VK_HUAWEI_INVOCATION_MASK_SPEC_VERSION"/>
+ <enum value="&quot;VK_HUAWEI_invocation_mask&quot;" name="VK_HUAWEI_INVOCATION_MASK_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INVOCATION_MASK_FEATURES_HUAWEI"/>
+ <enum bitpos="39" extends="VkAccessFlagBits2" name="VK_ACCESS_2_INVOCATION_MASK_READ_BIT_HUAWEI"/>
+ <enum bitpos="18" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_INVOCATION_MASK_BIT_HUAWEI"/>
+ <enum bitpos="40" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_INVOCATION_MASK_BIT_HUAWEI"/>
+ <type name="VkPhysicalDeviceInvocationMaskFeaturesHUAWEI"/>
+ <command name="vkCmdBindInvocationMaskHUAWEI"/>
</require>
</extension>
- <extension name="VK_NV_extension_372" number="372" author="NV" contact="Carsten Rohde @crohde" supported="disabled">
+ <extension name="VK_NV_external_memory_rdma" number="372" type="device" requires="VK_KHR_external_memory" author="NV" contact="Carsten Rohde @crohde" supported="vulkan">
<require>
- <enum value="0" name="VK_NV_EXTENSION_372_SPEC_VERSION"/>
- <enum value="&quot;VK_NV_extension_372&quot;" name="VK_NV_EXTENSION_372_EXTENSION_NAME"/>
- <enum bitpos="5" extends="VkBufferCreateFlagBits" name="VK_BUFFER_CREATE_RESERVED_5_BIT_NV"/>
- <enum bitpos="15" extends="VkImageCreateFlagBits" name="VK_IMAGE_CREATE_RESERVED_15_BIT_NV"/>
+ <enum value="1" name="VK_NV_EXTERNAL_MEMORY_RDMA_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_external_memory_rdma&quot;" name="VK_NV_EXTERNAL_MEMORY_RDMA_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_MEMORY_GET_REMOTE_ADDRESS_INFO_NV"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_RDMA_FEATURES_NV"/>
+ <enum bitpos="8" extends="VkMemoryPropertyFlagBits" name="VK_MEMORY_PROPERTY_RDMA_CAPABLE_BIT_NV"/>
+ <enum bitpos="12" extends="VkExternalMemoryHandleTypeFlagBits" name="VK_EXTERNAL_MEMORY_HANDLE_TYPE_RDMA_ADDRESS_BIT_NV"/>
+ <type name="VkRemoteAddressNV"/>
+ <type name="VkMemoryGetRemoteAddressInfoNV"/>
+ <type name="VkPhysicalDeviceExternalMemoryRDMAFeaturesNV"/>
+ <command name="vkGetMemoryRemoteAddressNV"/>
</require>
</extension>
- <extension name="VK_NV_extension_373" number="373" author="NV" contact="Daniel Koch @dgkoch" supported="disabled">
+ <extension name="VK_EXT_pipeline_properties" number="373" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Mukund Keshava @mkeshavanv" supported="vulkan">
<require>
- <enum value="0" name="VK_NV_EXTENSION_373_SPEC_VERSION"/>
- <enum value="&quot;VK_NV_extension_373&quot;" name="VK_NV_EXTENSION_373_EXTENSION_NAME"/>
+ <enum value="1" name="VK_EXT_PIPELINE_PROPERTIES_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_pipeline_properties&quot;" name="VK_EXT_PIPELINE_PROPERTIES_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PIPELINE_PROPERTIES_IDENTIFIER_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_PROPERTIES_FEATURES_EXT"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PIPELINE_INFO_EXT" alias="VK_STRUCTURE_TYPE_PIPELINE_INFO_KHR"/>
+ <type name="VkPipelineInfoEXT"/>
+ <type name="VkPipelinePropertiesIdentifierEXT"/>
+ <type name="VkPhysicalDevicePipelinePropertiesFeaturesEXT"/>
+ <command name="vkGetPipelinePropertiesEXT"/>
</require>
</extension>
<extension name="VK_NV_extension_374" number="374" author="NV" contact="Daniel Koch @dgkoch" supported="disabled">
<require>
<enum value="0" name="VK_NV_EXTENSION_374_SPEC_VERSION"/>
<enum value="&quot;VK_NV_extension_374&quot;" name="VK_NV_EXTENSION_374_EXTENSION_NAME"/>
+ <enum bitpos="4" extends="VkExternalFenceHandleTypeFlagBits" name="VK_EXTERNAL_FENCE_HANDLE_TYPE_RESERVED_4_BIT_NV"/>
+ <enum bitpos="5" extends="VkExternalFenceHandleTypeFlagBits" name="VK_EXTERNAL_FENCE_HANDLE_TYPE_RESERVED_5_BIT_NV"/>
+ <enum bitpos="5" extends="VkExternalSemaphoreHandleTypeFlagBits" name="VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_RESERVED_5_BIT_NV"/>
</require>
</extension>
<extension name="VK_NV_extension_375" number="375" author="NV" contact="Daniel Koch @dgkoch" supported="disabled">
<require>
<enum value="0" name="VK_NV_EXTENSION_375_SPEC_VERSION"/>
<enum value="&quot;VK_NV_extension_375&quot;" name="VK_NV_EXTENSION_375_EXTENSION_NAME"/>
+ <enum bitpos="13" extends="VkExternalMemoryHandleTypeFlagBits" name="VK_EXTERNAL_MEMORY_HANDLE_TYPE_RESERVED_13_BIT_NV"/>
</require>
</extension>
- <extension name="VK_EXT_extension_376" number="376" author="EXT" contact="Shahbaz Youssefi @syoussefi" supported="disabled">
+ <extension name="VK_EXT_extension_376" number="376" author="EXT" contact="Melih Yasin Yalcin @yalcinmelihyasin" supported="disabled">
<require>
<enum value="0" name="VK_EXT_EXTENSION_376_SPEC_VERSION"/>
<enum value="&quot;VK_EXT_extension_376&quot;" name="VK_EXT_EXTENSION_376_EXTENSION_NAME"/>
</require>
</extension>
- <extension name="VK_EXT_extension_377" number="377" author="EXT" contact="Hugues Evrard @hevrard" supported="disabled">
+ <extension name="VK_EXT_multisampled_render_to_single_sampled" number="377" type="device" requires="VK_KHR_create_renderpass2,VK_KHR_depth_stencil_resolve" author="EXT" contact="Shahbaz Youssefi @syoussefi" supported="vulkan">
<require>
- <enum value="0" name="VK_EXT_EXTENSION_377_SPEC_VERSION"/>
- <enum value="&quot;VK_EXT_extension_377&quot;" name="VK_EXT_EXTENSION_377_EXTENSION_NAME"/>
+ <enum value="1" name="VK_EXT_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_multisampled_render_to_single_sampled&quot;" name="VK_EXT_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_FEATURES_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_SUBPASS_RESOLVE_PERFORMANCE_QUERY_EXT"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_INFO_EXT"/>
+ <enum bitpos="18" extends="VkImageCreateFlagBits" name="VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT"/>
+ <type name="VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT"/>
+ <type name="VkSubpassResolvePerformanceQueryEXT"/>
+ <type name="VkMultisampledRenderToSingleSampledInfoEXT"/>
</require>
</extension>
- <extension name="VK_NV_extension_378" number="378" author="NV" contact="Vikram Kushwaha @vkushwaha-nv" supported="disabled">
+ <extension name="VK_EXT_extended_dynamic_state2" number="378" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Vikram Kushwaha @vkushwaha-nv" supported="vulkan" promotedto="VK_VERSION_1_3">
<require>
- <enum value="0" name="VK_NV_EXTENSION_378_SPEC_VERSION"/>
- <enum value="&quot;VK_NV_extension_378&quot;" name="VK_NV_EXTENSION_378_EXTENSION_NAME"/>
+ <enum value="1" name="VK_EXT_EXTENDED_DYNAMIC_STATE_2_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extended_dynamic_state2&quot;" name="VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT" comment="Not promoted to 1.3"/>
+ <enum offset="0" extends="VkDynamicState" name="VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT" comment="Not promoted to 1.3"/>
+ <enum extends="VkDynamicState" name="VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT" alias="VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE"/>
+ <enum extends="VkDynamicState" name="VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT" alias="VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE"/>
+ <enum offset="3" extends="VkDynamicState" name="VK_DYNAMIC_STATE_LOGIC_OP_EXT" comment="Not promoted to 1.3"/>
+ <enum extends="VkDynamicState" name="VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT" alias="VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE"/>
+ <type name="VkPhysicalDeviceExtendedDynamicState2FeaturesEXT" comment="Not promoted to 1.3"/>
+ <command name="vkCmdSetPatchControlPointsEXT" comment="Not promoted to 1.3"/>
+ <command name="vkCmdSetRasterizerDiscardEnableEXT"/>
+ <command name="vkCmdSetDepthBiasEnableEXT"/>
+ <command name="vkCmdSetLogicOpEXT" comment="Not promoted to 1.3"/>
+ <command name="vkCmdSetPrimitiveRestartEnableEXT"/>
</require>
</extension>
- <extension name="VK_QNX_screen_surface" number="379" type="instance" requires="VK_KHR_surface" platform="screen" author="QNX" contact="Mike Gorchak @mgorchak-blackberry" supported="disabled">
+ <extension name="VK_QNX_screen_surface" number="379" type="instance" requires="VK_KHR_surface" platform="screen" author="QNX" contact="Mike Gorchak @mgorchak-blackberry" supported="vulkan">
<require>
<enum value="1" name="VK_QNX_SCREEN_SURFACE_SPEC_VERSION"/>
<enum value="&quot;VK_QNX_screen_surface&quot;" name="VK_QNX_SCREEN_SURFACE_EXTENSION_NAME"/>
@@ -14603,10 +18486,2180 @@ typedef void <name>CAMetalLayer</name>;
<command name="vkGetPhysicalDeviceScreenPresentationSupportQNX"/>
</require>
</extension>
+ <extension name="VK_KHR_extension_380" number="380" author="KHR" contact="James Jones @cubanismo" supported="disabled">
+ <require>
+ <enum value="0" name="VK_KHR_EXTENSION_380_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_extension_380&quot;" name="VK_KHR_EXTENSION_380_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_KHR_extension_381" number="381" author="KHR" contact="James Jones @cubanismo" supported="disabled">
+ <require>
+ <enum value="0" name="VK_KHR_EXTENSION_381_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_extension_381&quot;" name="VK_KHR_EXTENSION_381_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_color_write_enable" number="382" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Sharif Elcott @selcott" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_EXT_COLOR_WRITE_ENABLE_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_color_write_enable&quot;" name="VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COLOR_WRITE_ENABLE_FEATURES_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PIPELINE_COLOR_WRITE_CREATE_INFO_EXT"/>
+ <enum offset="0" extends="VkDynamicState" name="VK_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT"/>
+ <type name="VkPhysicalDeviceColorWriteEnableFeaturesEXT"/>
+ <type name="VkPipelineColorWriteCreateInfoEXT"/>
+ <command name="vkCmdSetColorWriteEnableEXT"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_primitives_generated_query" number="383" type="device" requires="VK_EXT_transform_feedback" author="EXT" contact="Shahbaz Youssefi @syoussefi" supported="vulkan" specialuse="glemulation">
+ <require>
+ <enum value="1" name="VK_EXT_PRIMITIVES_GENERATED_QUERY_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_primitives_generated_query&quot;" name="VK_EXT_PRIMITIVES_GENERATED_QUERY_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT"/>
+ <enum offset="0" extends="VkQueryType" name="VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT"/>
+ <type name="VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_384" number="384" type="instance" author="EXT" contact="Chia-I Wu @olvaffe1" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_384_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_384&quot;" name="VK_EXT_EXTENSION_384_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_MESA_extension_385" number="385" type="instance" author="MESA" contact="Chia-I Wu @olvaffe1" supported="disabled">
+ <require>
+ <enum value="0" name="VK_MESA_EXTENSION_385_SPEC_VERSION"/>
+ <enum value="&quot;VK_MESA_extension_385&quot;" name="VK_MESA_EXTENSION_385_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_GOOGLE_extension_386" number="386" author="GOOGLE" contact="Chia-I Wu @olvaffe1" supported="disabled">
+ <require>
+ <enum value="0" name="VK_GOOGLE_EXTENSION_386_SPEC_VERSION"/>
+ <enum value="&quot;VK_GOOGLE_extension_386&quot;" name="VK_GOOGLE_EXTENSION_386_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_KHR_ray_tracing_maintenance1" number="387" type="device" requiresCore="1.1" requires="VK_KHR_acceleration_structure" author="KHR" contact="Daniel Koch @dgkoch" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_KHR_RAY_TRACING_MAINTENANCE_1_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_ray_tracing_maintenance1&quot;" name="VK_KHR_RAY_TRACING_MAINTENANCE_1_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_MAINTENANCE_1_FEATURES_KHR"/>
+ <enum offset="0" extends="VkQueryType" name="VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SERIALIZATION_BOTTOM_LEVEL_POINTERS_KHR"/>
+ <enum offset="1" extends="VkQueryType" name="VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SIZE_KHR"/>
+ <type name="VkPhysicalDeviceRayTracingMaintenance1FeaturesKHR"/>
+ </require>
+ <require extension="VK_KHR_synchronization2">
+ <!-- VkPipelineStageFlagBits bitpos="28" is reserved by this extension, but not used -->
+ <enum bitpos="28" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_COPY_BIT_KHR"/>
+ </require>
+ <require extension="VK_KHR_synchronization2,VK_KHR_ray_tracing_pipeline">
+ <enum bitpos="40" extends="VkAccessFlagBits2" name="VK_ACCESS_2_SHADER_BINDING_TABLE_READ_BIT_KHR"/>
+ </require>
+ <require extension="VK_KHR_ray_tracing_pipeline">
+ <type name="VkTraceRaysIndirectCommand2KHR"/>
+ <command name="vkCmdTraceRaysIndirect2KHR"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_388" number="388" author="EXT" contact="Alan Baker @alan-baker" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_388_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_388&quot;" name="VK_EXT_EXTENSION_388_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_global_priority_query" number="389" type="device" requires="VK_EXT_global_priority,VK_KHR_get_physical_device_properties2" author="EXT" contact="Yiwei Zhang @zhangyiwei" supported="vulkan" promotedto="VK_KHR_global_priority">
+ <require>
+ <enum value="1" name="VK_EXT_GLOBAL_PRIORITY_QUERY_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_global_priority_query&quot;" name="VK_EXT_GLOBAL_PRIORITY_QUERY_EXTENSION_NAME"/>
+ <enum extends="VkStructureType" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_KHR" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_EXT"/>
+ <enum extends="VkStructureType" alias="VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_KHR" name="VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT"/>
+ <enum name="VK_MAX_GLOBAL_PRIORITY_SIZE_EXT"/>
+ <type name="VkPhysicalDeviceGlobalPriorityQueryFeaturesEXT"/>
+ <type name="VkQueueFamilyGlobalPriorityPropertiesEXT"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_390" number="390" author="EXT" contact="Joshua Ashton @Joshua-Ashton" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_390_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_390&quot;" name="VK_EXT_EXTENSION_390_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_391" number="391" author="EXT" contact="Joshua Ashton @Joshua-Ashton" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_391_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_391&quot;" name="VK_EXT_EXTENSION_391_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_image_view_min_lod" number="392" type="device" requires="VK_KHR_get_physical_device_properties2" author="EXT" contact="Joshua Ashton @Joshua-Ashton" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_EXT_IMAGE_VIEW_MIN_LOD_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_image_view_min_lod&quot;" name="VK_EXT_IMAGE_VIEW_MIN_LOD_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_MIN_LOD_FEATURES_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_VIEW_MIN_LOD_CREATE_INFO_EXT"/>
+ <type name="VkPhysicalDeviceImageViewMinLodFeaturesEXT"/>
+ <type name="VkImageViewMinLodCreateInfoEXT"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_multi_draw" number="393" author="EXT" contact="Mike Blumenkrantz @zmike" type="device" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_EXT_MULTI_DRAW_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_multi_draw&quot;" name="VK_EXT_MULTI_DRAW_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT"/>
+ <type name="VkPhysicalDeviceMultiDrawFeaturesEXT"/>
+ <type name="VkPhysicalDeviceMultiDrawPropertiesEXT"/>
+ <command name="vkCmdDrawMultiEXT"/>
+ <command name="vkCmdDrawMultiIndexedEXT"/>
+ <type name="VkMultiDrawInfoEXT"/>
+ <type name="VkMultiDrawIndexedInfoEXT"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_image_2d_view_of_3d" number="394" requires="VK_KHR_maintenance1,VK_KHR_get_physical_device_properties2" author="EXT" contact="Mike Blumenkrantz @zmike" specialuse="glemulation" type="device" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_EXT_IMAGE_2D_VIEW_OF_3D_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_image_2d_view_of_3d&quot;" name="VK_EXT_IMAGE_2D_VIEW_OF_3D_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_2D_VIEW_OF_3D_FEATURES_EXT"/>
+ <type name="VkPhysicalDeviceImage2DViewOf3DFeaturesEXT"/>
+ <enum extends="VkImageCreateFlagBits" bitpos="17" name="VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT" comment="Image is created with a layout where individual slices are capable of being used as 2D images"/>
+ </require>
+ </extension>
+ <extension name="VK_KHR_portability_enumeration" number="395" author="KHR" contact="Charles Giessen @charles-lunarg" type="instance" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_KHR_PORTABILITY_ENUMERATION_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_portability_enumeration&quot;" name="VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME"/>
+ <enum bitpos="0" extends="VkInstanceCreateFlagBits" name="VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR"/>
+ </require>
+ </extension>
+ <extension name="VK_KHR_extension_396" number="396" author="EXT" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" supported="disabled">
+ <require>
+ <enum value="0" name="VK_KHR_EXTENSION_396_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_extension_396&quot;" name="VK_KHR_EXTENSION_396_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_NV_extension_397" number="397" author="NV" contact="Christoph Kubisch @pixeljetstream" supported="disabled">
+ <require>
+ <enum value="0" name="VK_NV_EXTENSION_397_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_extension_397&quot;" name="VK_NV_EXTENSION_397_EXTENSION_NAME"/>
+ <enum bitpos="30" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_RESERVED_30_BIT_NV"/>
+ <enum bitpos="44" extends="VkAccessFlagBits2" name="VK_ACCESS_2_RESERVED_44_BIT_NV"/>
+ <enum bitpos="45" extends="VkAccessFlagBits2" name="VK_ACCESS_2_RESERVED_45_BIT_NV"/>
+ <enum bitpos="23" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_RESERVED_23_BIT_NV"/>
+ <enum bitpos="24" extends="VkBufferUsageFlagBits" name="VK_BUFFER_USAGE_RESERVED_24_BIT_NV"/>
+ <enum bitpos="24" extends="VkPipelineCreateFlagBits" name="VK_PIPELINE_CREATE_RESERVED_24_BIT_NV"/>
+ <enum bitpos="4" extends="VkGeometryInstanceFlagBitsKHR" name="VK_GEOMETRY_INSTANCE_RESERVED_4_BIT_NV"/>
+ <enum bitpos="5" extends="VkGeometryInstanceFlagBitsKHR" name="VK_GEOMETRY_INSTANCE_RESERVED_5_BIT_NV"/>
+ <enum bitpos="6" extends="VkBuildAccelerationStructureFlagBitsKHR" name="VK_BUILD_ACCELERATION_STRUCTURE_RESERVED_6_BIT_NV"/>
+ <enum bitpos="7" extends="VkBuildAccelerationStructureFlagBitsKHR" name="VK_BUILD_ACCELERATION_STRUCTURE_RESERVED_7_BIT_NV"/>
+ </require>
+ </extension>
+ <extension name="VK_NV_extension_398" number="398" author="NV" contact="Christoph Kubisch @pixeljetstream" supported="disabled">
+ <require>
+ <enum value="0" name="VK_NV_EXTENSION_398_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_extension_398&quot;" name="VK_NV_EXTENSION_398_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_JUICE_extension_399" number="399" author="JUICE" contact="Dean Beeler @canadacow" supported="disabled">
+ <require>
+ <enum value="0" name="VK_JUICE_EXTENSION_399_SPEC_VERSION"/>
+ <enum value="&quot;VK_JUICE_extension_399&quot;" name="VK_JUICE_EXTENSION_399_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_JUICE_extension_400" number="400" author="JUICE" contact="David McCloskey @damcclos" supported="disabled">
+ <require>
+ <enum value="0" name="VK_JUICE_EXTENSION_400_SPEC_VERSION"/>
+ <enum value="&quot;VK_JUICE_extension_400&quot;" name="VK_JUICE_EXTENSION_400_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_load_store_op_none" number="401" author="EXT" type="device" contact="Shahbaz Youssefi @syoussefi" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_EXT_LOAD_STORE_OP_NONE_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_load_store_op_none&quot;" name="VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkAttachmentLoadOp" name="VK_ATTACHMENT_LOAD_OP_NONE_EXT"/>
+ <enum extends="VkAttachmentStoreOp" name="VK_ATTACHMENT_STORE_OP_NONE_EXT" alias="VK_ATTACHMENT_STORE_OP_NONE"/>
+ </require>
+ </extension>
+ <extension name="VK_FB_extension_402" number="402" author="FB" contact="Artem Bolgar @artyom17" supported="disabled">
+ <require>
+ <enum value="0" name="VK_FB_EXTENSION_402_SPEC_VERSION"/>
+ <enum value="&quot;VK_FB_extension_402&quot;" name="VK_FB_EXTENSION_402_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_FB_extension_403" number="403" author="FB" contact="Artem Bolgar @artyom17" supported="disabled">
+ <require>
+ <enum value="0" name="VK_FB_EXTENSION_403_SPEC_VERSION"/>
+ <enum value="&quot;VK_FB_extension_403&quot;" name="VK_FB_EXTENSION_403_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_FB_extension_404" number="404" author="FB" contact="Artem Bolgar @artyom17" supported="disabled">
+ <require>
+ <enum value="0" name="VK_FB_EXTENSION_404_SPEC_VERSION"/>
+ <enum value="&quot;VK_FB_extension_404&quot;" name="VK_FB_EXTENSION_404_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_HUAWEI_extension_405" number="405" author="HUAWEI" contact="Hueilong Wang @wyvernathuawei" supported="disabled">
+ <require>
+ <enum value="0" name="VK_HUAWEI_EXTENSION_405_SPEC_VERSION"/>
+ <enum value="&quot;VK_HUAWEI_extension_405&quot;" name="VK_HUAWEI_EXTENSION_405_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_HUAWEI_extension_406" number="406" author="HUAWEI" contact="Hueilong Wang @wyvernathuawei" supported="disabled">
+ <require>
+ <enum value="0" name="VK_HUAWEI_EXTENSION_406_SPEC_VERSION"/>
+ <enum value="&quot;VK_HUAWEI_extension_406&quot;" name="VK_HUAWEI_EXTENSION_406_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_GGP_extension_407" number="407" author="GGP" contact="J.D. Rouan @jdrouan" supported="disabled">
+ <require>
+ <enum value="0" name="VK_GGP_EXTENSION_407_SPEC_VERSION"/>
+ <enum value="&quot;VK_GGP_extension_407&quot;" name="VK_GGP_EXTENSION_407_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_GGP_extension_408" number="408" author="GGP" contact="J.D. Rouan @jdrouan" supported="disabled">
+ <require>
+ <enum value="0" name="VK_GGP_EXTENSION_408_SPEC_VERSION"/>
+ <enum value="&quot;VK_GGP_extension_408&quot;" name="VK_GGP_EXTENSION_408_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_GGP_extension_409" number="409" author="GGP" contact="J.D. Rouan @jdrouan" supported="disabled">
+ <require>
+ <enum value="0" name="VK_GGP_EXTENSION_409_SPEC_VERSION"/>
+ <enum value="&quot;VK_GGP_extension_409&quot;" name="VK_GGP_EXTENSION_409_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_GGP_extension_410" number="410" author="GGP" contact="J.D. Rouan @jdrouan" supported="disabled">
+ <require>
+ <enum value="0" name="VK_GGP_EXTENSION_410_SPEC_VERSION"/>
+ <enum value="&quot;VK_GGP_extension_410&quot;" name="VK_GGP_EXTENSION_410_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_GGP_extension_411" number="411" author="GGP" contact="J.D. Rouan @jdrouan" supported="disabled">
+ <require>
+ <enum value="0" name="VK_GGP_EXTENSION_411_SPEC_VERSION"/>
+ <enum value="&quot;VK_GGP_extension_411&quot;" name="VK_GGP_EXTENSION_411_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_border_color_swizzle" number="412" type="device" author="EXT" contact="Piers Daniell @pdaniell-nv" supported="vulkan" requires="VK_EXT_custom_border_color" specialuse="glemulation,d3demulation">
+ <require>
+ <enum value="1" name="VK_EXT_BORDER_COLOR_SWIZZLE_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_border_color_swizzle&quot;" name="VK_EXT_BORDER_COLOR_SWIZZLE_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BORDER_COLOR_SWIZZLE_FEATURES_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_SAMPLER_BORDER_COLOR_COMPONENT_MAPPING_CREATE_INFO_EXT"/>
+ <type name="VkPhysicalDeviceBorderColorSwizzleFeaturesEXT"/>
+ <type name="VkSamplerBorderColorComponentMappingCreateInfoEXT"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_pageable_device_local_memory" number="413" author="EXT" contact="Piers Daniell @pdaniell-nv" type="device" requires="VK_EXT_memory_priority" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_pageable_device_local_memory&quot;" name="VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PAGEABLE_DEVICE_LOCAL_MEMORY_FEATURES_EXT"/>
+ <type name="VkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT"/>
+ <command name="vkSetDeviceMemoryPriorityEXT"/>
+ </require>
+ </extension>
+ <extension name="VK_KHR_maintenance4" number="414" type="device" requiresCore="1.1" author="KHR" contact="Piers Daniell @pdaniell-nv" supported="vulkan" promotedto="VK_VERSION_1_3">
+ <require>
+ <enum value="2" name="VK_KHR_MAINTENANCE_4_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_maintenance4&quot;" name="VK_KHR_MAINTENANCE_4_EXTENSION_NAME"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES_KHR" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES_KHR" alias="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS_KHR" alias="VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS"/>
+ <enum extends="VkStructureType" name="VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS_KHR" alias="VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS"/>
+ <enum extends="VkImageAspectFlagBits" name="VK_IMAGE_ASPECT_NONE_KHR" alias="VK_IMAGE_ASPECT_NONE"/>
+ <type name="VkPhysicalDeviceMaintenance4FeaturesKHR"/>
+ <type name="VkPhysicalDeviceMaintenance4PropertiesKHR"/>
+ <type name="VkDeviceBufferMemoryRequirementsKHR"/>
+ <type name="VkDeviceImageMemoryRequirementsKHR"/>
+ <command name="vkGetDeviceBufferMemoryRequirementsKHR"/>
+ <command name="vkGetDeviceImageMemoryRequirementsKHR"/>
+ <command name="vkGetDeviceImageSparseMemoryRequirementsKHR"/>
+ </require>
+ </extension>
+ <extension name="VK_HUAWEI_extension_415" number="415" author="HUAWEI" contact="Hueilong Wang @wyvernathuawei" supported="disabled">
+ <require>
+ <enum value="0" name="VK_HUAWEI_EXTENSION_415_SPEC_VERSION"/>
+ <enum value="&quot;VK_HUAWEI_extension_415&quot;" name="VK_HUAWEI_EXTENSION_415_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_ARM_extension_416" number="416" author="ARM" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" supported="disabled">
+ <require>
+ <enum value="0" name="VK_ARM_EXTENSION_416_SPEC_VERSION"/>
+ <enum value="&quot;VK_ARM_extension_416&quot;" name="VK_ARM_EXTENSION_416_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_KHR_extension_417" number="417" author="KHR" contact="Kevin Petit @kevinpetit" supported="disabled">
+ <require>
+ <enum value="0" name="VK_KHR_EXTENSION_417_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_extension_417&quot;" name="VK_KHR_EXTENSION_417_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_ARM_extension_418" number="418" author="ARM" contact="Kevin Petit @kevinpetit" supported="disabled">
+ <require>
+ <enum value="0" name="VK_ARM_EXTENSION_418_SPEC_VERSION"/>
+ <enum value="&quot;VK_ARM_extension_418&quot;" name="VK_ARM_EXTENSION_418_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_419" number="419" author="EXT" contact="Mike Blumenkrantz @zmike" type="device" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_419_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_419&quot;" name="VK_EXT_EXTENSION_419_EXTENSION_NAME"/>
+ <enum bitpos="3" extends="VkImageViewCreateFlagBits" name="VK_IMAGE_VIEW_CREATE_RESERVED_3_BIT_EXT"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_420" number="420" author="EXT" contact="Mike Blumenkrantz @zmike" type="device" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_420_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_420&quot;" name="VK_EXT_EXTENSION_420_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_VALVE_descriptor_set_host_mapping" number="421" type="device" author="VALVE" contact="Hans-Kristian Arntzen @HansKristian-Work" specialuse="d3demulation" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_VALVE_DESCRIPTOR_SET_HOST_MAPPING_SPEC_VERSION"/>
+ <enum value="&quot;VK_VALVE_descriptor_set_host_mapping&quot;" name="VK_VALVE_DESCRIPTOR_SET_HOST_MAPPING_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_SET_HOST_MAPPING_FEATURES_VALVE"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DESCRIPTOR_SET_BINDING_REFERENCE_VALVE"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_HOST_MAPPING_INFO_VALVE"/>
+ <type name="VkPhysicalDeviceDescriptorSetHostMappingFeaturesVALVE"/>
+ <type name="VkDescriptorSetBindingReferenceVALVE"/>
+ <type name="VkDescriptorSetLayoutHostMappingInfoVALVE"/>
+ <command name="vkGetDescriptorSetLayoutHostMappingInfoVALVE"/>
+ <command name="vkGetDescriptorSetHostMappingVALVE"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_422" number="422" author="EXT" contact="Graeme Leese @gnl21" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_422_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_422&quot;" name="VK_EXT_EXTENSION_422_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_non_seamless_cube_map" number="423" author="EXT" type="device" contact="Georg Lehmann @DadSchoorse" specialuse="d3demulation,glemulation" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_EXT_NON_SEAMLESS_CUBE_MAP_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_non_seamless_cube_map&quot;" name="VK_EXT_NON_SEAMLESS_CUBE_MAP_EXTENSION_NAME"/>
+ <enum bitpos="2" extends="VkSamplerCreateFlagBits" name="VK_SAMPLER_CREATE_NON_SEAMLESS_CUBE_MAP_BIT_EXT"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NON_SEAMLESS_CUBE_MAP_FEATURES_EXT"/>
+ <type name="VkPhysicalDeviceNonSeamlessCubeMapFeaturesEXT"/>
+ </require>
+ </extension>
+ <extension name="VK_ARM_extension_424" number="424" author="ARM" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" supported="disabled">
+ <require>
+ <enum value="0" name="VK_ARM_EXTENSION_424_SPEC_VERSION"/>
+ <enum value="&quot;VK_ARM_extension_424&quot;" name="VK_ARM_EXTENSION_424_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_ARM_extension_425" number="425" author="ARM" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" supported="disabled">
+ <require>
+ <enum value="0" name="VK_ARM_EXTENSION_425_SPEC_VERSION"/>
+ <enum value="&quot;VK_ARM_extension_425&quot;" name="VK_ARM_EXTENSION_425_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_QCOM_fragment_density_map_offset" number="426" type="device" requires="VK_KHR_get_physical_device_properties2,VK_EXT_fragment_density_map" author="QCOM" contact="Matthew Netsch @mnetsch" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_QCOM_FRAGMENT_DENSITY_MAP_OFFSET_SPEC_VERSION"/>
+ <enum value="&quot;VK_QCOM_fragment_density_map_offset&quot;" name="VK_QCOM_FRAGMENT_DENSITY_MAP_OFFSET_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_OFFSET_FEATURES_QCOM"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_OFFSET_PROPERTIES_QCOM"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_SUBPASS_FRAGMENT_DENSITY_MAP_OFFSET_END_INFO_QCOM"/>
+ <enum bitpos="15" extends="VkImageCreateFlagBits" name="VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM"/>
+ <type name="VkPhysicalDeviceFragmentDensityMapOffsetFeaturesQCOM"/>
+ <type name="VkPhysicalDeviceFragmentDensityMapOffsetPropertiesQCOM"/>
+ <type name="VkSubpassFragmentDensityMapOffsetEndInfoQCOM"/>
+ </require>
+ </extension>
+ <extension name="VK_NV_extension_427" number="427" author="NV" contact="Vikram Kushwaha @vkushwaha-nv" supported="disabled">
+ <require>
+ <enum value="0" name="VK_NV_EXTENSION_427_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_extension_427&quot;" name="VK_NV_EXTENSION_427_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_NV_extension_428" number="428" author="NV" contact="Vikram Kushwaha @vkushwaha-nv" supported="disabled">
+ <require>
+ <enum value="0" name="VK_NV_EXTENSION_428_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_extension_428&quot;" name="VK_NV_EXTENSION_428_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_NV_extension_429" number="429" author="NV" contact="Vikram Kushwaha @vkushwaha-nv" supported="disabled">
+ <require>
+ <enum value="0" name="VK_NV_EXTENSION_429_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_extension_429&quot;" name="VK_NV_EXTENSION_429_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_NV_extension_430" number="430" author="NV" contact="Vikram Kushwaha @vkushwaha-nv" supported="disabled">
+ <require>
+ <enum value="0" name="VK_NV_EXTENSION_430_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_extension_430&quot;" name="VK_NV_EXTENSION_430_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_NV_linear_color_attachment" number="431" type="device" author="NVIDIA" contact="sourav parmar @souravpNV" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_NV_LINEAR_COLOR_ATTACHMENT_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_linear_color_attachment&quot;" name="VK_NV_LINEAR_COLOR_ATTACHMENT_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINEAR_COLOR_ATTACHMENT_FEATURES_NV"/>
+ <type name="VkPhysicalDeviceLinearColorAttachmentFeaturesNV"/>
+ </require>
+ <require extension="VK_KHR_format_feature_flags2">
+ <enum bitpos="38" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_LINEAR_COLOR_ATTACHMENT_BIT_NV" comment="Format support linear image as render target, it cannot be mixed with non linear attachment"/>
+ </require>
+ </extension>
+ <extension name="VK_NV_extension_432" number="432" author="NV" contact="Sourav Parmar @souravpNV" supported="disabled">
+ <require>
+ <enum value="0" name="VK_NV_EXTENSION_432_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_extension_432&quot;" name="VK_NV_EXTENSION_432_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_NV_extension_433" number="433" author="NV" contact="Sourav Parmar @souravpNV" supported="disabled">
+ <require>
+ <enum value="0" name="VK_NV_EXTENSION_433_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_extension_433&quot;" name="VK_NV_EXTENSION_433_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_GOOGLE_surfaceless_query" number="434" type="instance" requires="VK_KHR_surface" author="GOOGLE" contact="Shahbaz Youssefi @syoussefi" specialuse="glemulation" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_GOOGLE_SURFACELESS_QUERY_SPEC_VERSION"/>
+ <enum value="&quot;VK_GOOGLE_surfaceless_query&quot;" name="VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_KHR_extension_435" number="435" author="KHR" contact="Alan Baker @alan-baker" supported="disabled">
+ <require>
+ <enum value="0" name="VK_KHR_EXTENSION_435_SPEC_VERSION"/>
+ <enum value="&quot;VK_KHR_extension_435&quot;" name="VK_KHR_EXTENSION_435_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_NV_extension_436" number="436" author="NV" contact="Daniel Koch @dgkoch" supported="disabled">
+ <require>
+ <enum value="0" name="VK_NV_EXTENSION_436_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_extension_436&quot;" name="VK_NV_EXTENSION_436_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_437" number="437" author="EXT" contact="Jonathan Weinstein @Jonathan-Weinstein" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_437_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_437&quot;" name="VK_EXT_EXTENSION_437_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_image_compression_control_swapchain" number="438" type="device" requires="VK_EXT_image_compression_control" author="EXT" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_image_compression_control_swapchain&quot;" name="VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT"/>
+ <type name="VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT"/>
+ </require>
+ </extension>
+ <extension name="VK_SEC_extension_439" number="439" author="SEC" contact="Ralph Potter gitlab:@r_potter" supported="disabled">
+ <require>
+ <enum value="0" name="VK_SEC_EXTENSION_439_SPEC_VERSION"/>
+ <enum value="&quot;VK_SEC_extension_439&quot;" name="VK_SEC_EXTENSION_439_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_QCOM_extension_440" number="440" author="QCOM" contact="Jeff Leger @jackohound" supported="disabled">
+ <require>
+ <enum value="0" name="VK_QCOM_EXTENSION_440_SPEC_VERSION"/>
+ <enum value="&quot;VK_QCOM_extension_440&quot;" name="VK_QCOM_EXTENSION_440_EXTENSION_NAME"/>
+ <enum bitpos="7" extends="VkQueueFlagBits" name="VK_QUEUE_RESERVED_7_BIT_QCOM"/>
+ <enum bitpos="1" extends="VkDeviceQueueCreateFlagBits" name="VK_DEVICE_QUEUE_CREATE_RESERVED_1_BIT_QCOM"/>
+ </require>
+ </extension>
+ <extension name="VK_QCOM_image_processing" number="441" type="device" requires="VK_KHR_format_feature_flags2" author="QCOM" contact="Jeff Leger @jackohound" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_QCOM_IMAGE_PROCESSING_SPEC_VERSION"/>
+ <enum value="&quot;VK_QCOM_image_processing&quot;" name="VK_QCOM_IMAGE_PROCESSING_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_FEATURES_QCOM"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_PROPERTIES_QCOM"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMAGE_VIEW_SAMPLE_WEIGHT_CREATE_INFO_QCOM"/>
+ <enum bitpos="4" extends="VkSamplerCreateFlagBits" name="VK_SAMPLER_CREATE_IMAGE_PROCESSING_BIT_QCOM"/>
+ <enum bitpos="20" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_SAMPLE_WEIGHT_BIT_QCOM"/>
+ <enum bitpos="21" extends="VkImageUsageFlagBits" name="VK_IMAGE_USAGE_SAMPLE_BLOCK_MATCH_BIT_QCOM"/>
+ <enum offset="0" extends="VkDescriptorType" name="VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM"/>
+ <enum offset="1" extends="VkDescriptorType" name="VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM"/>
+ <type name="VkImageViewSampleWeightCreateInfoQCOM"/>
+ <type name="VkPhysicalDeviceImageProcessingFeaturesQCOM"/>
+ <type name="VkPhysicalDeviceImageProcessingPropertiesQCOM"/>
+ </require>
+ <require extension="VK_KHR_format_feature_flags2">
+ <enum bitpos="34" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_WEIGHT_IMAGE_BIT_QCOM"/>
+ <enum bitpos="35" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_WEIGHT_SAMPLED_IMAGE_BIT_QCOM"/>
+ <enum bitpos="36" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_BLOCK_MATCHING_BIT_QCOM"/>
+ <enum bitpos="37" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_BOX_FILTER_SAMPLED_BIT_QCOM"/>
+ </require>
+ </extension>
+ <extension name="VK_COREAVI_extension_442" number="442" author="COREAVI" contact="Aidan Fabius @afabius" supported="disabled">
+ <require>
+ <enum value="0" name="VK_COREAVI_EXTENSION_442_SPEC_VERSION"/>
+ <enum value="&quot;VK_COREAVI_extension_442&quot;" name="VK_COREAVI_EXTENSION_442_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_COREAVI_extension_443" number="443" author="COREAVI" contact="Aidan Fabius @afabius" supported="disabled">
+ <require>
+ <enum value="0" name="VK_COREAVI_EXTENSION_443_SPEC_VERSION"/>
+ <enum value="&quot;VK_COREAVI_extension_443&quot;" name="VK_COREAVI_EXTENSION_443_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_COREAVI_extension_444" number="444" author="COREAVI" contact="Aidan Fabius @afabius" supported="disabled">
+ <require>
+ <enum value="0" name="VK_COREAVI_EXTENSION_444_SPEC_VERSION"/>
+ <enum value="&quot;VK_COREAVI_extension_444&quot;" name="VK_COREAVI_EXTENSION_444_EXTENSION_NAME"/>
+ <enum extends="VkCommandPoolResetFlagBits" bitpos="1" name="VK_COMMAND_POOL_RESET_RESERVED_1_BIT_COREAVI"/>
+ </require>
+ </extension>
+ <extension name="VK_COREAVI_extension_445" number="445" author="COREAVI" contact="Aidan Fabius @afabius" supported="disabled">
+ <require>
+ <enum value="0" name="VK_COREAVI_EXTENSION_445_SPEC_VERSION"/>
+ <enum value="&quot;VK_COREAVI_extension_445&quot;" name="VK_COREAVI_EXTENSION_445_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_COREAVI_extension_446" number="446" author="COREAVI" contact="Aidan Fabius @afabius" supported="disabled">
+ <require>
+ <enum value="0" name="VK_COREAVI_EXTENSION_446_SPEC_VERSION"/>
+ <enum value="&quot;VK_COREAVI_extension_446&quot;" name="VK_COREAVI_EXTENSION_446_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_COREAVI_extension_447" number="447" author="COREAVI" contact="Aidan Fabius @afabius" supported="disabled">
+ <require>
+ <enum value="0" name="VK_COREAVI_EXTENSION_447_SPEC_VERSION"/>
+ <enum value="&quot;VK_COREAVI_extension_447&quot;" name="VK_COREAVI_EXTENSION_447_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_SEC_extension_448" number="448" author="SEC" contact="Ralph Potter gitlab:@r_potter" supported="disabled">
+ <require>
+ <enum value="0" name="VK_SEC_EXTENSION_448_SPEC_VERSION"/>
+ <enum value="&quot;VK_SEC_extension_448&quot;" name="VK_SEC_EXTENSION_448_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_SEC_extension_449" number="449" author="SEC" contact="Ralph Potter gitlab:@r_potter" supported="disabled">
+ <require>
+ <enum value="0" name="VK_SEC_EXTENSION_449_SPEC_VERSION"/>
+ <enum value="&quot;VK_SEC_extension_449&quot;" name="VK_SEC_EXTENSION_449_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_SEC_extension_450" number="450" author="SEC" contact="Ralph Potter gitlab:@r_potter" supported="disabled">
+ <require>
+ <enum value="0" name="VK_SEC_EXTENSION_450_SPEC_VERSION"/>
+ <enum value="&quot;VK_SEC_extension_450&quot;" name="VK_SEC_EXTENSION_450_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_SEC_extension_451" number="451" author="SEC" contact="Ralph Potter gitlab:@r_potter" supported="disabled">
+ <require>
+ <enum value="0" name="VK_SEC_EXTENSION_451_SPEC_VERSION"/>
+ <enum value="&quot;VK_SEC_extension_451&quot;" name="VK_SEC_EXTENSION_451_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_NV_extension_452" number="452" author="NV" contact="Piers Daniell @pdaniell-nv" supported="disabled">
+ <require>
+ <enum value="0" name="VK_NV_EXTENSION_452_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_extension_452&quot;" name="VK_NV_EXTENSION_452_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_ARM_extension_453" number="453" author="Arm" contact="Kevin Petit @kevinpetit" supported="disabled">
+ <require>
+ <enum value="0" name="VK_ARM_EXTENSION_453_SPEC_VERSION"/>
+ <enum value="&quot;VK_ARM_extension_453&quot;" name="VK_ARM_EXTENSION_453_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_GOOGLE_extension_454" number="454" author="GOOGLE" contact="Chad Versace @chadversary" supported="disabled">
+ <require>
+ <enum value="0" name="VK_GOOGLE_EXTENSION_454_SPEC_VERSION"/>
+ <enum value="&quot;VK_GOOGLE_extension_454&quot;" name="VK_GOOGLE_EXTENSION_454_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_GOOGLE_extension_455" number="455" author="GOOGLE" contact="Chad Versace @chadversary" supported="disabled">
+ <require>
+ <enum value="0" name="VK_GOOGLE_EXTENSION_455_SPEC_VERSION"/>
+ <enum value="&quot;VK_GOOGLE_extension_455&quot;" name="VK_GOOGLE_EXTENSION_455_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_NV_extension_456" number="456" author="NV" contact="Piers Daniell @pdaniell-nv" supported="disabled">
+ <require>
+ <enum value="0" name="VK_NV_EXTENSION_456_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_extension_456&quot;" name="VK_NV_EXTENSION_456_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_457" number="457" author="RASTERGRID" contact="Daniel Rakos @aqnuep1" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_457_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_457&quot;" name="VK_EXT_EXTENSION_457_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_458" number="458" author="RASTERGRID" contact="Daniel Rakos @aqnuep1" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_458_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_458&quot;" name="VK_EXT_EXTENSION_458_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_subpass_merge_feedback" number="459" type="device" author="EXT" contact="Ting Wei @catweiting" supported="vulkan">
+ <require>
+ <enum value="2" name="VK_EXT_SUBPASS_MERGE_FEEDBACK_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_subpass_merge_feedback&quot;" name="VK_EXT_SUBPASS_MERGE_FEEDBACK_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_MERGE_FEEDBACK_FEATURES_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_RENDER_PASS_CREATION_CONTROL_EXT"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_RENDER_PASS_CREATION_FEEDBACK_CREATE_INFO_EXT"/>
+ <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_RENDER_PASS_SUBPASS_FEEDBACK_CREATE_INFO_EXT"/>
+ <type name="VkPhysicalDeviceSubpassMergeFeedbackFeaturesEXT"/>
+ <type name="VkRenderPassCreationControlEXT"/>
+ <type name="VkRenderPassCreationFeedbackInfoEXT"/>
+ <type name="VkRenderPassCreationFeedbackCreateInfoEXT"/>
+ <type name="VkRenderPassSubpassFeedbackInfoEXT"/>
+ <type name="VkRenderPassSubpassFeedbackCreateInfoEXT"/>
+ <type name="VkSubpassMergeStatusEXT"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_460" number="460" author="EXT" contact="Charles Giessen @charles-lunarg" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_460_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_460&quot;" name="VK_EXT_EXTENSION_460_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_461" number="461" author="EXT" contact="Kevin Petit @kevinpetit" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_461_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_461&quot;" name="VK_EXT_EXTENSION_461_EXTENSION_NAME"/>
+ <enum bitpos="39" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_RESERVED_39_BIT_EXT"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_462" number="462" author="EXT" contact="Joshua Ashton @Joshua-Ashton,Liam Middlebrook @liam-middlebrook" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_462_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_462&quot;" name="VK_EXT_EXTENSION_462_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_shader_module_identifier" number="463" type="device" requires="VK_KHR_get_physical_device_properties2,VK_EXT_pipeline_creation_cache_control" author="EXT" contact="Hans-Kristian Arntzen @HansKristian-Work" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_EXT_SHADER_MODULE_IDENTIFIER_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_shader_module_identifier&quot;" name="VK_EXT_SHADER_MODULE_IDENTIFIER_EXTENSION_NAME"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MODULE_IDENTIFIER_FEATURES_EXT"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MODULE_IDENTIFIER_PROPERTIES_EXT"/>
+ <enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_MODULE_IDENTIFIER_CREATE_INFO_EXT"/>
+ <enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_SHADER_MODULE_IDENTIFIER_EXT"/>
+ <enum name="VK_MAX_SHADER_MODULE_IDENTIFIER_SIZE_EXT"/>
+ <type name="VkPhysicalDeviceShaderModuleIdentifierFeaturesEXT"/>
+ <type name="VkPhysicalDeviceShaderModuleIdentifierPropertiesEXT"/>
+ <type name="VkPipelineShaderStageModuleIdentifierCreateInfoEXT"/>
+ <type name="VkShaderModuleIdentifierEXT"/>
+ <command name="vkGetShaderModuleIdentifierEXT"/>
+ <command name="vkGetShaderModuleCreateInfoIdentifierEXT"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_464" number="464" author="EXT" contact="Jan-Harald Fredriksen @janharaldfredriksen-arm" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_464_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_464&quot;" name="VK_EXT_EXTENSION_464_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_NV_extension_465" number="465" author="NV" contact="Carsten Rohde @crohde" supported="disabled">
+ <require>
+ <enum value="0" name="VK_NV_EXTENSION_465_SPEC_VERSION"/>
+ <enum value="&quot;VK_NV_extension_465&quot;" name="VK_NV_EXTENSION_465_EXTENSION_NAME"/>
+ <enum bitpos="8" extends="VkQueueFlagBits" name="VK_QUEUE_RESERVED_8_BIT_NV"/>
+ <enum bitpos="29" extends="VkPipelineStageFlagBits2" name="VK_PIPELINE_STAGE_2_RESERVED_29_BIT_NV"/>
+ <enum bitpos="42" extends="VkAccessFlagBits2" name="VK_ACCESS_2_RESERVED_42_BIT_NV"/>
+ <enum bitpos="43" extends="VkAccessFlagBits2" name="VK_ACCESS_2_RESERVED_43_BIT_NV"/>
+ <enum bitpos="40" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_RESERVED_40_BIT_NV"/>
+ <enum bitpos="41" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_RESERVED_41_BIT_NV"/>
+ <enum bitpos="42" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_RESERVED_42_BIT_NV"/>
+ <enum bitpos="43" extends="VkFormatFeatureFlagBits2" name="VK_FORMAT_FEATURE_2_RESERVED_43_BIT_NV"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_466" number="466" author="EXT" contact="Shahbaz Youssefi @syoussefi" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_466_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_466&quot;" name="VK_EXT_EXTENSION_466_EXTENSION_NAME"/>
+ <enum bitpos="7" extends="VkSubpassDescriptionFlagBits" name="VK_SUBPASS_DESCRIPTION_RESERVED_7_BIT_EXT"/>
+ <enum bitpos="3" extends="VkRenderingFlagBits" name="VK_RENDERING_RESERVED_3_BIT_EXT"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_467" number="467" author="EXT" contact="Shahbaz Youssefi @syoussefi" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_467_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_467&quot;" name="VK_EXT_EXTENSION_467_EXTENSION_NAME"/>
+ <enum bitpos="27" extends="VkPipelineCreateFlagBits" name="VK_PIPELINE_CREATE_RESERVED_27_BIT_EXT"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_468" number="468" author="EXT" contact="Shahbaz Youssefi @syoussefi" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_468_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_468&quot;" name="VK_EXT_EXTENSION_468_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_ANDROID_extension_469" number="469" type="device" platform="android" author="ANDROID" contact="Chris Forbes @chrisforbes" supported="disabled">
+ <require>
+ <enum value="0" name="VK_ANDROID_EXTENSION_469_SPEC_VERSION"/>
+ <enum value="&quot;VK_ANDROID_extension_469&quot;" name="VK_ANDROID_EXTENSION_469_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_AMD_extension_470" number="470" author="AMD" contact="Stu Smith" supported="disabled">
+ <require>
+ <enum value="0" name="VK_AMD_EXTENSION_470_SPEC_VERSION"/>
+ <enum value="&quot;VK_AMD_extension_470&quot;" name="VK_AMD_EXTENSION_470_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_AMD_extension_471" number="471" author="AMD" contact="Stu Smith" supported="disabled">
+ <require>
+ <enum value="0" name="VK_AMD_EXTENSION_471_SPEC_VERSION"/>
+ <enum value="&quot;VK_AMD_extension_471&quot;" name="VK_AMD_EXTENSION_471_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_AMD_extension_472" number="472" author="AMD" contact="Stu Smith" supported="disabled">
+ <require>
+ <enum value="0" name="VK_AMD_EXTENSION_472_SPEC_VERSION"/>
+ <enum value="&quot;VK_AMD_extension_472&quot;" name="VK_AMD_EXTENSION_472_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_AMD_extension_473" number="473" author="AMD" contact="Stu Smith" supported="disabled">
+ <require>
+ <enum value="0" name="VK_AMD_EXTENSION_473_SPEC_VERSION"/>
+ <enum value="&quot;VK_AMD_extension_473&quot;" name="VK_AMD_EXTENSION_473_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_AMD_extension_474" number="474" author="AMD" contact="Stu Smith" supported="disabled">
+ <require>
+ <enum value="0" name="VK_AMD_EXTENSION_474_SPEC_VERSION"/>
+ <enum value="&quot;VK_AMD_extension_474&quot;" name="VK_AMD_EXTENSION_474_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_AMD_extension_475" number="475" author="AMD" contact="Stu Smith" supported="disabled">
+ <require>
+ <enum value="0" name="VK_AMD_EXTENSION_475_SPEC_VERSION"/>
+ <enum value="&quot;VK_AMD_extension_475&quot;" name="VK_AMD_EXTENSION_475_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_AMD_extension_476" number="476" author="AMD" contact="Stu Smith" supported="disabled">
+ <require>
+ <enum value="0" name="VK_AMD_EXTENSION_476_SPEC_VERSION"/>
+ <enum value="&quot;VK_AMD_extension_476&quot;" name="VK_AMD_EXTENSION_476_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_AMD_extension_477" number="477" author="AMD" contact="Stu Smith" supported="disabled">
+ <require>
+ <enum value="0" name="VK_AMD_EXTENSION_477_SPEC_VERSION"/>
+ <enum value="&quot;VK_AMD_extension_477&quot;" name="VK_AMD_EXTENSION_477_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_AMD_extension_478" number="478" author="AMD" contact="Stu Smith" supported="disabled">
+ <require>
+ <enum value="0" name="VK_AMD_EXTENSION_478_SPEC_VERSION"/>
+ <enum value="&quot;VK_AMD_extension_478&quot;" name="VK_AMD_EXTENSION_478_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_AMD_extension_479" number="479" author="AMD" contact="Stu Smith" supported="disabled">
+ <require>
+ <enum value="0" name="VK_AMD_EXTENSION_479_SPEC_VERSION"/>
+ <enum value="&quot;VK_AMD_extension_479&quot;" name="VK_AMD_EXTENSION_479_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_480" number="480" author="EXT" contact="Daniel Stone" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_480_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_480&quot;" name="VK_EXT_EXTENSION_480_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_481" number="481" author="EXT" contact="Daniel Stone" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_481_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_481&quot;" name="VK_EXT_EXTENSION_481_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_482" number="482" author="KHR" contact="Eric Werness" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_482_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_482&quot;" name="VK_EXT_EXTENSION_482_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_483" number="483" author="EXT" contact="Piers Daniell" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_483_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_483&quot;" name="VK_EXT_EXTENSION_483_EXTENSION_NAME"/>
+ <enum bitpos="15" extends="VkShaderStageFlagBits" name="VK_SHADER_STAGE_EXT_483_RESERVE_15"/>
+ <enum bitpos="16" extends="VkShaderStageFlagBits" name="VK_SHADER_STAGE_EXT_483_RESERVE_16"/>
+ <enum bitpos="17" extends="VkShaderStageFlagBits" name="VK_SHADER_STAGE_EXT_483_RESERVE_17"/>
+ </require>
+ </extension>
+ <extension name="VK_EXT_extension_484" number="484" author="KHR" contact="Chris Glover @cdglove" supported="disabled">
+ <require>
+ <enum value="0" name="VK_EXT_EXTENSION_484_SPEC_VERSION"/>
+ <enum value="&quot;VK_EXT_extension_484&quot;" name="VK_EXT_EXTENSION_484_EXTENSION_NAME"/>
+ </require>
+ </extension>
+ <extension name="VK_QCOM_tile_properties" number="485" type="device" requires="VK_KHR_get_physical_device_properties2" author="QCOM" contact="Jeff Leger @jackohound" supported="vulkan">
+ <require>
+ <enum value="1" name="VK_QCOM_TILE_PROPERTIES_SPEC_VERSION"/>
+ <enum value="&quot;VK_QCOM_tile_properties&quot;" name="VK_QCOM_TILE_PROPERTIES_EXTENSION_NAME"/>
+ <command name="vkGetFramebufferTilePropertiesQCOM"/>
+ <command name="vkGetDynamicRenderingTilePropertiesQCOM"/>
+ <enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TILE_PROPERTIES_FEATURES_QCOM"/>
+ <enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_TILE_PROPERTIES_QCOM"/>
+ <type name="VkPhysicalDeviceTilePropertiesFeaturesQCOM"/>
+ <type name="VkTilePropertiesQCOM"/>
+ <type name="VkRenderingInfoKHR"/>
+ </require>
+ </extension>
</extensions>
+ <formats>
+ <format name="VK_FORMAT_R4G4_UNORM_PACK8" class="8-bit" blockSize="1" texelsPerBlock="1" packed="8">
+ <component name="R" bits="4" numericFormat="UNORM"/>
+ <component name="G" bits="4" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_R4G4B4A4_UNORM_PACK16" class="16-bit" blockSize="2" texelsPerBlock="1" packed="16">
+ <component name="R" bits="4" numericFormat="UNORM"/>
+ <component name="G" bits="4" numericFormat="UNORM"/>
+ <component name="B" bits="4" numericFormat="UNORM"/>
+ <component name="A" bits="4" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_B4G4R4A4_UNORM_PACK16" class="16-bit" blockSize="2" texelsPerBlock="1" packed="16">
+ <component name="B" bits="4" numericFormat="UNORM"/>
+ <component name="G" bits="4" numericFormat="UNORM"/>
+ <component name="R" bits="4" numericFormat="UNORM"/>
+ <component name="A" bits="4" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_R5G6B5_UNORM_PACK16" class="16-bit" blockSize="2" texelsPerBlock="1" packed="16">
+ <component name="R" bits="5" numericFormat="UNORM"/>
+ <component name="G" bits="6" numericFormat="UNORM"/>
+ <component name="B" bits="5" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_B5G6R5_UNORM_PACK16" class="16-bit" blockSize="2" texelsPerBlock="1" packed="16">
+ <component name="B" bits="5" numericFormat="UNORM"/>
+ <component name="G" bits="6" numericFormat="UNORM"/>
+ <component name="R" bits="5" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_R5G5B5A1_UNORM_PACK16" class="16-bit" blockSize="2" texelsPerBlock="1" packed="16">
+ <component name="R" bits="5" numericFormat="UNORM"/>
+ <component name="G" bits="5" numericFormat="UNORM"/>
+ <component name="B" bits="5" numericFormat="UNORM"/>
+ <component name="A" bits="1" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_B5G5R5A1_UNORM_PACK16" class="16-bit" blockSize="2" texelsPerBlock="1" packed="16">
+ <component name="B" bits="5" numericFormat="UNORM"/>
+ <component name="R" bits="5" numericFormat="UNORM"/>
+ <component name="G" bits="5" numericFormat="UNORM"/>
+ <component name="A" bits="1" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_A1R5G5B5_UNORM_PACK16" class="16-bit" blockSize="2" texelsPerBlock="1" packed="16">
+ <component name="A" bits="1" numericFormat="UNORM"/>
+ <component name="R" bits="5" numericFormat="UNORM"/>
+ <component name="G" bits="5" numericFormat="UNORM"/>
+ <component name="B" bits="5" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_R8_UNORM" class="8-bit" blockSize="1" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="UNORM"/>
+ <spirvimageformat name="R8"/>
+ </format>
+ <format name="VK_FORMAT_R8_SNORM" class="8-bit" blockSize="1" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SNORM"/>
+ <spirvimageformat name="R8Snorm"/>
+ </format>
+ <format name="VK_FORMAT_R8_USCALED" class="8-bit" blockSize="1" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="USCALED"/>
+ </format>
+ <format name="VK_FORMAT_R8_SSCALED" class="8-bit" blockSize="1" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SSCALED"/>
+ </format>
+ <format name="VK_FORMAT_R8_UINT" class="8-bit" blockSize="1" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="UINT"/>
+ <spirvimageformat name="R8ui"/>
+ </format>
+ <format name="VK_FORMAT_R8_SINT" class="8-bit" blockSize="1" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SINT"/>
+ <spirvimageformat name="R8i"/>
+ </format>
+ <format name="VK_FORMAT_R8_SRGB" class="8-bit" blockSize="1" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_R8G8_UNORM" class="16-bit" blockSize="2" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="UNORM"/>
+ <component name="G" bits="8" numericFormat="UNORM"/>
+ <spirvimageformat name="Rg8"/>
+ </format>
+ <format name="VK_FORMAT_R8G8_SNORM" class="16-bit" blockSize="2" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SNORM"/>
+ <component name="G" bits="8" numericFormat="SNORM"/>
+ <spirvimageformat name="Rg8Snorm"/>
+ </format>
+ <format name="VK_FORMAT_R8G8_USCALED" class="16-bit" blockSize="2" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="USCALED"/>
+ <component name="G" bits="8" numericFormat="USCALED"/>
+ </format>
+ <format name="VK_FORMAT_R8G8_SSCALED" class="16-bit" blockSize="2" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SSCALED"/>
+ <component name="G" bits="8" numericFormat="SSCALED"/>
+ </format>
+ <format name="VK_FORMAT_R8G8_UINT" class="16-bit" blockSize="2" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="UINT"/>
+ <component name="G" bits="8" numericFormat="UINT"/>
+ <spirvimageformat name="Rg8ui"/>
+ </format>
+ <format name="VK_FORMAT_R8G8_SINT" class="16-bit" blockSize="2" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SINT"/>
+ <component name="G" bits="8" numericFormat="SINT"/>
+ <spirvimageformat name="Rg8i"/>
+ </format>
+ <format name="VK_FORMAT_R8G8_SRGB" class="16-bit" blockSize="2" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SRGB"/>
+ <component name="G" bits="8" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_R8G8B8_UNORM" class="24-bit" blockSize="3" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="UNORM"/>
+ <component name="G" bits="8" numericFormat="UNORM"/>
+ <component name="B" bits="8" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_R8G8B8_SNORM" class="24-bit" blockSize="3" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SNORM"/>
+ <component name="G" bits="8" numericFormat="SNORM"/>
+ <component name="B" bits="8" numericFormat="SNORM"/>
+ </format>
+ <format name="VK_FORMAT_R8G8B8_USCALED" class="24-bit" blockSize="3" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="USCALED"/>
+ <component name="G" bits="8" numericFormat="USCALED"/>
+ <component name="B" bits="8" numericFormat="USCALED"/>
+ </format>
+ <format name="VK_FORMAT_R8G8B8_SSCALED" class="24-bit" blockSize="3" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SSCALED"/>
+ <component name="G" bits="8" numericFormat="SSCALED"/>
+ <component name="B" bits="8" numericFormat="SSCALED"/>
+ </format>
+ <format name="VK_FORMAT_R8G8B8_UINT" class="24-bit" blockSize="3" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="UINT"/>
+ <component name="G" bits="8" numericFormat="UINT"/>
+ <component name="B" bits="8" numericFormat="UINT"/>
+ </format>
+ <format name="VK_FORMAT_R8G8B8_SINT" class="24-bit" blockSize="3" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SINT"/>
+ <component name="G" bits="8" numericFormat="SINT"/>
+ <component name="B" bits="8" numericFormat="SINT"/>
+ </format>
+ <format name="VK_FORMAT_R8G8B8_SRGB" class="24-bit" blockSize="3" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SRGB"/>
+ <component name="G" bits="8" numericFormat="SRGB"/>
+ <component name="B" bits="8" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_B8G8R8_UNORM" class="24-bit" blockSize="3" texelsPerBlock="1">
+ <component name="B" bits="8" numericFormat="UNORM"/>
+ <component name="G" bits="8" numericFormat="UNORM"/>
+ <component name="R" bits="8" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_B8G8R8_SNORM" class="24-bit" blockSize="3" texelsPerBlock="1">
+ <component name="B" bits="8" numericFormat="SNORM"/>
+ <component name="G" bits="8" numericFormat="SNORM"/>
+ <component name="R" bits="8" numericFormat="SNORM"/>
+ </format>
+ <format name="VK_FORMAT_B8G8R8_USCALED" class="24-bit" blockSize="3" texelsPerBlock="1">
+ <component name="B" bits="8" numericFormat="USCALED"/>
+ <component name="G" bits="8" numericFormat="USCALED"/>
+ <component name="R" bits="8" numericFormat="USCALED"/>
+ </format>
+ <format name="VK_FORMAT_B8G8R8_SSCALED" class="24-bit" blockSize="3" texelsPerBlock="1">
+ <component name="B" bits="8" numericFormat="SSCALED"/>
+ <component name="G" bits="8" numericFormat="SSCALED"/>
+ <component name="R" bits="8" numericFormat="SSCALED"/>
+ </format>
+ <format name="VK_FORMAT_B8G8R8_UINT" class="24-bit" blockSize="3" texelsPerBlock="1">
+ <component name="B" bits="8" numericFormat="UINT"/>
+ <component name="G" bits="8" numericFormat="UINT"/>
+ <component name="R" bits="8" numericFormat="UINT"/>
+ </format>
+ <format name="VK_FORMAT_B8G8R8_SINT" class="24-bit" blockSize="3" texelsPerBlock="1">
+ <component name="B" bits="8" numericFormat="SINT"/>
+ <component name="G" bits="8" numericFormat="SINT"/>
+ <component name="R" bits="8" numericFormat="SINT"/>
+ </format>
+ <format name="VK_FORMAT_B8G8R8_SRGB" class="24-bit" blockSize="3" texelsPerBlock="1">
+ <component name="B" bits="8" numericFormat="SRGB"/>
+ <component name="G" bits="8" numericFormat="SRGB"/>
+ <component name="R" bits="8" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_R8G8B8A8_UNORM" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="UNORM"/>
+ <component name="G" bits="8" numericFormat="UNORM"/>
+ <component name="B" bits="8" numericFormat="UNORM"/>
+ <component name="A" bits="8" numericFormat="UNORM"/>
+ <spirvimageformat name="Rgba8"/>
+ </format>
+ <format name="VK_FORMAT_R8G8B8A8_SNORM" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SNORM"/>
+ <component name="G" bits="8" numericFormat="SNORM"/>
+ <component name="B" bits="8" numericFormat="SNORM"/>
+ <component name="A" bits="8" numericFormat="SNORM"/>
+ <spirvimageformat name="Rgba8Snorm"/>
+ </format>
+ <format name="VK_FORMAT_R8G8B8A8_USCALED" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="USCALED"/>
+ <component name="G" bits="8" numericFormat="USCALED"/>
+ <component name="B" bits="8" numericFormat="USCALED"/>
+ <component name="A" bits="8" numericFormat="USCALED"/>
+ </format>
+ <format name="VK_FORMAT_R8G8B8A8_SSCALED" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SSCALED"/>
+ <component name="G" bits="8" numericFormat="SSCALED"/>
+ <component name="B" bits="8" numericFormat="SSCALED"/>
+ <component name="A" bits="8" numericFormat="SSCALED"/>
+ </format>
+ <format name="VK_FORMAT_R8G8B8A8_UINT" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="UINT"/>
+ <component name="G" bits="8" numericFormat="UINT"/>
+ <component name="B" bits="8" numericFormat="UINT"/>
+ <component name="A" bits="8" numericFormat="UINT"/>
+ <spirvimageformat name="Rgba8ui"/>
+ </format>
+ <format name="VK_FORMAT_R8G8B8A8_SINT" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SINT"/>
+ <component name="G" bits="8" numericFormat="SINT"/>
+ <component name="B" bits="8" numericFormat="SINT"/>
+ <component name="A" bits="8" numericFormat="SINT"/>
+ <spirvimageformat name="Rgba8i"/>
+ </format>
+ <format name="VK_FORMAT_R8G8B8A8_SRGB" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="8" numericFormat="SRGB"/>
+ <component name="G" bits="8" numericFormat="SRGB"/>
+ <component name="B" bits="8" numericFormat="SRGB"/>
+ <component name="A" bits="8" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_B8G8R8A8_UNORM" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="B" bits="8" numericFormat="UNORM"/>
+ <component name="G" bits="8" numericFormat="UNORM"/>
+ <component name="R" bits="8" numericFormat="UNORM"/>
+ <component name="A" bits="8" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_B8G8R8A8_SNORM" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="B" bits="8" numericFormat="SNORM"/>
+ <component name="G" bits="8" numericFormat="SNORM"/>
+ <component name="R" bits="8" numericFormat="SNORM"/>
+ <component name="A" bits="8" numericFormat="SNORM"/>
+ </format>
+ <format name="VK_FORMAT_B8G8R8A8_USCALED" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="B" bits="8" numericFormat="USCALED"/>
+ <component name="G" bits="8" numericFormat="USCALED"/>
+ <component name="R" bits="8" numericFormat="USCALED"/>
+ <component name="A" bits="8" numericFormat="USCALED"/>
+ </format>
+ <format name="VK_FORMAT_B8G8R8A8_SSCALED" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="B" bits="8" numericFormat="SSCALED"/>
+ <component name="G" bits="8" numericFormat="SSCALED"/>
+ <component name="R" bits="8" numericFormat="SSCALED"/>
+ <component name="A" bits="8" numericFormat="SSCALED"/>
+ </format>
+ <format name="VK_FORMAT_B8G8R8A8_UINT" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="B" bits="8" numericFormat="UINT"/>
+ <component name="G" bits="8" numericFormat="UINT"/>
+ <component name="R" bits="8" numericFormat="UINT"/>
+ <component name="A" bits="8" numericFormat="UINT"/>
+ </format>
+ <format name="VK_FORMAT_B8G8R8A8_SINT" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="B" bits="8" numericFormat="SINT"/>
+ <component name="G" bits="8" numericFormat="SINT"/>
+ <component name="R" bits="8" numericFormat="SINT"/>
+ <component name="A" bits="8" numericFormat="SINT"/>
+ </format>
+ <format name="VK_FORMAT_B8G8R8A8_SRGB" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="B" bits="8" numericFormat="SRGB"/>
+ <component name="G" bits="8" numericFormat="SRGB"/>
+ <component name="R" bits="8" numericFormat="SRGB"/>
+ <component name="A" bits="8" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_A8B8G8R8_UNORM_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="8" numericFormat="UNORM"/>
+ <component name="B" bits="8" numericFormat="UNORM"/>
+ <component name="G" bits="8" numericFormat="UNORM"/>
+ <component name="R" bits="8" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_A8B8G8R8_SNORM_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="8" numericFormat="SNORM"/>
+ <component name="B" bits="8" numericFormat="SNORM"/>
+ <component name="G" bits="8" numericFormat="SNORM"/>
+ <component name="R" bits="8" numericFormat="SNORM"/>
+ </format>
+ <format name="VK_FORMAT_A8B8G8R8_USCALED_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="8" numericFormat="USCALED"/>
+ <component name="B" bits="8" numericFormat="USCALED"/>
+ <component name="G" bits="8" numericFormat="USCALED"/>
+ <component name="R" bits="8" numericFormat="USCALED"/>
+ </format>
+ <format name="VK_FORMAT_A8B8G8R8_SSCALED_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="8" numericFormat="SSCALED"/>
+ <component name="B" bits="8" numericFormat="SSCALED"/>
+ <component name="G" bits="8" numericFormat="SSCALED"/>
+ <component name="R" bits="8" numericFormat="SSCALED"/>
+ </format>
+ <format name="VK_FORMAT_A8B8G8R8_UINT_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="8" numericFormat="UINT"/>
+ <component name="B" bits="8" numericFormat="UINT"/>
+ <component name="G" bits="8" numericFormat="UINT"/>
+ <component name="R" bits="8" numericFormat="UINT"/>
+ </format>
+ <format name="VK_FORMAT_A8B8G8R8_SINT_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="8" numericFormat="SINT"/>
+ <component name="B" bits="8" numericFormat="SINT"/>
+ <component name="G" bits="8" numericFormat="SINT"/>
+ <component name="R" bits="8" numericFormat="SINT"/>
+ </format>
+ <format name="VK_FORMAT_A8B8G8R8_SRGB_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="8" numericFormat="SRGB"/>
+ <component name="B" bits="8" numericFormat="SRGB"/>
+ <component name="G" bits="8" numericFormat="SRGB"/>
+ <component name="R" bits="8" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_A2R10G10B10_UNORM_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="2" numericFormat="UNORM"/>
+ <component name="R" bits="10" numericFormat="UNORM"/>
+ <component name="G" bits="10" numericFormat="UNORM"/>
+ <component name="B" bits="10" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_A2R10G10B10_SNORM_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="2" numericFormat="SNORM"/>
+ <component name="R" bits="10" numericFormat="SNORM"/>
+ <component name="G" bits="10" numericFormat="SNORM"/>
+ <component name="B" bits="10" numericFormat="SNORM"/>
+ </format>
+ <format name="VK_FORMAT_A2R10G10B10_USCALED_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="2" numericFormat="USCALED"/>
+ <component name="R" bits="10" numericFormat="USCALED"/>
+ <component name="G" bits="10" numericFormat="USCALED"/>
+ <component name="B" bits="10" numericFormat="USCALED"/>
+ </format>
+ <format name="VK_FORMAT_A2R10G10B10_SSCALED_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="2" numericFormat="SSCALED"/>
+ <component name="R" bits="10" numericFormat="SSCALED"/>
+ <component name="G" bits="10" numericFormat="SSCALED"/>
+ <component name="B" bits="10" numericFormat="SSCALED"/>
+ </format>
+ <format name="VK_FORMAT_A2R10G10B10_UINT_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="2" numericFormat="UINT"/>
+ <component name="R" bits="10" numericFormat="UINT"/>
+ <component name="G" bits="10" numericFormat="UINT"/>
+ <component name="B" bits="10" numericFormat="UINT"/>
+ </format>
+ <format name="VK_FORMAT_A2R10G10B10_SINT_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="2" numericFormat="SINT"/>
+ <component name="R" bits="10" numericFormat="SINT"/>
+ <component name="G" bits="10" numericFormat="SINT"/>
+ <component name="B" bits="10" numericFormat="SINT"/>
+ </format>
+ <format name="VK_FORMAT_A2B10G10R10_UNORM_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="2" numericFormat="UNORM"/>
+ <component name="B" bits="10" numericFormat="UNORM"/>
+ <component name="G" bits="10" numericFormat="UNORM"/>
+ <component name="R" bits="10" numericFormat="UNORM"/>
+ <spirvimageformat name="Rgb10A2"/>
+ </format>
+ <format name="VK_FORMAT_A2B10G10R10_SNORM_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="2" numericFormat="SNORM"/>
+ <component name="B" bits="10" numericFormat="SNORM"/>
+ <component name="G" bits="10" numericFormat="SNORM"/>
+ <component name="R" bits="10" numericFormat="SNORM"/>
+ </format>
+ <format name="VK_FORMAT_A2B10G10R10_USCALED_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="2" numericFormat="USCALED"/>
+ <component name="B" bits="10" numericFormat="USCALED"/>
+ <component name="G" bits="10" numericFormat="USCALED"/>
+ <component name="R" bits="10" numericFormat="USCALED"/>
+ </format>
+ <format name="VK_FORMAT_A2B10G10R10_SSCALED_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="2" numericFormat="SSCALED"/>
+ <component name="B" bits="10" numericFormat="SSCALED"/>
+ <component name="G" bits="10" numericFormat="SSCALED"/>
+ <component name="R" bits="10" numericFormat="SSCALED"/>
+ </format>
+ <format name="VK_FORMAT_A2B10G10R10_UINT_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="2" numericFormat="UINT"/>
+ <component name="B" bits="10" numericFormat="UINT"/>
+ <component name="G" bits="10" numericFormat="UINT"/>
+ <component name="R" bits="10" numericFormat="UINT"/>
+ <spirvimageformat name="Rgb10a2ui"/>
+ </format>
+ <format name="VK_FORMAT_A2B10G10R10_SINT_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="A" bits="2" numericFormat="SINT"/>
+ <component name="B" bits="10" numericFormat="SINT"/>
+ <component name="G" bits="10" numericFormat="SINT"/>
+ <component name="R" bits="10" numericFormat="SINT"/>
+ </format>
+ <format name="VK_FORMAT_R16_UNORM" class="16-bit" blockSize="2" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="UNORM"/>
+ <spirvimageformat name="R16"/>
+ </format>
+ <format name="VK_FORMAT_R16_SNORM" class="16-bit" blockSize="2" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SNORM"/>
+ <spirvimageformat name="R16Snorm"/>
+ </format>
+ <format name="VK_FORMAT_R16_USCALED" class="16-bit" blockSize="2" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="USCALED"/>
+ </format>
+ <format name="VK_FORMAT_R16_SSCALED" class="16-bit" blockSize="2" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SSCALED"/>
+ </format>
+ <format name="VK_FORMAT_R16_UINT" class="16-bit" blockSize="2" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="UINT"/>
+ <spirvimageformat name="R16ui"/>
+ </format>
+ <format name="VK_FORMAT_R16_SINT" class="16-bit" blockSize="2" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SINT"/>
+ <spirvimageformat name="R16i"/>
+ </format>
+ <format name="VK_FORMAT_R16_SFLOAT" class="16-bit" blockSize="2" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SFLOAT"/>
+ <spirvimageformat name="R16f"/>
+ </format>
+ <format name="VK_FORMAT_R16G16_UNORM" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="UNORM"/>
+ <component name="G" bits="16" numericFormat="UNORM"/>
+ <spirvimageformat name="Rg16"/>
+ </format>
+ <format name="VK_FORMAT_R16G16_SNORM" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SNORM"/>
+ <component name="G" bits="16" numericFormat="SNORM"/>
+ <spirvimageformat name="Rg16Snorm"/>
+ </format>
+ <format name="VK_FORMAT_R16G16_USCALED" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="USCALED"/>
+ <component name="G" bits="16" numericFormat="USCALED"/>
+ </format>
+ <format name="VK_FORMAT_R16G16_SSCALED" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SSCALED"/>
+ <component name="G" bits="16" numericFormat="SSCALED"/>
+ </format>
+ <format name="VK_FORMAT_R16G16_UINT" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="UINT"/>
+ <component name="G" bits="16" numericFormat="UINT"/>
+ <spirvimageformat name="Rg16ui"/>
+ </format>
+ <format name="VK_FORMAT_R16G16_SINT" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SINT"/>
+ <component name="G" bits="16" numericFormat="SINT"/>
+ <spirvimageformat name="Rg16i"/>
+ </format>
+ <format name="VK_FORMAT_R16G16_SFLOAT" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SFLOAT"/>
+ <component name="G" bits="16" numericFormat="SFLOAT"/>
+ <spirvimageformat name="Rg16f"/>
+ </format>
+ <format name="VK_FORMAT_R16G16B16_UNORM" class="48-bit" blockSize="6" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="UNORM"/>
+ <component name="G" bits="16" numericFormat="UNORM"/>
+ <component name="B" bits="16" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_R16G16B16_SNORM" class="48-bit" blockSize="6" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SNORM"/>
+ <component name="G" bits="16" numericFormat="SNORM"/>
+ <component name="B" bits="16" numericFormat="SNORM"/>
+ </format>
+ <format name="VK_FORMAT_R16G16B16_USCALED" class="48-bit" blockSize="6" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="USCALED"/>
+ <component name="G" bits="16" numericFormat="USCALED"/>
+ <component name="B" bits="16" numericFormat="USCALED"/>
+ </format>
+ <format name="VK_FORMAT_R16G16B16_SSCALED" class="48-bit" blockSize="6" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SSCALED"/>
+ <component name="G" bits="16" numericFormat="SSCALED"/>
+ <component name="B" bits="16" numericFormat="SSCALED"/>
+ </format>
+ <format name="VK_FORMAT_R16G16B16_UINT" class="48-bit" blockSize="6" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="UINT"/>
+ <component name="G" bits="16" numericFormat="UINT"/>
+ <component name="B" bits="16" numericFormat="UINT"/>
+ </format>
+ <format name="VK_FORMAT_R16G16B16_SINT" class="48-bit" blockSize="6" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SINT"/>
+ <component name="G" bits="16" numericFormat="SINT"/>
+ <component name="B" bits="16" numericFormat="SINT"/>
+ </format>
+ <format name="VK_FORMAT_R16G16B16_SFLOAT" class="48-bit" blockSize="6" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SFLOAT"/>
+ <component name="G" bits="16" numericFormat="SFLOAT"/>
+ <component name="B" bits="16" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_R16G16B16A16_UNORM" class="64-bit" blockSize="8" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="UNORM"/>
+ <component name="G" bits="16" numericFormat="UNORM"/>
+ <component name="B" bits="16" numericFormat="UNORM"/>
+ <component name="A" bits="16" numericFormat="UNORM"/>
+ <spirvimageformat name="Rgba16"/>
+ </format>
+ <format name="VK_FORMAT_R16G16B16A16_SNORM" class="64-bit" blockSize="8" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SNORM"/>
+ <component name="G" bits="16" numericFormat="SNORM"/>
+ <component name="B" bits="16" numericFormat="SNORM"/>
+ <component name="A" bits="16" numericFormat="SNORM"/>
+ <spirvimageformat name="Rgba16Snorm"/>
+ </format>
+ <format name="VK_FORMAT_R16G16B16A16_USCALED" class="64-bit" blockSize="8" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="USCALED"/>
+ <component name="G" bits="16" numericFormat="USCALED"/>
+ <component name="B" bits="16" numericFormat="USCALED"/>
+ <component name="A" bits="16" numericFormat="USCALED"/>
+ </format>
+ <format name="VK_FORMAT_R16G16B16A16_SSCALED" class="64-bit" blockSize="8" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SSCALED"/>
+ <component name="G" bits="16" numericFormat="SSCALED"/>
+ <component name="B" bits="16" numericFormat="SSCALED"/>
+ <component name="A" bits="16" numericFormat="SSCALED"/>
+ </format>
+ <format name="VK_FORMAT_R16G16B16A16_UINT" class="64-bit" blockSize="8" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="UINT"/>
+ <component name="G" bits="16" numericFormat="UINT"/>
+ <component name="B" bits="16" numericFormat="UINT"/>
+ <component name="A" bits="16" numericFormat="UINT"/>
+ <spirvimageformat name="Rgba16ui"/>
+ </format>
+ <format name="VK_FORMAT_R16G16B16A16_SINT" class="64-bit" blockSize="8" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SINT"/>
+ <component name="G" bits="16" numericFormat="SINT"/>
+ <component name="B" bits="16" numericFormat="SINT"/>
+ <component name="A" bits="16" numericFormat="SINT"/>
+ <spirvimageformat name="Rgba16i"/>
+ </format>
+ <format name="VK_FORMAT_R16G16B16A16_SFLOAT" class="64-bit" blockSize="8" texelsPerBlock="1">
+ <component name="R" bits="16" numericFormat="SFLOAT"/>
+ <component name="G" bits="16" numericFormat="SFLOAT"/>
+ <component name="B" bits="16" numericFormat="SFLOAT"/>
+ <component name="A" bits="16" numericFormat="SFLOAT"/>
+ <spirvimageformat name="Rgba16f"/>
+ </format>
+ <format name="VK_FORMAT_R32_UINT" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="32" numericFormat="UINT"/>
+ <spirvimageformat name="R32ui"/>
+ </format>
+ <format name="VK_FORMAT_R32_SINT" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="32" numericFormat="SINT"/>
+ <spirvimageformat name="R32i"/>
+ </format>
+ <format name="VK_FORMAT_R32_SFLOAT" class="32-bit" blockSize="4" texelsPerBlock="1">
+ <component name="R" bits="32" numericFormat="SFLOAT"/>
+ <spirvimageformat name="R32f"/>
+ </format>
+ <format name="VK_FORMAT_R32G32_UINT" class="64-bit" blockSize="8" texelsPerBlock="1">
+ <component name="R" bits="32" numericFormat="UINT"/>
+ <component name="G" bits="32" numericFormat="UINT"/>
+ <spirvimageformat name="Rg32ui"/>
+ </format>
+ <format name="VK_FORMAT_R32G32_SINT" class="64-bit" blockSize="8" texelsPerBlock="1">
+ <component name="R" bits="32" numericFormat="SINT"/>
+ <component name="G" bits="32" numericFormat="SINT"/>
+ <spirvimageformat name="Rg32i"/>
+ </format>
+ <format name="VK_FORMAT_R32G32_SFLOAT" class="64-bit" blockSize="8" texelsPerBlock="1">
+ <component name="R" bits="32" numericFormat="SFLOAT"/>
+ <component name="G" bits="32" numericFormat="SFLOAT"/>
+ <spirvimageformat name="Rg32f"/>
+ </format>
+ <format name="VK_FORMAT_R32G32B32_UINT" class="96-bit" blockSize="12" texelsPerBlock="1">
+ <component name="R" bits="32" numericFormat="UINT"/>
+ <component name="G" bits="32" numericFormat="UINT"/>
+ <component name="B" bits="32" numericFormat="UINT"/>
+ </format>
+ <format name="VK_FORMAT_R32G32B32_SINT" class="96-bit" blockSize="12" texelsPerBlock="1">
+ <component name="R" bits="32" numericFormat="SINT"/>
+ <component name="G" bits="32" numericFormat="SINT"/>
+ <component name="B" bits="32" numericFormat="SINT"/>
+ </format>
+ <format name="VK_FORMAT_R32G32B32_SFLOAT" class="96-bit" blockSize="12" texelsPerBlock="1">
+ <component name="R" bits="32" numericFormat="SFLOAT"/>
+ <component name="G" bits="32" numericFormat="SFLOAT"/>
+ <component name="B" bits="32" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_R32G32B32A32_UINT" class="128-bit" blockSize="16" texelsPerBlock="1">
+ <component name="R" bits="32" numericFormat="UINT"/>
+ <component name="G" bits="32" numericFormat="UINT"/>
+ <component name="B" bits="32" numericFormat="UINT"/>
+ <component name="A" bits="32" numericFormat="UINT"/>
+ <spirvimageformat name="Rgba32ui"/>
+ </format>
+ <format name="VK_FORMAT_R32G32B32A32_SINT" class="128-bit" blockSize="16" texelsPerBlock="1">
+ <component name="R" bits="32" numericFormat="SINT"/>
+ <component name="G" bits="32" numericFormat="SINT"/>
+ <component name="B" bits="32" numericFormat="SINT"/>
+ <component name="A" bits="32" numericFormat="SINT"/>
+ <spirvimageformat name="Rgba32i"/>
+ </format>
+ <format name="VK_FORMAT_R32G32B32A32_SFLOAT" class="128-bit" blockSize="16" texelsPerBlock="1">
+ <component name="R" bits="32" numericFormat="SFLOAT"/>
+ <component name="G" bits="32" numericFormat="SFLOAT"/>
+ <component name="B" bits="32" numericFormat="SFLOAT"/>
+ <component name="A" bits="32" numericFormat="SFLOAT"/>
+ <spirvimageformat name="Rgba32f"/>
+ </format>
+ <format name="VK_FORMAT_R64_UINT" class="64-bit" blockSize="8" texelsPerBlock="1">
+ <component name="R" bits="64" numericFormat="UINT"/>
+ <spirvimageformat name="R64ui"/>
+ </format>
+ <format name="VK_FORMAT_R64_SINT" class="64-bit" blockSize="8" texelsPerBlock="1">
+ <component name="R" bits="64" numericFormat="SINT"/>
+ <spirvimageformat name="R64i"/>
+ </format>
+ <format name="VK_FORMAT_R64_SFLOAT" class="64-bit" blockSize="8" texelsPerBlock="1">
+ <component name="R" bits="64" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_R64G64_UINT" class="128-bit" blockSize="16" texelsPerBlock="1">
+ <component name="R" bits="64" numericFormat="UINT"/>
+ <component name="B" bits="64" numericFormat="UINT"/>
+ </format>
+ <format name="VK_FORMAT_R64G64_SINT" class="128-bit" blockSize="16" texelsPerBlock="1">
+ <component name="R" bits="64" numericFormat="SINT"/>
+ <component name="B" bits="64" numericFormat="SINT"/>
+ </format>
+ <format name="VK_FORMAT_R64G64_SFLOAT" class="128-bit" blockSize="16" texelsPerBlock="1">
+ <component name="R" bits="64" numericFormat="SFLOAT"/>
+ <component name="B" bits="64" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_R64G64B64_UINT" class="192-bit" blockSize="24" texelsPerBlock="1">
+ <component name="R" bits="64" numericFormat="UINT"/>
+ <component name="G" bits="64" numericFormat="UINT"/>
+ <component name="B" bits="64" numericFormat="UINT"/>
+ </format>
+ <format name="VK_FORMAT_R64G64B64_SINT" class="192-bit" blockSize="24" texelsPerBlock="1">
+ <component name="R" bits="64" numericFormat="SINT"/>
+ <component name="G" bits="64" numericFormat="SINT"/>
+ <component name="B" bits="64" numericFormat="SINT"/>
+ </format>
+ <format name="VK_FORMAT_R64G64B64_SFLOAT" class="192-bit" blockSize="24" texelsPerBlock="1">
+ <component name="R" bits="64" numericFormat="SFLOAT"/>
+ <component name="G" bits="64" numericFormat="SFLOAT"/>
+ <component name="B" bits="64" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_R64G64B64A64_UINT" class="256-bit" blockSize="32" texelsPerBlock="1">
+ <component name="R" bits="64" numericFormat="UINT"/>
+ <component name="G" bits="64" numericFormat="UINT"/>
+ <component name="B" bits="64" numericFormat="UINT"/>
+ <component name="A" bits="64" numericFormat="UINT"/>
+ </format>
+ <format name="VK_FORMAT_R64G64B64A64_SINT" class="256-bit" blockSize="32" texelsPerBlock="1">
+ <component name="R" bits="64" numericFormat="SINT"/>
+ <component name="G" bits="64" numericFormat="SINT"/>
+ <component name="B" bits="64" numericFormat="SINT"/>
+ <component name="A" bits="64" numericFormat="SINT"/>
+ </format>
+ <format name="VK_FORMAT_R64G64B64A64_SFLOAT" class="256-bit" blockSize="32" texelsPerBlock="1">
+ <component name="R" bits="64" numericFormat="SFLOAT"/>
+ <component name="G" bits="64" numericFormat="SFLOAT"/>
+ <component name="B" bits="64" numericFormat="SFLOAT"/>
+ <component name="A" bits="64" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_B10G11R11_UFLOAT_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="B" bits="10" numericFormat="UFLOAT"/>
+ <component name="G" bits="11" numericFormat="UFLOAT"/>
+ <component name="R" bits="10" numericFormat="UFLOAT"/>
+ <spirvimageformat name="R11fG11fB10f"/>
+ </format>
+ <format name="VK_FORMAT_E5B9G9R9_UFLOAT_PACK32" class="32-bit" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="B" bits="9" numericFormat="UFLOAT"/>
+ <component name="G" bits="9" numericFormat="UFLOAT"/>
+ <component name="R" bits="9" numericFormat="UFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_D16_UNORM" class="D16" blockSize="2" texelsPerBlock="1">
+ <component name="D" bits="16" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_X8_D24_UNORM_PACK32" class="D24" blockSize="4" texelsPerBlock="1" packed="32">
+ <component name="D" bits="24" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_D32_SFLOAT" class="D32" blockSize="4" texelsPerBlock="1">
+ <component name="D" bits="32" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_S8_UINT" class="S8" blockSize="1" texelsPerBlock="1">
+ <component name="S" bits="8" numericFormat="UINT"/>
+ </format>
+ <format name="VK_FORMAT_D16_UNORM_S8_UINT" class="D16S8" blockSize="3" texelsPerBlock="1">
+ <component name="D" bits="16" numericFormat="UNORM"/>
+ <component name="S" bits="8" numericFormat="UINT"/>
+ </format>
+ <format name="VK_FORMAT_D24_UNORM_S8_UINT" class="D24S8" blockSize="4" texelsPerBlock="1">
+ <component name="D" bits="24" numericFormat="UNORM"/>
+ <component name="S" bits="8" numericFormat="UINT"/>
+ </format>
+ <format name="VK_FORMAT_D32_SFLOAT_S8_UINT" class="D32S8" blockSize="5" texelsPerBlock="1">
+ <component name="D" bits="32" numericFormat="SFLOAT"/>
+ <component name="S" bits="8" numericFormat="UINT"/>
+ </format>
+ <format name="VK_FORMAT_BC1_RGB_UNORM_BLOCK" class="BC1_RGB" blockSize="8" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_BC1_RGB_SRGB_BLOCK" class="BC1_RGB" blockSize="8" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_BC1_RGBA_UNORM_BLOCK" class="BC1_RGBA" blockSize="8" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_BC1_RGBA_SRGB_BLOCK" class="BC1_RGBA" blockSize="8" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_BC2_UNORM_BLOCK" class="BC2" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_BC2_SRGB_BLOCK" class="BC2" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_BC3_UNORM_BLOCK" class="BC3" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_BC3_SRGB_BLOCK" class="BC3" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_BC4_UNORM_BLOCK" class="BC4" blockSize="8" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_BC4_SNORM_BLOCK" class="BC4" blockSize="8" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_BC5_UNORM_BLOCK" class="BC5" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_BC5_SNORM_BLOCK" class="BC5" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_BC6H_UFLOAT_BLOCK" class="BC6H" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="UFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="UFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="UFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_BC6H_SFLOAT_BLOCK" class="BC6H" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_BC7_UNORM_BLOCK" class="BC7" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_BC7_SRGB_BLOCK" class="BC7" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="BC">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK" class="ETC2_RGB" blockSize="8" texelsPerBlock="16" blockExtent="4,4,1" compressed="ETC2">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK" class="ETC2_RGB" blockSize="8" texelsPerBlock="16" blockExtent="4,4,1" compressed="ETC2">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK" class="ETC2_RGBA" blockSize="8" texelsPerBlock="16" blockExtent="4,4,1" compressed="ETC2">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK" class="ETC2_RGBA" blockSize="8" texelsPerBlock="16" blockExtent="4,4,1" compressed="ETC2">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK" class="ETC2_EAC_RGBA" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="ETC2">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK" class="ETC2_EAC_RGBA" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="ETC2">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_EAC_R11_UNORM_BLOCK" class="EAC_R" blockSize="8" texelsPerBlock="16" blockExtent="4,4,1" compressed="EAC">
+ <component name="R" bits="11" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_EAC_R11_SNORM_BLOCK" class="EAC_R" blockSize="8" texelsPerBlock="16" blockExtent="4,4,1" compressed="EAC">
+ <component name="R" bits="11" numericFormat="SNORM"/>
+ </format>
+ <format name="VK_FORMAT_EAC_R11G11_UNORM_BLOCK" class="EAC_RG" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="EAC">
+ <component name="R" bits="11" numericFormat="UNORM"/>
+ <component name="G" bits="11" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_EAC_R11G11_SNORM_BLOCK" class="EAC_RG" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="EAC">
+ <component name="R" bits="11" numericFormat="SNORM"/>
+ <component name="G" bits="11" numericFormat="SNORM"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_4x4_UNORM_BLOCK" class="ASTC_4x4" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_4x4_SRGB_BLOCK" class="ASTC_4x4" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_5x4_UNORM_BLOCK" class="ASTC_5x4" blockSize="16" texelsPerBlock="20" blockExtent="5,4,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_5x4_SRGB_BLOCK" class="ASTC_5x4" blockSize="16" texelsPerBlock="20" blockExtent="5,4,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_5x5_UNORM_BLOCK" class="ASTC_5x5" blockSize="16" texelsPerBlock="25" blockExtent="5,5,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_5x5_SRGB_BLOCK" class="ASTC_5x5" blockSize="16" texelsPerBlock="25" blockExtent="5,5,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_6x5_UNORM_BLOCK" class="ASTC_6x5" blockSize="16" texelsPerBlock="30" blockExtent="6,5,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_6x5_SRGB_BLOCK" class="ASTC_6x5" blockSize="16" texelsPerBlock="30" blockExtent="6,5,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_6x6_UNORM_BLOCK" class="ASTC_6x6" blockSize="16" texelsPerBlock="36" blockExtent="6,6,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_6x6_SRGB_BLOCK" class="ASTC_6x6" blockSize="16" texelsPerBlock="36" blockExtent="6,6,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_8x5_UNORM_BLOCK" class="ASTC_8x5" blockSize="16" texelsPerBlock="40" blockExtent="8,5,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_8x5_SRGB_BLOCK" class="ASTC_8x5" blockSize="16" texelsPerBlock="40" blockExtent="8,5,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_8x6_UNORM_BLOCK" class="ASTC_8x6" blockSize="16" texelsPerBlock="48" blockExtent="8,6,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_8x6_SRGB_BLOCK" class="ASTC_8x6" blockSize="16" texelsPerBlock="48" blockExtent="8,6,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_8x8_UNORM_BLOCK" class="ASTC_8x8" blockSize="16" texelsPerBlock="64" blockExtent="8,8,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_8x8_SRGB_BLOCK" class="ASTC_8x8" blockSize="16" texelsPerBlock="64" blockExtent="8,8,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_10x5_UNORM_BLOCK" class="ASTC_10x5" blockSize="16" texelsPerBlock="50" blockExtent="10,5,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_10x5_SRGB_BLOCK" class="ASTC_10x5" blockSize="16" texelsPerBlock="50" blockExtent="10,5,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_10x6_UNORM_BLOCK" class="ASTC_10x6" blockSize="16" texelsPerBlock="60" blockExtent="10,6,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_10x6_SRGB_BLOCK" class="ASTC_10x6" blockSize="16" texelsPerBlock="60" blockExtent="10,6,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_10x8_UNORM_BLOCK" class="ASTC_10x8" blockSize="16" texelsPerBlock="80" blockExtent="10,8,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_10x8_SRGB_BLOCK" class="ASTC_10x8" blockSize="16" texelsPerBlock="80" blockExtent="10,8,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_10x10_UNORM_BLOCK" class="ASTC_10x10" blockSize="16" texelsPerBlock="100" blockExtent="10,10,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_10x10_SRGB_BLOCK" class="ASTC_10x10" blockSize="16" texelsPerBlock="100" blockExtent="10,10,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_12x10_UNORM_BLOCK" class="ASTC_12x10" blockSize="16" texelsPerBlock="120" blockExtent="12,10,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_12x10_SRGB_BLOCK" class="ASTC_12x10" blockSize="16" texelsPerBlock="120" blockExtent="12,10,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_12x12_UNORM_BLOCK" class="ASTC_12x12" blockSize="16" texelsPerBlock="144" blockExtent="12,12,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_12x12_SRGB_BLOCK" class="ASTC_12x12" blockSize="16" texelsPerBlock="144" blockExtent="12,12,1" compressed="ASTC LDR">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_G8B8G8R8_422_UNORM" class="32-bit G8B8G8R8" blockSize="4" texelsPerBlock="1" blockExtent="2,1,1" chroma="422">
+ <component name="G" bits="8" numericFormat="UNORM"/>
+ <component name="B" bits="8" numericFormat="UNORM"/>
+ <component name="G" bits="8" numericFormat="UNORM"/>
+ <component name="R" bits="8" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_B8G8R8G8_422_UNORM" class="32-bit B8G8R8G8" blockSize="4" texelsPerBlock="1" blockExtent="2,1,1" chroma="422">
+ <component name="B" bits="8" numericFormat="UNORM"/>
+ <component name="G" bits="8" numericFormat="UNORM"/>
+ <component name="R" bits="8" numericFormat="UNORM"/>
+ <component name="G" bits="8" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM" class="8-bit 3-plane 420" blockSize="3" texelsPerBlock="1" chroma="420">
+ <component name="G" bits="8" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="8" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="8" numericFormat="UNORM" planeIndex="2"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R8_UNORM"/>
+ <plane index="1" widthDivisor="2" heightDivisor="2" compatible="VK_FORMAT_R8_UNORM"/>
+ <plane index="2" widthDivisor="2" heightDivisor="2" compatible="VK_FORMAT_R8_UNORM"/>
+ </format>
+ <format name="VK_FORMAT_G8_B8R8_2PLANE_420_UNORM" class="8-bit 2-plane 420" blockSize="3" texelsPerBlock="1" chroma="420">
+ <component name="G" bits="8" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="8" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="8" numericFormat="UNORM" planeIndex="1"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R8_UNORM"/>
+ <plane index="1" widthDivisor="2" heightDivisor="2" compatible="VK_FORMAT_R8G8_UNORM"/>
+ </format>
+ <format name="VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM" class="8-bit 3-plane 422" blockSize="3" texelsPerBlock="1" chroma="422">
+ <component name="G" bits="8" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="8" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="8" numericFormat="UNORM" planeIndex="2"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R8_UNORM"/>
+ <plane index="1" widthDivisor="2" heightDivisor="1" compatible="VK_FORMAT_R8_UNORM"/>
+ <plane index="2" widthDivisor="2" heightDivisor="1" compatible="VK_FORMAT_R8_UNORM"/>
+ </format>
+ <format name="VK_FORMAT_G8_B8R8_2PLANE_422_UNORM" class="8-bit 2-plane 422" blockSize="3" texelsPerBlock="1" chroma="422">
+ <component name="G" bits="8" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="8" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="8" numericFormat="UNORM" planeIndex="1"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R8_UNORM"/>
+ <plane index="1" widthDivisor="2" heightDivisor="1" compatible="VK_FORMAT_R8G8_UNORM"/>
+ </format>
+ <format name="VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM" class="8-bit 3-plane 444" blockSize="3" texelsPerBlock="1" chroma="444">
+ <component name="G" bits="8" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="8" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="8" numericFormat="UNORM" planeIndex="2"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R8_UNORM"/>
+ <plane index="1" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R8_UNORM"/>
+ <plane index="2" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R8_UNORM"/>
+ </format>
+ <format name="VK_FORMAT_R10X6_UNORM_PACK16" class="16-bit" blockSize="2" texelsPerBlock="1" packed="16">
+ <component name="R" bits="10" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_R10X6G10X6_UNORM_2PACK16" class="32-bit" blockSize="4" texelsPerBlock="1" packed="16">
+ <component name="R" bits="10" numericFormat="UNORM"/>
+ <component name="G" bits="10" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16" class="64-bit R10G10B10A10" blockSize="8" texelsPerBlock="1" packed="16" chroma="444">
+ <component name="R" bits="10" numericFormat="UNORM"/>
+ <component name="G" bits="10" numericFormat="UNORM"/>
+ <component name="B" bits="10" numericFormat="UNORM"/>
+ <component name="A" bits="10" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16" class="64-bit G10B10G10R10" blockSize="8" texelsPerBlock="1" blockExtent="2,1,1" packed="16" chroma="422">
+ <component name="G" bits="10" numericFormat="UNORM"/>
+ <component name="B" bits="10" numericFormat="UNORM"/>
+ <component name="G" bits="10" numericFormat="UNORM"/>
+ <component name="R" bits="10" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16" class="64-bit B10G10R10G10" blockSize="8" texelsPerBlock="1" blockExtent="2,1,1" packed="16" chroma="422">
+ <component name="B" bits="10" numericFormat="UNORM"/>
+ <component name="G" bits="10" numericFormat="UNORM"/>
+ <component name="R" bits="10" numericFormat="UNORM"/>
+ <component name="G" bits="10" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16" class="10-bit 3-plane 420" blockSize="6" texelsPerBlock="1" packed="16" chroma="420">
+ <component name="G" bits="10" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="10" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="10" numericFormat="UNORM" planeIndex="2"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R10X6_UNORM_PACK16"/>
+ <plane index="1" widthDivisor="2" heightDivisor="2" compatible="VK_FORMAT_R10X6_UNORM_PACK16"/>
+ <plane index="2" widthDivisor="2" heightDivisor="2" compatible="VK_FORMAT_R10X6_UNORM_PACK16"/>
+ </format>
+ <format name="VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16" class="10-bit 2-plane 420" blockSize="6" texelsPerBlock="1" packed="16" chroma="420">
+ <component name="G" bits="10" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="10" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="10" numericFormat="UNORM" planeIndex="1"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R10X6_UNORM_PACK16"/>
+ <plane index="1" widthDivisor="2" heightDivisor="2" compatible="VK_FORMAT_R10X6G10X6_UNORM_2PACK16"/>
+ </format>
+ <format name="VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16" class="10-bit 3-plane 422" blockSize="6" texelsPerBlock="1" packed="16" chroma="422">
+ <component name="G" bits="10" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="10" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="10" numericFormat="UNORM" planeIndex="2"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R10X6_UNORM_PACK16"/>
+ <plane index="1" widthDivisor="2" heightDivisor="1" compatible="VK_FORMAT_R10X6_UNORM_PACK16"/>
+ <plane index="2" widthDivisor="2" heightDivisor="1" compatible="VK_FORMAT_R10X6_UNORM_PACK16"/>
+ </format>
+ <format name="VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16" class="10-bit 2-plane 422" blockSize="6" texelsPerBlock="1" packed="16" chroma="422">
+ <component name="G" bits="10" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="10" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="10" numericFormat="UNORM" planeIndex="1"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R10X6_UNORM_PACK16"/>
+ <plane index="1" widthDivisor="2" heightDivisor="1" compatible="VK_FORMAT_R10X6G10X6_UNORM_2PACK16"/>
+ </format>
+ <format name="VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16" class="10-bit 3-plane 444" blockSize="6" texelsPerBlock="1" packed="16" chroma="444">
+ <component name="G" bits="10" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="10" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="10" numericFormat="UNORM" planeIndex="2"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R10X6_UNORM_PACK16"/>
+ <plane index="1" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R10X6_UNORM_PACK16"/>
+ <plane index="2" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R10X6_UNORM_PACK16"/>
+ </format>
+ <format name="VK_FORMAT_R12X4_UNORM_PACK16" class="16-bit" blockSize="2" texelsPerBlock="1" packed="16">
+ <component name="R" bits="12" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_R12X4G12X4_UNORM_2PACK16" class="32-bit" blockSize="4" texelsPerBlock="1" packed="16">
+ <component name="R" bits="12" numericFormat="UNORM"/>
+ <component name="G" bits="12" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16" class="64-bit R12G12B12A12" blockSize="8" texelsPerBlock="1" packed="16" chroma="444">
+ <component name="R" bits="12" numericFormat="UNORM"/>
+ <component name="G" bits="12" numericFormat="UNORM"/>
+ <component name="B" bits="12" numericFormat="UNORM"/>
+ <component name="A" bits="12" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16" class="64-bit G12B12G12R12" blockSize="8" texelsPerBlock="1" blockExtent="2,1,1" packed="16" chroma="422">
+ <component name="G" bits="12" numericFormat="UNORM"/>
+ <component name="B" bits="12" numericFormat="UNORM"/>
+ <component name="G" bits="12" numericFormat="UNORM"/>
+ <component name="R" bits="12" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16" class="64-bit B12G12R12G12" blockSize="8" texelsPerBlock="1" blockExtent="2,1,1" packed="16" chroma="422">
+ <component name="B" bits="12" numericFormat="UNORM"/>
+ <component name="G" bits="12" numericFormat="UNORM"/>
+ <component name="R" bits="12" numericFormat="UNORM"/>
+ <component name="G" bits="12" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16" class="12-bit 3-plane 420" blockSize="6" texelsPerBlock="1" packed="16" chroma="420">
+ <component name="G" bits="12" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="12" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="12" numericFormat="UNORM" planeIndex="2"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R12X4_UNORM_PACK16"/>
+ <plane index="1" widthDivisor="2" heightDivisor="2" compatible="VK_FORMAT_R12X4_UNORM_PACK16"/>
+ <plane index="2" widthDivisor="2" heightDivisor="2" compatible="VK_FORMAT_R12X4_UNORM_PACK16"/>
+ </format>
+ <format name="VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16" class="12-bit 2-plane 420" blockSize="6" texelsPerBlock="1" packed="16" chroma="420">
+ <component name="G" bits="12" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="12" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="12" numericFormat="UNORM" planeIndex="1"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R12X4_UNORM_PACK16"/>
+ <plane index="1" widthDivisor="2" heightDivisor="2" compatible="VK_FORMAT_R12X4G12X4_UNORM_2PACK16"/>
+ </format>
+ <format name="VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16" class="12-bit 3-plane 422" blockSize="6" texelsPerBlock="1" packed="16" chroma="422">
+ <component name="G" bits="12" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="12" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="12" numericFormat="UNORM" planeIndex="2"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R12X4_UNORM_PACK16"/>
+ <plane index="1" widthDivisor="2" heightDivisor="1" compatible="VK_FORMAT_R12X4_UNORM_PACK16"/>
+ <plane index="2" widthDivisor="2" heightDivisor="1" compatible="VK_FORMAT_R12X4_UNORM_PACK16"/>
+ </format>
+ <format name="VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16" class="12-bit 2-plane 422" blockSize="6" texelsPerBlock="1" packed="16" chroma="422">
+ <component name="G" bits="12" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="12" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="12" numericFormat="UNORM" planeIndex="1"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R12X4_UNORM_PACK16"/>
+ <plane index="1" widthDivisor="2" heightDivisor="1" compatible="VK_FORMAT_R12X4G12X4_UNORM_2PACK16"/>
+ </format>
+ <format name="VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16" class="12-bit 3-plane 444" blockSize="6" texelsPerBlock="1" packed="16" chroma="444">
+ <component name="G" bits="12" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="12" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="12" numericFormat="UNORM" planeIndex="2"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R12X4_UNORM_PACK16"/>
+ <plane index="1" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R12X4_UNORM_PACK16"/>
+ <plane index="2" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R12X4_UNORM_PACK16"/>
+ </format>
+ <format name="VK_FORMAT_G16B16G16R16_422_UNORM" class="64-bit G16B16G16R16" blockSize="8" texelsPerBlock="1" blockExtent="2,1,1" chroma="422">
+ <component name="G" bits="16" numericFormat="UNORM"/>
+ <component name="B" bits="16" numericFormat="UNORM"/>
+ <component name="G" bits="16" numericFormat="UNORM"/>
+ <component name="R" bits="16" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_B16G16R16G16_422_UNORM" class="64-bit B16G16R16G16" blockSize="8" texelsPerBlock="1" blockExtent="2,1,1" chroma="422">
+ <component name="B" bits="16" numericFormat="UNORM"/>
+ <component name="G" bits="16" numericFormat="UNORM"/>
+ <component name="R" bits="16" numericFormat="UNORM"/>
+ <component name="G" bits="16" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM" class="16-bit 3-plane 420" blockSize="6" texelsPerBlock="1" chroma="420">
+ <component name="G" bits="16" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="16" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="16" numericFormat="UNORM" planeIndex="2"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R16_UNORM"/>
+ <plane index="1" widthDivisor="2" heightDivisor="2" compatible="VK_FORMAT_R16_UNORM"/>
+ <plane index="2" widthDivisor="2" heightDivisor="2" compatible="VK_FORMAT_R16_UNORM"/>
+ </format>
+ <format name="VK_FORMAT_G16_B16R16_2PLANE_420_UNORM" class="16-bit 2-plane 420" blockSize="6" texelsPerBlock="1" chroma="420">
+ <component name="G" bits="16" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="16" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="16" numericFormat="UNORM" planeIndex="1"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R16_UNORM"/>
+ <plane index="1" widthDivisor="2" heightDivisor="2" compatible="VK_FORMAT_R16G16_UNORM"/>
+ </format>
+ <format name="VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM" class="16-bit 3-plane 422" blockSize="6" texelsPerBlock="1" chroma="422">
+ <component name="G" bits="16" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="16" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="16" numericFormat="UNORM" planeIndex="2"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R16_UNORM"/>
+ <plane index="1" widthDivisor="2" heightDivisor="1" compatible="VK_FORMAT_R16_UNORM"/>
+ <plane index="2" widthDivisor="2" heightDivisor="1" compatible="VK_FORMAT_R16_UNORM"/>
+ </format>
+ <format name="VK_FORMAT_G16_B16R16_2PLANE_422_UNORM" class="16-bit 2-plane 422" blockSize="6" texelsPerBlock="1" chroma="422">
+ <component name="G" bits="16" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="16" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="16" numericFormat="UNORM" planeIndex="1"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R16_UNORM"/>
+ <plane index="1" widthDivisor="2" heightDivisor="1" compatible="VK_FORMAT_R16G16_UNORM"/>
+ </format>
+ <format name="VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM" class="16-bit 3-plane 444" blockSize="6" texelsPerBlock="1" chroma="444">
+ <component name="G" bits="16" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="16" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="16" numericFormat="UNORM" planeIndex="2"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R16_UNORM"/>
+ <plane index="1" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R16_UNORM"/>
+ <plane index="2" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R16_UNORM"/>
+ </format>
+ <format name="VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG" class="PVRTC1_2BPP" blockSize="8" texelsPerBlock="1" blockExtent="8,4,1" compressed="PVRTC">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG" class="PVRTC1_4BPP" blockSize="8" texelsPerBlock="1" blockExtent="4,4,1" compressed="PVRTC">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG" class="PVRTC2_2BPP" blockSize="8" texelsPerBlock="1" blockExtent="8,4,1" compressed="PVRTC">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG" class="PVRTC2_4BPP" blockSize="8" texelsPerBlock="1" blockExtent="4,4,1" compressed="PVRTC">
+ <component name="R" bits="compressed" numericFormat="UNORM"/>
+ <component name="G" bits="compressed" numericFormat="UNORM"/>
+ <component name="B" bits="compressed" numericFormat="UNORM"/>
+ <component name="A" bits="compressed" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG" class="PVRTC1_2BPP" blockSize="8" texelsPerBlock="1" blockExtent="8,4,1" compressed="PVRTC">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG" class="PVRTC1_4BPP" blockSize="8" texelsPerBlock="1" blockExtent="4,4,1" compressed="PVRTC">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG" class="PVRTC2_2BPP" blockSize="8" texelsPerBlock="1" blockExtent="8,4,1" compressed="PVRTC">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG" class="PVRTC2_4BPP" blockSize="8" texelsPerBlock="1" blockExtent="4,4,1" compressed="PVRTC">
+ <component name="R" bits="compressed" numericFormat="SRGB"/>
+ <component name="G" bits="compressed" numericFormat="SRGB"/>
+ <component name="B" bits="compressed" numericFormat="SRGB"/>
+ <component name="A" bits="compressed" numericFormat="SRGB"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK" class="ASTC_4x4" blockSize="16" texelsPerBlock="16" blockExtent="4,4,1" compressed="ASTC HDR">
+ <component name="R" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="A" bits="compressed" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK" class="ASTC_5x4" blockSize="16" texelsPerBlock="20" blockExtent="5,4,1" compressed="ASTC HDR">
+ <component name="R" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="A" bits="compressed" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK" class="ASTC_5x5" blockSize="16" texelsPerBlock="25" blockExtent="5,5,1" compressed="ASTC HDR">
+ <component name="R" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="A" bits="compressed" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK" class="ASTC_6x5" blockSize="16" texelsPerBlock="30" blockExtent="6,5,1" compressed="ASTC HDR">
+ <component name="R" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="A" bits="compressed" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK" class="ASTC_6x6" blockSize="16" texelsPerBlock="36" blockExtent="6,6,1" compressed="ASTC HDR">
+ <component name="R" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="A" bits="compressed" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK" class="ASTC_8x5" blockSize="16" texelsPerBlock="40" blockExtent="8,5,1" compressed="ASTC HDR">
+ <component name="R" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="A" bits="compressed" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK" class="ASTC_8x6" blockSize="16" texelsPerBlock="48" blockExtent="8,6,1" compressed="ASTC HDR">
+ <component name="R" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="A" bits="compressed" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK" class="ASTC_8x8" blockSize="16" texelsPerBlock="64" blockExtent="8,8,1" compressed="ASTC HDR">
+ <component name="R" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="A" bits="compressed" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK" class="ASTC_10x5" blockSize="16" texelsPerBlock="50" blockExtent="10,5,1" compressed="ASTC HDR">
+ <component name="R" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="A" bits="compressed" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK" class="ASTC_10x6" blockSize="16" texelsPerBlock="60" blockExtent="10,6,1" compressed="ASTC HDR">
+ <component name="R" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="A" bits="compressed" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK" class="ASTC_10x8" blockSize="16" texelsPerBlock="80" blockExtent="10,8,1" compressed="ASTC HDR">
+ <component name="R" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="A" bits="compressed" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK" class="ASTC_10x10" blockSize="16" texelsPerBlock="100" blockExtent="10,10,1" compressed="ASTC HDR">
+ <component name="R" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="A" bits="compressed" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK" class="ASTC_12x10" blockSize="16" texelsPerBlock="120" blockExtent="12,10,1" compressed="ASTC HDR">
+ <component name="R" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="A" bits="compressed" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK" class="ASTC_12x12" blockSize="16" texelsPerBlock="144" blockExtent="12,12,1" compressed="ASTC HDR">
+ <component name="R" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="G" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="B" bits="compressed" numericFormat="SFLOAT"/>
+ <component name="A" bits="compressed" numericFormat="SFLOAT"/>
+ </format>
+ <format name="VK_FORMAT_G8_B8R8_2PLANE_444_UNORM" class="8-bit 2-plane 444" blockSize="3" texelsPerBlock="1" chroma="444">
+ <component name="G" bits="8" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="8" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="8" numericFormat="UNORM" planeIndex="1"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R8_UNORM"/>
+ <plane index="1" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R8G8_UNORM"/>
+ </format>
+ <format name="VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16" class="10-bit 2-plane 444" blockSize="6" texelsPerBlock="1" packed="16" chroma="444">
+ <component name="G" bits="10" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="10" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="10" numericFormat="UNORM" planeIndex="1"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R10X6_UNORM_PACK16"/>
+ <plane index="1" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R10X6G10X6_UNORM_2PACK16"/>
+ </format>
+ <format name="VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16" class="12-bit 2-plane 444" blockSize="6" texelsPerBlock="1" packed="16" chroma="444">
+ <component name="G" bits="12" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="12" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="12" numericFormat="UNORM" planeIndex="1"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R12X4_UNORM_PACK16"/>
+ <plane index="1" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R12X4G12X4_UNORM_2PACK16"/>
+ </format>
+ <format name="VK_FORMAT_G16_B16R16_2PLANE_444_UNORM" class="16-bit 2-plane 444" blockSize="6" texelsPerBlock="1" chroma="444">
+ <component name="G" bits="16" numericFormat="UNORM" planeIndex="0"/>
+ <component name="B" bits="16" numericFormat="UNORM" planeIndex="1"/>
+ <component name="R" bits="16" numericFormat="UNORM" planeIndex="1"/>
+ <plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R16_UNORM"/>
+ <plane index="1" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R16G16_UNORM"/>
+ </format>
+ <format name="VK_FORMAT_A4R4G4B4_UNORM_PACK16" class="16-bit" blockSize="2" texelsPerBlock="1" packed="16">
+ <component name="A" bits="4" numericFormat="UNORM"/>
+ <component name="R" bits="4" numericFormat="UNORM"/>
+ <component name="G" bits="4" numericFormat="UNORM"/>
+ <component name="B" bits="4" numericFormat="UNORM"/>
+ </format>
+ <format name="VK_FORMAT_A4B4G4R4_UNORM_PACK16" class="16-bit" blockSize="2" texelsPerBlock="1" packed="16">
+ <component name="A" bits="4" numericFormat="UNORM"/>
+ <component name="B" bits="4" numericFormat="UNORM"/>
+ <component name="G" bits="4" numericFormat="UNORM"/>
+ <component name="R" bits="4" numericFormat="UNORM"/>
+ </format>
+ </formats>
<spirvextensions comment="SPIR-V Extensions allowed in Vulkan and what is required to use it">
<spirvextension name="SPV_KHR_variable_pointers">
- <enable version="VK_API_VERSION_1_1"/>
+ <enable version="VK_VERSION_1_1"/>
<enable extension="VK_KHR_variable_pointers"/>
</spirvextension>
<spirvextension name="SPV_AMD_shader_explicit_vertex_parameter">
@@ -14637,26 +20690,26 @@ typedef void <name>CAMetalLayer</name>;
<enable extension="VK_AMD_texture_gather_bias_lod"/>
</spirvextension>
<spirvextension name="SPV_KHR_shader_draw_parameters">
- <enable version="VK_API_VERSION_1_1"/>
+ <enable version="VK_VERSION_1_1"/>
<enable extension="VK_KHR_shader_draw_parameters"/>
</spirvextension>
<spirvextension name="SPV_KHR_8bit_storage">
- <enable version="VK_API_VERSION_1_2"/>
+ <enable version="VK_VERSION_1_2"/>
<enable extension="VK_KHR_8bit_storage"/>
</spirvextension>
<spirvextension name="SPV_KHR_16bit_storage">
- <enable version="VK_API_VERSION_1_1"/>
+ <enable version="VK_VERSION_1_1"/>
<enable extension="VK_KHR_16bit_storage"/>
</spirvextension>
<spirvextension name="SPV_KHR_shader_clock">
<enable extension="VK_KHR_shader_clock"/>
</spirvextension>
<spirvextension name="SPV_KHR_float_controls">
- <enable version="VK_API_VERSION_1_2"/>
+ <enable version="VK_VERSION_1_2"/>
<enable extension="VK_KHR_shader_float_controls"/>
</spirvextension>
<spirvextension name="SPV_KHR_storage_buffer_storage_class">
- <enable version="VK_API_VERSION_1_1"/>
+ <enable version="VK_VERSION_1_1"/>
<enable extension="VK_KHR_storage_buffer_storage_class"/>
</spirvextension>
<spirvextension name="SPV_KHR_post_depth_coverage">
@@ -14687,18 +20740,18 @@ typedef void <name>CAMetalLayer</name>;
<enable extension="VK_NV_shader_subgroup_partitioned"/>
</spirvextension>
<spirvextension name="SPV_EXT_shader_viewport_index_layer">
- <enable version="VK_API_VERSION_1_2"/>
+ <enable version="VK_VERSION_1_2"/>
<enable extension="VK_EXT_shader_viewport_index_layer"/>
</spirvextension>
<spirvextension name="SPV_NVX_multiview_per_view_attributes">
<enable extension="VK_NVX_multiview_per_view_attributes"/>
</spirvextension>
<spirvextension name="SPV_EXT_descriptor_indexing">
- <enable version="VK_API_VERSION_1_2"/>
+ <enable version="VK_VERSION_1_2"/>
<enable extension="VK_EXT_descriptor_indexing"/>
</spirvextension>
<spirvextension name="SPV_KHR_vulkan_memory_model">
- <enable version="VK_API_VERSION_1_2"/>
+ <enable version="VK_VERSION_1_2"/>
<enable extension="VK_KHR_vulkan_memory_model"/>
</spirvextension>
<spirvextension name="SPV_NV_compute_shader_derivatives">
@@ -14722,6 +20775,9 @@ typedef void <name>CAMetalLayer</name>;
<spirvextension name="SPV_KHR_ray_query">
<enable extension="VK_KHR_ray_query"/>
</spirvextension>
+ <spirvextension name="SPV_KHR_ray_cull_mask">
+ <enable extension="VK_KHR_ray_tracing_maintenance1"/>
+ </spirvextension>
<spirvextension name="SPV_GOOGLE_hlsl_functionality1">
<enable extension="VK_GOOGLE_hlsl_functionality1"/>
</spirvextension>
@@ -14735,7 +20791,7 @@ typedef void <name>CAMetalLayer</name>;
<enable extension="VK_EXT_fragment_density_map"/>
</spirvextension>
<spirvextension name="SPV_KHR_physical_storage_buffer">
- <enable version="VK_API_VERSION_1_2"/>
+ <enable version="VK_VERSION_1_2"/>
<enable extension="VK_KHR_buffer_device_address"/>
</spirvextension>
<spirvextension name="SPV_EXT_physical_storage_buffer">
@@ -14751,160 +20807,225 @@ typedef void <name>CAMetalLayer</name>;
<enable extension="VK_EXT_fragment_shader_interlock"/>
</spirvextension>
<spirvextension name="SPV_EXT_demote_to_helper_invocation">
+ <enable version="VK_API_VERSION_1_3"/>
<enable extension="VK_EXT_shader_demote_to_helper_invocation"/>
</spirvextension>
<spirvextension name="SPV_KHR_fragment_shading_rate">
<enable extension="VK_KHR_fragment_shading_rate"/>
</spirvextension>
<spirvextension name="SPV_KHR_non_semantic_info">
+ <enable version="VK_API_VERSION_1_3"/>
<enable extension="VK_KHR_shader_non_semantic_info"/>
</spirvextension>
<spirvextension name="SPV_EXT_shader_image_int64">
<enable extension="VK_EXT_shader_image_atomic_int64"/>
</spirvextension>
<spirvextension name="SPV_KHR_terminate_invocation">
+ <enable version="VK_API_VERSION_1_3"/>
<enable extension="VK_KHR_shader_terminate_invocation"/>
</spirvextension>
+ <spirvextension name="SPV_KHR_multiview">
+ <enable version="VK_VERSION_1_1"/>
+ <enable extension="VK_KHR_multiview"/>
+ </spirvextension>
+ <spirvextension name="SPV_KHR_workgroup_memory_explicit_layout">
+ <enable extension="VK_KHR_workgroup_memory_explicit_layout"/>
+ </spirvextension>
+ <spirvextension name="SPV_EXT_shader_atomic_float_add">
+ <enable extension="VK_EXT_shader_atomic_float"/>
+ </spirvextension>
+ <spirvextension name="SPV_KHR_fragment_shader_barycentric">
+ <enable extension="VK_KHR_fragment_shader_barycentric"/>
+ </spirvextension>
+ <spirvextension name="SPV_KHR_subgroup_uniform_control_flow">
+ <enable version="VK_API_VERSION_1_3"/>
+ <enable extension="VK_KHR_shader_subgroup_uniform_control_flow"/>
+ </spirvextension>
+ <spirvextension name="SPV_EXT_shader_atomic_float_min_max">
+ <enable extension="VK_EXT_shader_atomic_float2"/>
+ </spirvextension>
+ <spirvextension name="SPV_EXT_shader_atomic_float16_add">
+ <enable extension="VK_EXT_shader_atomic_float2"/>
+ </spirvextension>
+ <spirvextension name="SPV_KHR_integer_dot_product">
+ <enable version="VK_API_VERSION_1_3"/>
+ <enable extension="VK_KHR_shader_integer_dot_product"/>
+ </spirvextension>
+ <spirvextension name="SPV_INTEL_shader_integer_functions">
+ <enable extension="VK_INTEL_shader_integer_functions2"/>
+ </spirvextension>
+ <spirvextension name="SPV_KHR_device_group">
+ <enable version="VK_API_VERSION_1_1"/>
+ <enable extension="VK_KHR_device_group"/>
+ </spirvextension>
+ <spirvextension name="SPV_QCOM_image_processing">
+ <enable extension="VK_QCOM_image_processing"/>
+ </spirvextension>
</spirvextensions>
<spirvcapabilities comment="SPIR-V Capabilities allowed in Vulkan and what is required to use it">
<spirvcapability name="Matrix">
- <enable version="VK_API_VERSION_1_0"/>
+ <enable version="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="Shader">
- <enable version="VK_API_VERSION_1_0"/>
+ <enable version="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="InputAttachment">
- <enable version="VK_API_VERSION_1_0"/>
+ <enable version="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="Sampled1D">
- <enable version="VK_API_VERSION_1_0"/>
+ <enable version="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="Image1D">
- <enable version="VK_API_VERSION_1_0"/>
+ <enable version="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="SampledBuffer">
- <enable version="VK_API_VERSION_1_0"/>
+ <enable version="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="ImageBuffer">
- <enable version="VK_API_VERSION_1_0"/>
+ <enable version="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="ImageQuery">
- <enable version="VK_API_VERSION_1_0"/>
+ <enable version="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="DerivativeControl">
- <enable version="VK_API_VERSION_1_0"/>
+ <enable version="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="Geometry">
- <enable struct="VkPhysicalDeviceFeatures" feature="geometryShader"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="geometryShader" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="Tessellation">
- <enable struct="VkPhysicalDeviceFeatures" feature="tessellationShader"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="tessellationShader" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="Float64">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderFloat64"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderFloat64" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="Int64">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderInt64"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderInt64" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="Int64Atomics">
<enable struct="VkPhysicalDeviceVulkan12Features" feature="shaderBufferInt64Atomics" requires="VK_VERSION_1_2,VK_KHR_shader_atomic_int64"/>
<enable struct="VkPhysicalDeviceVulkan12Features" feature="shaderSharedInt64Atomics" requires="VK_VERSION_1_2,VK_KHR_shader_atomic_int64"/>
+ <enable struct="VkPhysicalDeviceShaderImageAtomicInt64FeaturesEXT" feature="shaderImageInt64Atomics" requires="VK_EXT_shader_image_atomic_int64"/>
+ </spirvcapability>
+ <spirvcapability name="AtomicFloat16AddEXT">
+ <enable struct="VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT" feature="shaderBufferFloat16AtomicAdd" requires="VK_EXT_shader_atomic_float2"/>
+ <enable struct="VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT" feature="shaderSharedFloat16AtomicAdd" requires="VK_EXT_shader_atomic_float2"/>
</spirvcapability>
<spirvcapability name="AtomicFloat32AddEXT">
<enable struct="VkPhysicalDeviceShaderAtomicFloatFeaturesEXT" feature="shaderBufferFloat32AtomicAdd" requires="VK_EXT_shader_atomic_float"/>
<enable struct="VkPhysicalDeviceShaderAtomicFloatFeaturesEXT" feature="shaderSharedFloat32AtomicAdd" requires="VK_EXT_shader_atomic_float"/>
<enable struct="VkPhysicalDeviceShaderAtomicFloatFeaturesEXT" feature="shaderImageFloat32AtomicAdd" requires="VK_EXT_shader_atomic_float"/>
- <enable struct="VkPhysicalDeviceShaderAtomicFloatFeaturesEXT" feature="sparseImageFloat32AtomicAdd" requires="VK_EXT_shader_atomic_float"/>
</spirvcapability>
<spirvcapability name="AtomicFloat64AddEXT">
<enable struct="VkPhysicalDeviceShaderAtomicFloatFeaturesEXT" feature="shaderBufferFloat64AtomicAdd" requires="VK_EXT_shader_atomic_float"/>
<enable struct="VkPhysicalDeviceShaderAtomicFloatFeaturesEXT" feature="shaderSharedFloat64AtomicAdd" requires="VK_EXT_shader_atomic_float"/>
</spirvcapability>
+ <spirvcapability name="AtomicFloat16MinMaxEXT">
+ <enable struct="VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT" feature="shaderBufferFloat16AtomicMinMax" requires="VK_EXT_shader_atomic_float2"/>
+ <enable struct="VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT" feature="shaderSharedFloat16AtomicMinMax" requires="VK_EXT_shader_atomic_float2"/>
+ </spirvcapability>
+ <spirvcapability name="AtomicFloat32MinMaxEXT">
+ <enable struct="VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT" feature="shaderBufferFloat32AtomicMinMax" requires="VK_EXT_shader_atomic_float2"/>
+ <enable struct="VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT" feature="shaderSharedFloat32AtomicMinMax" requires="VK_EXT_shader_atomic_float2"/>
+ <enable struct="VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT" feature="shaderImageFloat32AtomicMinMax" requires="VK_EXT_shader_atomic_float2"/>
+ </spirvcapability>
+ <spirvcapability name="AtomicFloat64MinMaxEXT">
+ <enable struct="VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT" feature="shaderBufferFloat64AtomicMinMax" requires="VK_EXT_shader_atomic_float2"/>
+ <enable struct="VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT" feature="shaderSharedFloat64AtomicMinMax" requires="VK_EXT_shader_atomic_float2"/>
+ </spirvcapability>
<spirvcapability name="Int64ImageEXT">
<enable struct="VkPhysicalDeviceShaderImageAtomicInt64FeaturesEXT" feature="shaderImageInt64Atomics" requires="VK_EXT_shader_image_atomic_int64"/>
</spirvcapability>
<spirvcapability name="Int16">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderInt16"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderInt16" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="TessellationPointSize">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderTessellationAndGeometryPointSize"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderTessellationAndGeometryPointSize" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="GeometryPointSize">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderTessellationAndGeometryPointSize"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderTessellationAndGeometryPointSize" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="ImageGatherExtended">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderImageGatherExtended"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderImageGatherExtended" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="StorageImageMultisample">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderStorageImageMultisample"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderStorageImageMultisample" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="UniformBufferArrayDynamicIndexing">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderUniformBufferArrayDynamicIndexing"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderUniformBufferArrayDynamicIndexing" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="SampledImageArrayDynamicIndexing">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderSampledImageArrayDynamicIndexing"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderSampledImageArrayDynamicIndexing" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="StorageBufferArrayDynamicIndexing">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderStorageBufferArrayDynamicIndexing"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderStorageBufferArrayDynamicIndexing" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="StorageImageArrayDynamicIndexing">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderStorageImageArrayDynamicIndexing"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderStorageImageArrayDynamicIndexing" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="ClipDistance">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderClipDistance"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderClipDistance" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="CullDistance">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderCullDistance"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderCullDistance" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="ImageCubeArray">
- <enable struct="VkPhysicalDeviceFeatures" feature="imageCubeArray"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="imageCubeArray" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="SampleRateShading">
- <enable struct="VkPhysicalDeviceFeatures" feature="sampleRateShading"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="sampleRateShading" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="SparseResidency">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderResourceResidency"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderResourceResidency" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="MinLod">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderResourceMinLod"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderResourceMinLod" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="SampledCubeArray">
- <enable struct="VkPhysicalDeviceFeatures" feature="imageCubeArray"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="imageCubeArray" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="ImageMSArray">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderStorageImageMultisample"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderStorageImageMultisample" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="StorageImageExtendedFormats">
- <enable version="VK_API_VERSION_1_0"/>
+ <enable version="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="InterpolationFunction">
- <enable struct="VkPhysicalDeviceFeatures" feature="sampleRateShading"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="sampleRateShading" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="StorageImageReadWithoutFormat">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderStorageImageReadWithoutFormat"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderStorageImageReadWithoutFormat" requires="VK_VERSION_1_0"/>
+ <enable version="VK_API_VERSION_1_3"/>
+ <enable extension="VK_KHR_format_feature_flags2"/>
</spirvcapability>
<spirvcapability name="StorageImageWriteWithoutFormat">
- <enable struct="VkPhysicalDeviceFeatures" feature="shaderStorageImageWriteWithoutFormat"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="shaderStorageImageWriteWithoutFormat" requires="VK_VERSION_1_0"/>
+ <enable version="VK_API_VERSION_1_3"/>
+ <enable extension="VK_KHR_format_feature_flags2"/>
</spirvcapability>
<spirvcapability name="MultiViewport">
- <enable struct="VkPhysicalDeviceFeatures" feature="multiViewport"/>
+ <enable struct="VkPhysicalDeviceFeatures" feature="multiViewport" requires="VK_VERSION_1_0"/>
</spirvcapability>
<spirvcapability name="DrawParameters">
- <enable struct="VkPhysicalDeviceVulkan11Features" feature="shaderDrawParameters" requires="VK_VERSION_1_1"/>
+ <enable struct="VkPhysicalDeviceVulkan11Features" feature="shaderDrawParameters" requires="VK_VERSION_1_2"/>
+ <enable struct="VkPhysicalDeviceShaderDrawParametersFeatures" feature="shaderDrawParameters" requires="VK_VERSION_1_1"/>
<enable extension="VK_KHR_shader_draw_parameters"/>
</spirvcapability>
<spirvcapability name="MultiView">
- <enable struct="VkPhysicalDeviceVulkan11Features" feature="multiview" requires="VK_VERSION_1_1,VK_KHR_multiview"/>
+ <enable struct="VkPhysicalDeviceVulkan11Features" feature="multiview" requires="VK_VERSION_1_2"/>
+ <enable struct="VkPhysicalDeviceMultiviewFeatures" feature="multiview" requires="VK_KHR_multiview"/>
</spirvcapability>
<spirvcapability name="DeviceGroup">
- <enable version="VK_API_VERSION_1_1"/>
+ <enable version="VK_VERSION_1_1"/>
<enable extension="VK_KHR_device_group"/>
</spirvcapability>
<spirvcapability name="VariablePointersStorageBuffer">
- <enable struct="VkPhysicalDeviceVulkan11Features" feature="variablePointersStorageBuffer" requires="VK_VERSION_1_1,VK_KHR_variable_pointers"/>
+ <enable struct="VkPhysicalDeviceVulkan11Features" feature="variablePointersStorageBuffer" requires="VK_VERSION_1_2"/>
+ <enable struct="VkPhysicalDeviceVariablePointersFeatures" feature="variablePointersStorageBuffer" requires="VK_KHR_variable_pointers"/>
</spirvcapability>
<spirvcapability name="VariablePointers">
- <enable struct="VkPhysicalDeviceVulkan11Features" feature="variablePointers" requires="VK_VERSION_1_1,VK_KHR_variable_pointers"/>
+ <enable struct="VkPhysicalDeviceVulkan11Features" feature="variablePointers" requires="VK_VERSION_1_2"/>
+ <enable struct="VkPhysicalDeviceVariablePointersFeatures" feature="variablePointers" requires="VK_KHR_variable_pointers"/>
</spirvcapability>
<spirvcapability name="ShaderClockKHR">
<enable extension="VK_KHR_shader_clock"/>
@@ -14952,16 +21073,20 @@ typedef void <name>CAMetalLayer</name>;
<enable extension="VK_NVX_multiview_per_view_attributes"/>
</spirvcapability>
<spirvcapability name="StorageBuffer16BitAccess">
- <enable struct="VkPhysicalDeviceVulkan11Features" feature="storageBuffer16BitAccess" requires="VK_VERSION_1_1,VK_KHR_16bit_storage"/>
+ <enable struct="VkPhysicalDeviceVulkan11Features" feature="storageBuffer16BitAccess" requires="VK_VERSION_1_2"/>
+ <enable struct="VkPhysicalDevice16BitStorageFeatures" feature="storageBuffer16BitAccess" requires="VK_KHR_16bit_storage"/>
</spirvcapability>
<spirvcapability name="UniformAndStorageBuffer16BitAccess">
- <enable struct="VkPhysicalDeviceVulkan11Features" feature="uniformAndStorageBuffer16BitAccess" requires="VK_VERSION_1_1,VK_KHR_16bit_storage"/>
+ <enable struct="VkPhysicalDeviceVulkan11Features" feature="uniformAndStorageBuffer16BitAccess" requires="VK_VERSION_1_2"/>
+ <enable struct="VkPhysicalDevice16BitStorageFeatures" feature="uniformAndStorageBuffer16BitAccess" requires="VK_KHR_16bit_storage"/>
</spirvcapability>
<spirvcapability name="StoragePushConstant16">
- <enable struct="VkPhysicalDeviceVulkan11Features" feature="storagePushConstant16" requires="VK_VERSION_1_1,VK_KHR_16bit_storage"/>
+ <enable struct="VkPhysicalDeviceVulkan11Features" feature="storagePushConstant16" requires="VK_VERSION_1_2"/>
+ <enable struct="VkPhysicalDevice16BitStorageFeatures" feature="storagePushConstant16" requires="VK_KHR_16bit_storage"/>
</spirvcapability>
<spirvcapability name="StorageInputOutput16">
- <enable struct="VkPhysicalDeviceVulkan11Features" feature="storageInputOutput16" requires="VK_VERSION_1_1,VK_KHR_16bit_storage"/>
+ <enable struct="VkPhysicalDeviceVulkan11Features" feature="storageInputOutput16" requires="VK_VERSION_1_2"/>
+ <enable struct="VkPhysicalDevice16BitStorageFeatures" feature="storageInputOutput16" requires="VK_KHR_16bit_storage"/>
</spirvcapability>
<spirvcapability name="GroupNonUniform">
<enable property="VkPhysicalDeviceVulkan11Properties" member="subgroupSupportedOperations" value="VK_SUBGROUP_FEATURE_BASIC_BIT" requires="VK_VERSION_1_1"/>
@@ -14994,7 +21119,7 @@ typedef void <name>CAMetalLayer</name>;
<enable extension="VK_EXT_post_depth_coverage"/>
</spirvcapability>
<spirvcapability name="ShaderNonUniform">
- <enable version="VK_API_VERSION_1_2"/>
+ <enable version="VK_VERSION_1_2"/>
<enable extension="VK_EXT_descriptor_indexing"/>
</spirvcapability>
<spirvcapability name="RuntimeDescriptorArray">
@@ -15103,10 +21228,17 @@ typedef void <name>CAMetalLayer</name>;
</spirvcapability>
<spirvcapability name="RayTraversalPrimitiveCullingKHR">
<enable struct="VkPhysicalDeviceRayTracingPipelineFeaturesKHR" feature="rayTraversalPrimitiveCulling" requires="VK_KHR_ray_tracing_pipeline"/>
+ <enable struct="VkPhysicalDeviceRayQueryFeaturesKHR" feature="rayQuery" requires="VK_KHR_ray_query"/>
+ </spirvcapability>
+ <spirvcapability name="RayCullMaskKHR">
+ <enable struct="VkPhysicalDeviceRayTracingMaintenance1FeaturesKHR" feature="rayTracingMaintenance1" requires="VK_KHR_ray_tracing_maintenance1"/>
</spirvcapability>
<spirvcapability name="RayTracingNV">
<enable extension="VK_NV_ray_tracing"/>
</spirvcapability>
+ <spirvcapability name="RayTracingMotionBlurNV">
+ <enable struct="VkPhysicalDeviceRayTracingMotionBlurFeaturesNV" feature="rayTracingMotionBlur" requires="VK_NV_ray_tracing_motion_blur"/>
+ </spirvcapability>
<spirvcapability name="TransformFeedback">
<enable struct="VkPhysicalDeviceTransformFeedbackFeaturesEXT" feature="transformFeedback" requires="VK_EXT_transform_feedback"/>
</spirvcapability>
@@ -15140,6 +21272,7 @@ typedef void <name>CAMetalLayer</name>;
<enable struct="VkPhysicalDeviceShadingRateImageFeaturesNV" feature="shadingRateImage" requires="VK_NV_shading_rate_image"/>
</spirvcapability>
<spirvcapability name="DemoteToHelperInvocationEXT">
+ <enable struct="VkPhysicalDeviceVulkan13Features" feature="shaderDemoteToHelperInvocation" requires="VK_VERSION_1_3,VK_EXT_shader_demote_to_helper_invocation"/>
<enable struct="VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT" feature="shaderDemoteToHelperInvocation" requires="VK_EXT_shader_demote_to_helper_invocation"/>
</spirvcapability>
<spirvcapability name="FragmentShadingRateKHR">
@@ -15147,5 +21280,42 @@ typedef void <name>CAMetalLayer</name>;
<enable struct="VkPhysicalDeviceFragmentShadingRateFeaturesKHR" feature="primitiveFragmentShadingRate" requires="VK_KHR_fragment_shading_rate"/>
<enable struct="VkPhysicalDeviceFragmentShadingRateFeaturesKHR" feature="attachmentFragmentShadingRate" requires="VK_KHR_fragment_shading_rate"/>
</spirvcapability>
+ <spirvcapability name="WorkgroupMemoryExplicitLayoutKHR">
+ <enable struct="VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR" feature="workgroupMemoryExplicitLayout" requires="VK_KHR_workgroup_memory_explicit_layout"/>
+ </spirvcapability>
+ <spirvcapability name="WorkgroupMemoryExplicitLayout8BitAccessKHR">
+ <enable struct="VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR" feature="workgroupMemoryExplicitLayout8BitAccess" requires="VK_KHR_workgroup_memory_explicit_layout"/>
+ </spirvcapability>
+ <spirvcapability name="WorkgroupMemoryExplicitLayout16BitAccessKHR">
+ <enable struct="VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR" feature="workgroupMemoryExplicitLayout16BitAccess" requires="VK_KHR_workgroup_memory_explicit_layout"/>
+ </spirvcapability>
+ <spirvcapability name="DotProductInputAllKHR">
+ <enable struct="VkPhysicalDeviceVulkan13Features" feature="shaderIntegerDotProduct" requires="VK_VERSION_1_3,VK_KHR_shader_integer_dot_product"/>
+ <enable struct="VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR" feature="shaderIntegerDotProduct" requires="VK_KHR_shader_integer_dot_product"/>
+ </spirvcapability>
+ <spirvcapability name="DotProductInput4x8BitKHR">
+ <enable struct="VkPhysicalDeviceVulkan13Features" feature="shaderIntegerDotProduct" requires="VK_VERSION_1_3,VK_KHR_shader_integer_dot_product"/>
+ <enable struct="VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR" feature="shaderIntegerDotProduct" requires="VK_KHR_shader_integer_dot_product"/>
+ </spirvcapability>
+ <spirvcapability name="DotProductInput4x8BitPackedKHR">
+ <enable struct="VkPhysicalDeviceVulkan13Features" feature="shaderIntegerDotProduct" requires="VK_VERSION_1_3,VK_KHR_shader_integer_dot_product"/>
+ <enable struct="VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR" feature="shaderIntegerDotProduct" requires="VK_KHR_shader_integer_dot_product"/>
+ </spirvcapability>
+ <spirvcapability name="DotProductKHR">
+ <enable struct="VkPhysicalDeviceVulkan13Features" feature="shaderIntegerDotProduct" requires="VK_VERSION_1_3,VK_KHR_shader_integer_dot_product"/>
+ <enable struct="VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR" feature="shaderIntegerDotProduct" requires="VK_KHR_shader_integer_dot_product"/>
+ </spirvcapability>
+ <spirvcapability name="FragmentBarycentricKHR">
+ <enable struct="VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR" feature="fragmentShaderBarycentric" requires="VK_KHR_fragment_shader_barycentric"/>
+ </spirvcapability>
+ <spirvcapability name="TextureSampleWeightedQCOM">
+ <enable struct="VkPhysicalDeviceImageProcessingFeaturesQCOM" feature="textureSampleWeighted" requires="VK_QCOM_image_processing"/>
+ </spirvcapability>
+ <spirvcapability name="TextureBoxFilterQCOM">
+ <enable struct="VkPhysicalDeviceImageProcessingFeaturesQCOM" feature="textureBoxFilter" requires="VK_QCOM_image_processing"/>
+ </spirvcapability>
+ <spirvcapability name="TextureBlockMatchQCOM">
+ <enable struct="VkPhysicalDeviceImageProcessingFeaturesQCOM" feature="textureBlockMatch" requires="VK_QCOM_image_processing"/>
+ </spirvcapability>
</spirvcapabilities>
</registry>